Операция XOR и простейшее шифрование файлов. Алгоритм XOR-шифрования

Согласно определению, двухместная однобитовая операция "исключающее ИЛИ" выполняет действия, показанные в табл. 19.1.

Таблица 19.1. Исключающее ИЛИ(ХОЯ)

Вход 1

Вход 2

Выход

Ее можно еще назвать "операция несовпадения"- на выходе логическая единица тогда, когда значения на входах не совпадают. Из этого определения легко вывести основное свойство этой операции: будучи применена дважды к одному и тому же операнду, она ничего не изменит, независимо от значения второго операнда, лишь бы он не менялся. Этим, в частности, широко пользуются в компьютерной графике: если приложить через операцию XOR к фону маску, состоящую из всех единиц (чисто белую), то изображение на этом месте инвертируется по цветам, при повторении того же действия все восстанавливается в неизменном виде (так, в частности, удобно производить выделение. см. главу 10).

Шифрование файлов может быть смоделировано с первого взгляда для шифрования сообщений, где отправитель и получатель являются одним и тем же человеком. Однако это не так просто, как может показаться. При шифровании файлов необходимо помнить такие функции, как.

Часто бывает, что после шифрования на диске остается незашифрованная копия или дамп; Размер блока в алгоритме блочного шифрования может значительно превышать размер отдельной части данных в файле. Лучше всего зашифровать каждый файл одним ключом, а затем зашифровать все ключи с помощью мастер-ключа. Те же пользователи будут освобождены от путаницы организации безопасного хранения большого количества ключей. Дифференциация доступа групп пользователей к различным файлам осуществляется путем деления количества ключей на подмножества и шифрования этих подмножеств с помощью разных основных ключей.

Данное свойство и положено в основу практически всех алгоритмов шифрования, которые иногда могут быть очень навороченными, но мы не будем углубляться в этот вопрос, а просто попробуем применить этот метод в его изначальном виде. Для проверки можно использовать такую простейшую процедуру. Пусть мы хотим зашифровать некий текстовый файл. Так как мы дипломатические секреты не прячем, то нам хватит обычной в таких случаях длины ключа в 8 символов (64 бита)- этого достаточно, чтобы сделать взлом шифра методом перебора на обычном персональном компьютере задачей "с налета" нерешаемой. Для примера ключом будет служить слово "yvre- vich". Создавать такие ключи из своей фамилии на практике ни в коем случае нельзя, нельзя также употреблять любые словарные слова, даты, номера ге- лефонов- еще Вернам показал, что для эффективного шифрования ключ должен быть строго случайным, здесь это делается только в качестве примера (о том, как можно задавать случайный ключ, мы поговорим позже).

Устойчивость таких криптосистем будет намного выше, чем при использовании одного ключа для шифрования всех файлов на жестком диске, поскольку ключи, используемые для шифрования файлов, могут быть более случайными и более устойчивыми к атаке словаря. Алгоритмы сжатия данных очень полезны для использования с криптографическими алгоритмами.

Открывая криптоаналитический шифр, он полагается на избыточность, что очень характерно для любого открытого текста. Когда используется сжатие, длина открытого текста уменьшается, и сам процесс шифрования сокращается.

  • Сжатие помогает избавиться от этой избыточности.
  • Шифрование данных - довольно сложная операция.
Вы не должны забывать сжимать файл перед его зашифровкой, а не наоборот. Поскольку шифрование файла с использованием высококачественного криптографического алгоритма затрудняет сжатие зашифрованного текста.

Создадим новый проект (в папке Glaval9\l) под названием ProbaCrypt, и разместим в той же папке некий файл на пробу. Я взял текст песни "Однажды мир прогнется под нас" из репертуара А. Макаревича (в текстовом же формате, файл mashinavremeni.txt) с аккордами, т. к. достаточно сложное форматирование этого текста сделает пример нагляднее 3 .

Поместим на форму компонент Memo, диалог OpenDialog и две кнопки Butt эл. В заголовке Buttoni напишем Зашифровать, в заголовке Buttoni - Расшифровать. Объявим такие переменные:

Зашифрованные текстовые параметры будут близки к параметрам абсолютно случайного файла букв. Сжатие может быть специальным тестом для проверки качества криптографического алгоритма.

Открывая криптоаналитический шифр, он полагается на избыточность, что очень характерно для любого открытого текста. Когда используется сжатие, длина открытого текста уменьшается, и сам процесс шифрования сокращается.

  • Сжатие помогает избавиться от этой избыточности.
  • Шифрование данных - довольно сложная операция.
Вы не должны забывать сжимать файл перед его зашифровкой, а не наоборот. Поскольку шифрование файла с использованием высококачественного криптографического алгоритма затрудняет сжатие зашифрованного текста.

