Главная > криптография, Программирование > Криптография: Шифр Вернама и его реализация на C++ и Delphi.

Криптография: Шифр Вернама и его реализация на C++ и Delphi.

В криптографии шифр Вернама известен также как «схема одноразовых блокнотов». Решение является системой симметричного шифрования, которая была изобретена в 1917 году сотрудниками AT&T Мейджором Джозефом Моборном и Гильбертом Вернамом.

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

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

  1. Ключ для шифрования выбирается случайным образом.
  2. Длина ключа должна быть равна длине открытого текста.
  3. Ключ должен использоваться ТОЛЬКО один раз.

А теперь поподробней о самом шифре и процессе шифрования. Так как этот шифр был придуман для компьютерных систем, то следует заметить что базируется он на двоичной арифметике. Надеюсь что вы знакомы с ней ;).  Основным объектом рассмотрения в данном методе шифрования является логическая операция XOR (взаимоисключающее ИЛИ). Таким образом, так как у нас двоичная арифметика, то все операции будут осуществляется над нулем (0) и единицей (1). Логическая операция XOR в отличие от операции OR при логическом сравнении 0 и 1 дает 1,  при сравнении 1 с 1 дает 0, а при 0 с 0 дает 0. Следовательно, если мы выполним операцию XOR над числами 10110 и 11010 то получим: 10110 xor 11010 = 01100. Надеюсь что принцип работы операции XOR понятен.

Далее,  так как шифр работает с двоичной системой исчисления, необходимо понимать что буквы это всего лишь некоторая интерпретация числа, то есть число является кодом символа некоторой таблицы кодировок. К примеру, наиболее популярные таблицы кодировок это: ANSI, ASCII и UTF(unicode). Естественно, что в каждой таблицы один и тот же символ может иметь разный код, поэтому во избежании путаницы имейте это ввиду и используйте одну и ту же кодировку при шифровании и дешифровании.  Замечу, что использовать можно и свою (придуманную) кодировку. Кроме того, данный шифр может использоваться не только на компьютерах. Его можно применить и к тексту написанному на бумаге. Только перед применением надо сделать некие преобразования.

Таким образом, перед тем как осуществить шифрование необходимо перевести все символы в их однозначную числовую интерпретацию. Если Вы решили применить шифр в компьютерных системах то для вас уже существуют соответствующие кодировки, и язык программирования выбранный Вами скорее всего поддерживает явное или неявное преобразование. И вам остается только произвести над каждой парой операцию XOR. В различных языках это операция определяется по разному, приведу пример для Pasсal/Delphi/Assembler - xor, C/C++ - ^.  Однако, если же Вы  решили применить шифр Вернама к письменному тексту, то к примеру дайте каждой букве используемого вами алфавита, соответствующий ей порядковый номер в двоичной системе исчисления. Например, если вы используете русский алфавит (без учета буквы Ё) то это будет выглядеть так: а -> 00000, б -> 00001, в -> 00010, г -> 00011, ... я -> 111111. Тем самым мы определили свою таблицу кодировки. После этого, написав сообщение и придумав ключ, преобразуйте, каждый символ в их числовое значение, соответствующее вашей таблице кодировки, и после этого осуществляйте операцию XOR над каждой соответствующей парой. Так как данный метод шифрования является симметричным, следовательно применив операция XOR каждой паре символов шифр-текста (шифрограммы) и ключа, мы получим открытый текст.

Программная реализация шифра Вернама (C/C++ и Delphi).

На основе изученного материала, я покажу как реализовать шифр Вернама на С/С++ и Pascal/Delphi. Программу целиком писать нет надобность, поэтому будем рассматривать блочную структуру, для того чтобы каждый мог без особого труда заточить ее под сви надобности. А теперь, от слов к делу.

