Excel. Трюки и эффекты
Шрифт:
Если используется шифр Виженера с неограниченным неповторяющимся ключом, то мы имеем шифр Вернама, в котором й = xi + yi (mod т) и yi выбираются случайно и независимо среди чисел 0, 1…., т – 1. Если ключом служит текст, имеющий смысл, то имеем шифр «бегущего ключа».
Теперь перейдем к примеру. Рассмотрим одну из возможных реализаций шифра Цезаря. Как обычно, создадим новое приложение и, по аналогии с предыдущим примером, разместим на форме такие же компоненты. У вас получится приблизительно следующее приложение (рис. 12.5).
Рис. 12.5. Интерфейс приложения «Шифр Цезаря»
Текстовое поле имеет
Листинг 12.13.
Объявление типов и класса нашей формы
type
//исходный алфавит русского языка
TRusSrcAlphabet = array [0..65] of Char;
//сопоставление букв алфавита открытого текста и зашифрованного
TRusDstAlphabet = array [Char] of Char;
TfmCryptography = class(TForm)
mmDecryptMessage: TMemo;
mmEncryptMessage: TMemo;
lbDecryptMessage: TLabel;
lbEncryptMessage: TLabel;
btnEncryptMessage: TButton;
btnDecpyptMessage: TButton;
edKey: TEdit;
lbKey: TLabel;
procedure btnEncryptMessageClick(Sender: TObject);
procedure btnDecpyptMessageClick(Sender: TObject);
private
{ Private declarations }
RusDstAlphabet: TRusDstAlphabet;
function GetKey: Integer;
procedure RecalcAlphabet(nKey: Integer);
function EncryptDecryptString(strMsg: String;
nKey: Integer): String;
public
{ Public declarations }
end;
var
RusSrcAlphabet: TRusSrcAlphabet =
\'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ\' +
\'абвгдеёжзийклмнопрстуфхцчшщъыьэюя\
fmCryptography: TfmCryptography;
Далее приведем описание работы методов, решающих определенные подзадачи, которые возникают в процессе решения основной проблемы. Итак, начнем рассмотрение с функции получения введенного пользователем ключа. Ее работа заключается в следующем. Сначала текстовое представление ключа преобразуется в численное представление. Далее проверяется, успешно ли прошло преобразование. Если все отлично, то возвращается полученное значение. В противном случае результатом функции будет -1, что свидетельствует о некорректном вводе пользователем ключа. Исходный код данной функции приведен в листинге 12.14.
Листинг 12.14.
Функция получения ключа
function TfmCryptography.GetKey: Integer;
var
key, code: Integer;
begin
Result := –1;
//получаем текст элемента управления текстовая строка
Val(edKey.Text, key, code);
//ошибка во время преобразования к целому числу?
//или ключ имеет отрицательное значение?
if (code = 0) and (0 < key) then
Result := key;
end;
Процедура RecalcAlphabet имеет один параметр nKey, который принимает любое целое значение. Он показывает, на сколько требуется сдвинуть алфавит циклически вперед, то есть если имеется алфавит АБВГД, а пКеу=3, то результатом будет ВГДАБ. Первым делом алфавит соответствия заполняется один к одному, то есть каждый символ соответствует сам себе. После этого циклом проходимся по строке, содержащей весь необходимый алфавит, подлежащий сдвигу, и переназначаем соответствие этих букв смещенным. Как это делается, можно посмотреть в листинге 12.15.
Листинг 12.15.
Функция пересчета алфавита преобразования
procedure TfmCryptography.RecalcAlphabet(nKey: Integer);
var
Ch: Char;
i: Integer;
LetCnt: Integer;
begin
//предварительно все символы в алфавите шифрования
//соответствуют символам из незашифрованного алфавита
for Ch := Low(RusDstAlphabet) to High(RusDstAlphabet) do
RusDstAlphabet[Ch] := Ch;
//количество символов в алфавите
LetCnt := SizeOf(TRusSrcAlphabet);
//смещаем эталонный алфавит циклически влево на значение,
//заданное ключом nKey
for i := 0 to LetCnt – 1 do
RusDstAlphabet[RusSrcAlphabet[(i – nKey + LetCnt)
mod LetCnt]] := RusSrcAlphabet[i];
end;
Процедура RecalcAlphabet
производит необходимую подготовку перед шифрованием или дешифрованием. Результаты процедуры используются в функции EncryptDecryptString, где каждая буква открытого текста заменяется соответствующей ей буквой из смещенного алфавита. Это преобразование осуществляется простым проходом по всей строке и выполнением операции замены символа соответствующим ему. Стоит заметить, что для дешифровки сообщения по заданному ключу вычисляется симметричный ему ключ. В результате процесс дешифровки текста сообщения ничем не отличается от процесса его шифровки (листинг 12.16).Листинг 12.16.
Шифрование/дешифрование строки
function TfmCryptography.EncryptDecryptString(strMsg: String;
nKey: Integer): String;
var
i: Integer;
begin
//каждый символ строки заменяется соответствующим символом
//алфавита шифрования
for i := 1 to Length(strMsg) do
strMsg[i] := RusDstAlphabet[strMsg[i]];
Result := strMsg;
end;
Теперь у нас есть все, чтобы перейти к решению основной задачи. Процесс шифрования аналогичен процессу дешифрования текста сообщения. Для начала нужно попытаться получить ключ, который ввел пользователь, что мы и делаем. После проверяем значение ключа. Если он равен -1, то это значит, что ключ введен неверно и преобразование текста невозможно. Когда все отлично, перед преобразованием текста мы вызываем метод подготовки алфавита с полученным ключом. Стоит отметить, что, когда происходит процесс дешифрования, вычисляется обратный ключ. С его помощью можно получить алфавит, используя который аналогично процессу шифрования получаем открытый текст сообщения. Далее просто: для каждой строки текста сообщения вызывается функцияпреобразования. На этом каждый метод заканчивает свою работу. Исходный код, соответствующий приведенному выше описанию, показан в листинге 12.17.
Листинг 12.17.
Шифрование/дешифрование текста сообщения
procedure TfmCryptography.btnEncryptMessageClick(Sender: TObject);
var
i: Integer;
nKey: Integer;
begin
//получаем ключ, с помощью которого будет
//шифроваться сообщение
nKey := GetKey;
//ключ задан верно?
if nKey = –1 then
Begin
MessageDlg(\'Ошибка: ключ задан неверно\', mtError, [mbOk], 0);
Exit;
End;
//получаем алфавит, с помощью которого будет
//происходить шифрование
RecalcAlphabet(nKey);
//предотвращаем перерисовку компонента до тех пор, пока не
//зашифруем все строки сообщения
mmEncryptMessage.Lines.BeginUpdate;
//освобождаем список от любых старых значений
mmEncryptMessage.Clear;
//шифруем сообщение построчно
for i := 0 to mmDecryptMessage.Lines.Count – 1 do
mmEncryptMessage.Lines.Add(
EncryptDecryptString(mmDecryptMessage.Lines[i], nKey));
//заново разрешаем перерисовку компонента
mmEncryptMessage.Lines.EndUpdate;
end;
procedure TfmCryptography.btnDecpyptMessageClick(Sender: TObject);
var
i: Integer;
nKey: Integer;
begin
nKey := GetKey;
if nKey = –1 then
Begin
MessageDlg(\'Ошибка: ключ задан неверно\', mtError, [mbOk], 0);
Exit;
End;
//получаем алфавит, с помощью которого будет происходить
//дешифрование
RecalcAlphabet(SizeOf(TRusSrcAlphabet) – nKey
mod SizeOf(TRusSrcAlphabet));
mmDecryptMessage.Lines.BeginUpdate;
mmDecryptMessage.Clear;
for i := 0 to mmEncryptMessage.Lines.Count – 1 do
mmDecryptMessage.Lines.Add(
EncryptDecryptString(mmEncryptMessage.Lines[i], nKey));
mmDecryptMessage.Lines.EndUpdate;
end;
Первое, что бросается в глаза при рассмотрении всего текста приложения, это практически полная идентичность интерфейса и основной части исходного кода. На самом деле это совсем не случайно. Достаточно часто программы пишутся универсально (даже более универсально, чем здесь!). Это основывается на очень простом предположении, что код должен быть многоразовым, то есть его можно повторно использовать в других приложениях. В результате у вас получается некий шаблон, который позволяет решать целый класс задач. Для этого нужно выполнить несколько маленьких изменений и потом просто можно забыть об этом. Результат выполнения итогового приложения можно увидеть на рис. 12.6.