Forml: TForml ; fname,key:string; fi,fo:file of byte; i:integer; xb:byte;

При создании формы инициализируем ключ и диалог:

procedure TForml.FormCreate(Sender: TObject); begin key:=’yvrevich";

OpenDialogl.InitialDir:=ExtractFiieDir(Application.ExeName); end;

В обработчике нажатия кнопки Buttonl напишем следующий довольно длинный код:

procedure TForml.ButtonlClick(Sender: TObject); begin /Зашифровать/ OpenDialogl. FileName:?= 11 ; (очистим) OpenDialogl.Filter:=”; if OpenDialogl.Execute then fname:=OpenDialogl.FileName else exit; assignfile(fi,fname); {задали исходный файлI try

reset(fi); {открыли исходный) except

exit; {если не открывается – на выход) end;

assignfile(fo,ChangeFileExt(fname, 1 .sec’)) ; rewrite(fo);

read(fi,xb); {прочли первый байт)

Forml.Caption:=’ProbaCrypt: ‘+ExtractFileName(fname);

/название файла – в заголовок) Memol.Lines.Clear; Memol.Lines.Add(‘Подождите…’);

Application.ProcessMessages; Memol.Lines.Clear; while not eof(fi) do begin

xb:=xb xor ord(key[i]); {шифруем)

Memol.Text:=Kemol.Text+chi(xb); {выводим в Memo)

write(fo,xb); (записываем в файл)

read(fi,xb); {если конец файла – выходим) except break; end; end; end;

closefile(fi) ;

erase(fi); {уничтожаем исходник)

closefile(fo);

{зашифровали) end;

Тут мы складываем через операцию XOR каждый байт исходного файла с байтами ключа по очереди; когда ключ заканчивается, мы опять начинаем с его первого символа. Результаты пишем в файл с расширением sec (от "security") и выводим в Memoi. В точности ту же операцию мы производим при расшифровке, только уже с зашифрованным файлом, в результате чего исходный файл восстанавливается полностью:

procedure TForml.Buttor.2Click(Sender: TObject); begin {Pa оцифровать j OpenDialogl.FileName:=”; {очистим/ OpenDialogl.Filter: = ‘№4>poBaHHbie файлы| *.sec’ ; if OpenDialogl.Execute then fname:OpenDialogl.FileName else exit; assignfile(fi, fname); {открыли шифрованный) try

reset(fi); except exit; end;

assignfile(fo,ChangeFileExt(fname, ‘.txt’)); rewrite(fo); {перезаписываем старый) read(fi,xb);

Forml.Caption:=’ProbaCrypt: ‘+ExtractFileNajne (fname) ;

!название файла – в заголовокj

Memol.Lines.Clear;

Memol.Lines.Add(1 Подождите…’);

Application.ProcessMessages; {чтобы увидеть предупреждение) Memol.Lines.Clear; while not eof(fi) do begin /асе без изменений, как выше} for i:=l to length(key) do begin

xb:=xb xor ord(keyU));

Memol.Text:=Memol.Text+chr(xb);

write(fo,xb);

read(fi,xb); except break; end; end; end;

closefile(fi); closefile(fo); {pa сшифровали) end;

Обратите внимание, что я зашифрованный файл не уничтожаю - в принципе операцию шифрования можно производить сколько угодно раз над уже зашифрованным текстом, для его расшифровки придется также повторить се столько же раз, и этот прием часто применяют в "официальных" алгоритмах шифрования (только в данном случае придется зашифрованный файл переименовать вручную или несколько модифицировать программу, в имеющемся виде она вам не позволит открыть один и тот же файл и для расшифровывания и для шифрования). Естественно, процедуру шифрования можно применять к абсолютно любым файлам, не только текстовым.

Есть тонкий момент, связанный с уничтожением оригинала - как известно, при уничтожении дискового файла он не стирается, подобно музыкальной записи на магнитной ленте, а просто в заголовочных структурах FAT место, которое ои занимает, помечается, как свободное (примерно также происходит это и в NTFS). Именно с этой особенностью была связана работа DOS- программы Unerase (если кто помнит, что это такое). Поэтому, если вы даже удалите файл из Корзины (удаленный из нашей программы файл в Корзину, правда, итак не попадает), на диске останется его содержание до тех пор, пока туда не будет записано что-то еще. Поэтому для особо параноидальных личностей продвинутые программы шифрования предлагают опцию, при которой файлы после стирания уничтожаются гарантированно- на их место записываются нули. В нашем случае для этого в принципе достаточно не закрывая файл, заполнить его байтами с нулевым значением (или любым другим, но не увеличивая и не уменьшая размер файла), записать это на диск (закрыв файл), а потом уже его уничтожать. Правда, в Windows стопроцентной гарантии, что он запишется в точности на то же место, я дать не могу, поэтому лучше в таких случаях применять все же "официальные" программы, которые, кстати, уничтожают следы исходника не только в той области диска, где он хранился, но и SWAP-файле Windows, если они там остались.