Допусти что у нас есть строка или массив символов - oStr. Это будет открытый текст, который надо зашифровать. Теперь нам надо определить случайный ключ, длиной равной длине открытого текста. Для простоты понимания, мы воспользуемся стандартной функцией генерации случайных чисел: random для Pascal/Delphi и rand для C/C++. Но замечу сразу что это не лучший вариант, в случае если вы хотите реализовать действительно стойкий шифр. Данный пример выбран из соображения простоты. Теперь покажем как это будет выглядеть в исходном коде:

Pascal/Delphi

var
oStr, key:string;
i:integer;
begin
oStr:='Holo word!'; //определяем открытый текст
randomize; //Необходимая функция для функции random, чтобы последняя каждый раз выдавала случайные значения
//генерируем случайный ключ длиной равной длине открытого текста
for i:=1 to length(oStr) do
key[i]:=Chr(random(255)); //генерируем случайное число из диапозона от 0 до 255, и полученое число переводим в символ;
end;

C/C++

  int i,len; //Определяем необходимые переменные
len = strlen("Holo word!");// определяем длину строки открытого текста
char *oStr = new char[len]; //объявляем динамический массив указанной длины для открытого текста
char *key = new char[len]; //точно такой же массив объявляем для ключа
oStr="Holo word!"; //помещаем в массив открытый текст
// определяем ключ случайным образом
for(i = 0; i < len; i++)
key[i]=(char)rand()%255;

Так, самое основное мы сделали. Теперь нам осталось лишь реализовать шифрование, для этого мы опишем цикл в котором мы будем посимвольно осуществлять операцию XOR. Но для этого нам понадобиться еще объявить массив приемник, в который мы поместим зашифрованный текст. Смотрим:
Pascal/Delphi

procedure shifr_Vernam;
var
oStr, key, shStr :string;
i:integer;
begin
oStr:='Holo word!'; //определяем открытый текст
randomize; //Необходимая функция для функции random, чтобы последняя каждый раз выдавала случайные значения
//генерируем случайный ключ длиной равной длине открытого текста
for i:=1 to length(oStr) do
key[i]:=Chr(random(255)); //генерируем случайное число из диапозона от 0 до 255, и полученное число переводим в символ;
//собственно само шифрование
for i:=1 to length(oStr) do
shStr[i]:=oStr[i] xor key[i];
//для наглядности выведем на экран результат работы
writeLn('Otkrytyi text: ',oStr);
writeLn('Zashifrovanyi text: ',shStr);
end;

C/C++
void shifr_Vernam
{
int i,len; //Определяем необходимые переменные
len = strlen("Holo word!");// определяем длину строки открытого текста
char *oStr = new char[len]; //объявляем динамический массив указанной длины для открытого текста
char *key = new char[len]; //точно такой же массив объявляем для ключа
char *shStr = new char[len]; //массив-приемник для зашифрованного текста
oStr="Holo word!"; //помещаем в массив открытый текст
// определяем ключ случайным образом
for(i = 0; i < len; i++)
key[i]=(char)rand()%255;
//собственно само шифрование
for(i = 0; i < len; i++)
shStr[i]=oStr[i]^key[i];
//для наглядности выведем на экран результат работы
printf("\nOtkrytyi text: %s", oStr);
printf("\nZashifrovanyi text: %s", shStr);
}

На этом всё, реализация шифрования закончена. Надеюсь, что вы догадаетесь как реализовать дешифровку(Подсказка: те же действия нужно провести для шифрованного текста). Спасибо за внимание.