Что касается генерации случайных ключей, то вот один из способов. I Указывать реализацию полностью я не буду, так как она проста. Генератор псевдослучайных чисел в Delphi (и не только в Delphi) устроен следующим образом: через переменную RandSeea задается начальное число генератора (но умолчанию оно равно 0). Тогда функция Random при последовательном обращении к ней всегда будет возвращать один и гот же набор чисел, независимо от того, в какой программе и когда мы ее используем. Отсюда и способ- вы устанавливаете в программе такой генератор и на его основе генерируете ключ, например. вот так можно сгенерировать случайный 16-байтный (128-битный) набор символов, который будет зависеть только от величины начального смещения генератора init:

:

init:integer; st:string;

st:=”;

RandSeed:=init; {начальное смещение} while length(st)<16 do begin xb:=Random(255); if xb>31 then st:=st+chr(xb); end;

При этом вы передаете вашему корреспонденту не сам ключ, а величину ir.j .. У него будет сгенерирован по идентичной процедуре в точности тот же ключ. Можно придумать, разумеется, и более хитрые механизмы реализации этого метода.

Хочу заметить, чтобы не затемнять суть дела, ранее в программе я использовал традиционное побайтное чтение из дискового файла. Резко ускорить процедуру можно при использовании одного из механизмов предварительного чтения файла в память- file mapping, как в главе 14, потокового чтения или любого другого способа организации динамических массивов в памяти (см. главу 21). В примере с использованием стеганографии, к которому мы сейчас приступим, частично положение будет исправлено.

Таким образом при выполнении исключающего ИЛИ всегда будет нулевое значение, если переменные имели одинаковые значения.

Особенность XOR в том, что одной и той же функцией можно как зашифровать данные, так и расшифровать их. Это простой метод шифрации данных, который может быть взломан достаточно быстро при наличии достаточно большого зашифрованного текста, или большого словаря паролей. Но тем не менее это уже можно применять для небольшой первоначально защиты данных.

В контексте Qt применение XOR ничем не отличается от того, как если бы программа была написана без использования Qt. Вопрос здесь в другом, как правильно извлечь данные для шифрации из объектов QString, например, если текст вводился в QTextEdit.

Для этого напишем программу, которая содержит:

  • QTextEdit, в который будет введён текст, который будет зашифрован.
  • QLineEdit, в который будет вводится ключ шифрования.
  • QPushButton, в слоте обработчике нажатия которого, будет выполняться шифрация/дешифрация данных. Снова отмечу, что метод будет использоваться один и тот же.

Программа будет выглядеть следующим образом:

Структура проекта

Проект написан с использованием CMake, поэтому структура будет следующей:

  • CMakeLists.txt
  • main.cpp
  • EncoderWidget.h
  • EncoderWidget.cpp

Функция шифрации/дешифрации

Для реализации шифрации/дешифрации необходимо:

  • Наличие массива char с исходными данными
  • Длина массива с исходными данными
  • Ключ в виде массива char
  • Длин ключа
  • А также массив с выходными данными.
const char* input; int inputLength; const char* key; int keyLength; char output; for (int i = 0; i < inputLength + 1; ++i) { output[i] = input[i] ^ key; }

Слот обработчик для выполнения шифрации

Как уже говорилось, чтобы зашифровать текст, необходимо правильно извлечь данные из полей ввода. Для этого необходимо текст в виде строки QString перевести в QByteArray , из которого извлечь данные в виде const char*. А также забрать длину этих данных.

Здесь есть один момент. Данный из QString в QByteArray переводятся через использование метода toLatin1(), который приведёт данные к ASCII таблице, что приведёт к порче данных, если текст был написан на кириллице. То есть данный подход к шифрации будет актуален, если используются только символа из таблицы ASCII, например, для логина и пароля.

Void EncoderWidget::encodeDecode() { const char* input = m_textEdit->toPlainText().toLatin1().data(); int inputLength = m_textEdit->toPlainText().toLatin1().length(); const char* key = m_keyLineEdit->text().toLatin1().data(); int keyLength = m_keyLineEdit->text().toLatin1().length(); char output; for (int i = 0; i < inputLength + 1; ++i) { output[i] = input[i] ^ key; } m_textEdit->setText(QString::fromLatin1(output, inputLength)); }