Пожалуйста, оцените полезность и качество данной статьи. Одна звезда - плохо, 5 - хорошо.
1/5. Мы будем признательны, если вы напишете комментарий с причиной низкой оценки.2/5. Мы будем признательны, если вы напишете комментарий с причиной низкой оценки.3/5. Мы будем признательны, если вы напишете комментарий с причиной низкой оценки.4/5.5/5. (20 голосов, средний: 4,45 из 5)
Загрузка...
  1. Константин
    3 июня 2013 в 11:10 | #1

    Выдает ошибку

    [Error] Unit1.pas(69): Operator not applicable to this operand type

    на shStr[i]:=oStr[i] xor key[i];
    как исправить?

  2. lizz
    3 июня 2013 в 18:39 | #2

    @ Константин
    Я в дельфи 0, но попробуйте так:
    shStr[i] := Char(byte(oStr[i]) xor byte(key[i]));

    Отпишитесь, если помогло - поправим статью. :)

  3. Константин
    3 июня 2013 в 19:54 | #3

    теперь на неё ругается
    key[i]:=Chr(random(255));

  4. Константин
    3 июня 2013 в 21:16 | #4

    так вроде разобрался что то шифрует

    только так и не понял для чего первый кусок Delphi кода и куда его втыкнуть на какой компонент или обработчик

    и теперь вопрос как дешифруется всё это дело тоже не понял

  5. lizz
    4 июня 2013 в 12:19 | #5

    @Константин
    Это симметричная система шифрования. Дешифровка происходит так же, как и шифрование: берём тот же ключ и вместо открытого текста - зашифрованный, производим те же операции, что и при шифровании, и получаем открытый текст. Как описано в статье, вся прелесть этого шифра в том, что результат полностью зависит от ключа, т.е. обычным брутфорсом его не сломать - вы получите сколько угодно много (в пределах заданной длины) вполне осмысленных текстов, в зависимости от ключа. Хоть стихи Пушкина можете расшифровать, подобрав соответствующий ключ к моему комменту. :)

  6. Dasha Kepp
    14 августа 2013 в 09:50 | #6

    Я так и не поняла, как придумывать ключ ._______.

  7. lizz
    15 августа 2013 в 01:07 | #7

    @Dasha Kepp
    Никаких особых требований нет. Если вам нужен именно шифр Вернама, то есть только 3 пункта, которые описаны в статье (например, можно взять число "пи" с нужным количеством знаков после запятой в качестве ключа). Для не "боевого" применения можно этими пунктами пренебречь и выбрать вообще любой ключ (хоть стихотворение Пушкина: "Я вас любил..." или "А шутку не могу придумать я другую...", хоть текст этой статьи).

  8. Я
    30 октября 2013 в 21:40 | #8

    Зачем дважды повторять строку? Можно же написать так:
    const char *oStr="Holo word!";
    int len = strlen(oStr);

  9. Я
    30 октября 2013 в 21:42 | #9

    И счетчик цикла не обязательно было объявлять заранее.

  10. RENSER
    9 января 2014 в 07:17 | #10

    Я :
    Зачем дважды повторять строку? Можно же написать так:
    const char *oStr="Holo word!";
    int len = strlen(oStr);

    Я :
    И счетчик цикла не обязательно было объявлять заранее.

    Слушайте вы, умники, человек старался, писал интересную статью, а вы прицепились к объявлению переменных. Сами хоть напишите что-нибудь???

  11. lizz
    11 января 2014 в 13:29 | #11

    @RENSER

    Ну почему же, конструктивная критика всегда полезна. Тем более она написана не в агрессивном стиле и без перехода на личности. :)

  12. Nikfel
    25 июня 2014 в 21:55 | #12

    Спасибо большое. Попробовал интересный пример. Компилировал на C++. Вот только мусор появляется при выводе ключа и зашифрованного текста, т.к. не хватает нулевого символа конца строки.
    Ключ можно формировать без функции rand или с ней надёжнее будет ключ? По идее дешифровка данных будет возможна если иметь саму программу и сам зашифрованный файл, т.е. изучив код самой программы можно будет расшифровать файл или это не так просто будет?

  13. lizz
    27 июня 2014 в 02:29 | #13

    @Nikfel
    Ответы насчёт безопасности зависит от требований. Если говорить о каких-то серъёзных системах (или о строгом соблюдении требований к ключу), то "стандартная" функция rand скорее всего не подойдёт, т.к. обычно имеет перекосы в распределении. Но вряд ли вы собираетесь использовать какие-нибудь аппаратные генераторы случайных чисел (ГСЧ), поэтому можете хоть с rand'ом работать, хоть самому придумывать ключ. Кстати, по этой же причине серъёзные онлайн-казино также пользуются аппаратными ГСЧ.

    Насчёт возможности расшифровать текст вы заблуждаетесь. По Шеннону (если мне не изменяет память), безопасность шифра должна зависеть исключительно от безопасности ключа, а не от секретности алгоритма. И шифр Вернама - единственный (!) абсолютно безопасный при соблюдении всех 3х вышеперечисленных условий. Т.е. результат дешифровки полностью зависит от введённого ключа. При методе перебора вы можете хоть стихи Пушкина найти в зашифрованном тексте, только нужно подобрать ключ. Насколько я помню, с этим связан один куръёз: именно этот шифр удалось "вскрыть" в фильме "Пароль рыба-меч", что технически невозможно (сам я не смотрел фильм, только слышал о таком забавном факте).

  14. Nikfel
    29 июня 2014 в 20:47 | #14

    @ lizz
    Спасибо. Я просто хотел хранить ключ в самом exe и exe упаковывать каким-нибудь упаковщиком для защиты, но это похоже очень не надежно будет. Тогда вижу только один вариант хранения ключа - в отдельном файле и ещё раз шифровать другим ключом. А файл с ключом закрыть на доступ, т.е. при запуске exe файл с ключом будем не доступен для других программ кроме моей. Но тогда проблема будет - если прога не будет запущена то этот файл будет доступен для других программ. Или есть способ по надежнее для хранения ключа? Хранить планирую пароль от учетной записи администратора. Ещё как вариант можно запрашивать ввод ключа от пользователя, но хотелось бы без пароля обойтись.

  15. lizz
    11 июля 2014 в 15:46 | #15

    @Nikfel
    Между безопасностью и удобством почти всегда приходится делать выбор. Как сказано выше, безопасность зашифрованного текста полностью зависит от безопасности ключа. ssh обычно хранит свои ключи просто в папочке ~/.ssh/, при этом принудительно требуя установить соответствующие права на доступ к ним. Но если вам так хочется спрятать ключ или пароль, то можно ещё посмотреть в сторону стеганографии, но и тут тоже не всё так просто и однозначно (особенно если предположить что злоумышленнику доступен алгоритм вашей программы).

    А зачем вам вообще хранить пароль администратора в зашифрованном виде? Чем принципиально будет отличаться знание ключа от знания пароля?

    Кстати, двойное шифрование - это достаточно неоднозначный ход. Можете погуглить на эту тему.

  16. Nikfel
    16 июля 2014 в 20:02 | #16

    @ lizz
    Спасибо. Пароль администратора шифрую из-за того чтобы потом запускать программы от имени администратора без ввода пароля. Просто сижу под учёткой пользователя, а некоторые программы запускаются только от имени администратора. Знаю что есть программа AdmiLink, которая умеет запускать программы от имени другого пользователя и она написана на языке Delphi и в ней используется шифрование из исходников DCP Crypt, но увы мне такая программа не нравится, поэтому решил написать свою на C++ для большего удобства. Решил в качестве ключа гененрировать определенный ключ по данным в системе, т.е. для расшифровки потребуется получить сведения о установленной системе Windows. Ещё как вариант есть идея ставить пароль на запуск программы, чтобы её использовать, т.е. пароль(ключ расшифровки) будет нужен для расшифровки пароля, который будет храниться в файле.

  17. lizz
    17 июля 2014 в 01:57 | #17

    @Nikfel
    В линуксе с этим как-то проще. Можно разрешить выполнять определённые программы или команды от имени root'а без ввода пароля через sudo. По windows увы, ничего не подскажу. Хотя порт sudo (или другие альтернативы) гуглится легко, но не знаю насколько он хорош. Тем более его сырцы лежат на sourceforge, а лично у меня это ассоциируется со свалкой мёртвых проектов.

  1. Пока что нет уведомлений.