Задание: разработать библиотеку классов, реализующих криптографический алгоритм
AES (Advanced Encryption Standard). Предусмотреть следующие варианты шифровки/дешифровки:

а) шифрация данных из файла в файл;
б) дешифрация данных из файла в файл;
в) дешифрация данных из файла в память.

Ключ шифрования должен задаваться строкой, содержащей последовательность шестнадцатеричных цифр. Длина ключа - 128, 192 или 256 бит.

 

Введение в проблему

Advanced Encryption Standard (AES), также известный как
RIJNDAEL, — это симметричный алгоритм блочного шифрования (размер блока - 128 бит, ключ - 128/192/256 бит), выбранный в ходе конкурса и принятый в качестве американского стандарта шифрования правительством США. Выбор был сделан с расчетом на повсеместное использование и активный анализ алгоритма, как это было с его предшественником
DES. Государственный институт стандартов и технологий США (National Institute of Standards and Technology, NIST), после пятилетней подготовки, 26 ноября 2001 года опубликовал предварительную
спецификацию AES, а 26 мая 2002 года AES был объявлен стандартом. По состоянию на 2006 год
AES является одним из самых распространенных алгоритмов симметричного шифрования в мире.

 

История AES

В далеком 1998 году NIST объявил конкурс на создание алгоритма, удовлетворяющего выдвинутым институтом требованиям. Он опубликовал все несекретные данные о тестировании кандидатов на роль
AES и потребовал от авторов алгоритмов сообщить о базовых принципах построения используемых в них констант. В отличие от ситуации с DES, NIST при выборе
AES не стал опираться на секретные и, как следствие, запрещенные к публикации данные об исследовании алгоритмов-кандидатов.

Чтобы быть утвержденным в качестве стандарта, алгоритм должен был:

  1. реализовать шифрование частным ключом;
  2. представлять собой блочный шифр;
  3. работать со 128-разрядными блоками данных и ключами трех размеров (128, 192 и 256 разрядов).

Дополнительно кандидатам рекомендовалось:

  1. использовать операции, легко реализуемые как аппаратно (в микрочипах), так и программно (на персональных компьютерах и серверах);
  2. ориентироваться на 32-разрядные процессоры;
  3. не усложнять без необходимости структуру шифра для того, чтобы все заинтересованные стороны были в состоянии самостоятельно провести независимый криптоанализ алгоритма и убедиться, что в нем не заложено каких-либо недокументированных возможностей.

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

Перед первым туром конкурса в NIST поступило 21 предложение, 15 из которых соответствовали выдвинутым критериям. Затем были проведены исследования этих решений, в том числе связанные с дешифровкой и проверкой производительности, и получены экспертные оценки специалистов по криптографии. В августе 1999 года NIST объявил пять финалистов, которые получили право на участие во втором этапе обсуждений.
2 октября 2000 года NIST сообщил о своем выборе – победителем конкурса стал алгоритм
RIJNDAEL (произносится как «райндол») бельгийских криптографов
Винсента Раймана и Йоана Дамана, который зарегистрирован в качестве официального федерального стандарта как
FIPS 197 (Federal Information Processing Standard).

Для меня остается загадкой, зачем в российском вузе преподают стандарты иностранных государств. Видимо, исходят из принципа, что врага надо знать в лицо :). Ладно, в общем-то, это не наше дело. Нам надо просто программно реализовать основу национальной безопасности США.

 

Начало работы

Итак, приступим. Так как по заданию нам надо было разработать библиотеку классов, то начнем с описания этих классов, то есть с заголовочных файлов. Наш класс будет иметь три метода, которые реализуют основные операции, перечисленные в задании: encryptFile - шифрация данных из файла в файл, DecryptToFile - дешифрация данных из файла в файл, DecryptToMemory - дешифрация данных из файла в память. Собственно, в заголовочном файле нет ничего особенного, поэтому просто приведу его листинг с комментариями (смотри листинг 1).

Как видно из листинга, мы определяем всего три вышеперечисленных метода и конструктор с деструктором. Теперь займемся реализацией этих методов.

 

Вспомогательный код

Прежде чем приступить непосредственно к выполнению трех основных пунктов задания, надо подготовиться, то есть написать пару вспомогательных функций и подключить несколько заголовочных файлов. Эти файлы мы нашли в интернете, ведь ты не думал, что мы будем с нуля реализовывать криптографический госстандарт США. В этих файлах содержится куча полезных структур и алгоритмов, которые позволяют заниматься разработкой непосредственно класса, а не вникать в сложную
математику AES. В большинстве случаев преподаватели сами предоставляют подобные куски кода, чтобы облегчить и без того тяжелую жизнь студента. Все эти файлы ты найдешь (а при желании и детально ознакомишься с их содержанием) на нашем
DVD.

Взглянем на листинг. Там как раз описаны две наши вспомогательные функции и один макрос.

Все это хозяйство служит для заполнения массива псевдослучайных чисел. Непосредственно же заполнением занимается эта функция:

void FillRand(char *buf, const int len)

Как ее использовать, ты узнаешь чуть позже. Макрос RAND(a,b) вычисляет псевдослучайное число, а функция cycles считывает число из таймера процессора. Все это войдет в cpp-файл с реализацией класса. Также нам надо включить еще три заголовочных файла:

#include "caes.h"
#include "aes.h"
#include <stdio.h>

Здесь, caes.h описывает наш класс; aes.h – один из тех файлов, что мы нашли в интернете; stdio.h - говорить стыдно, все и так должны знать, что это стандартный ввод/вывод в
C.

 

Шифрование файла

Настало время заняться функцией шифрования. Прототип ее выглядит так:

int encryptFile(std::string inFileName, std::string outFileName, std::string
secretKey)

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

Весь код, к сожалению, мы привести не можем, поскольку он занял бы значительный объем статьи; полностью ты найдешь его только
на нашем диске

Здесь первым делом мы объявляем и инициализируем несколько переменных. Затем в цикле проверяем корректность введенного ключа, а поскольку передается он в функцию в виде строки, то должен содержать лишь цифры от 0 до 9 и латинские буквы от A до F. Кроме этого, дополнительно мы проверяем длину ключа. После этого открываем входной и выходной файлы и проводим предварительную инициализацию нужных нам структур. Далее в листинге должен идти код шифрования, но, как мы уже говорили, он опущен. Вот и все, главное - после шифрации не забыть корректно закрыть файлы, чтобы сохранить все изменения.

 

Дешифрация из файла в память

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

int DecryptToMemory(std::string fileName, std::string secretKey, unsigned char* &buffer)

Содержимое файла fileName расшифровывается ключом secretKey и помещается в участок памяти, на который ссылается переменная buffer. Код этой функции практически идентичен коду в листинге 3, но есть и небольшие отличия. Во-первых, мы открываем лишь один файл, а во-вторых, мы определяем размер файла и автоматически выделяем требуемый объем памяти для расшифрованной информации.

Последнее, что нам остается сделать, – это реализовать функцию расшифровки одного файла в другой. Ее прототип выглядит так:

int DecryptToFile(std::string inFileName, std::string outFileName, std::string key)

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

Для начала мы объявляем указатель на unsigned char. Затем с помощью реализованной выше функции DecryptToMemory расшифровываем содержимое файла в память. Если все прошло успешно, то записываем участок памяти, на который ссылается переменная buffer, в файл outFileName и удаляем память, выделенную функцей
DecryptToMemory.

 

Проверка работы

И в заключение напишем маленькую программку, которая будет шифровать/дешифровать файлы. Для этого нам понадобится консольный проект (я использовал Visual Studio 2003). Включим в проект файлы нашего недавно созданного класса и то, что мы скачали из интернета (aes.h, aeskey.c, aescrypt.c, aesopt.h, aestab.c, aestab.h). Проект мы назвали AES_demo, а потому и главный cpp-файл называется aes_demo.cpp. В нем мы подключим caes.h и определим 256-битный ключ шифрования.

#include "caes.h"

#define RESCRYPTO_KEY "1fe2daD67821f83092bbceeadf821fe0923ed923edfe98df98acD6238119faed"

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

AES_demo.exe /encrypt not_crypt_file.txt crypt_file.txt

После такого вызова в crypt_file.txt мы увидим зашифрованное содержимое not_crypt_file.txt. Для дешифровки надо вбить в командную строку следующее:

AES_demo.exe /decrypt crypt_file.txt decrypt_file.txt

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

Вот и все. Конечно, для сдачи лабы одной программы недостаточно - надо уметь еще и объяснить, что ты написал. Но если бы мы начали здесь объяснять
алгоритм AES, на это ушел бы весь объем журнала. Советуем поискать тебе инфу в интернете, благо там ее предостаточно. Например, вот эта ссылка кратко описывает основные моменты, правда, на английском языке:
http://en.wikipedia.org/wiki/Advanced_Encryption_Standard. В общем, удачи, не прогуливай пары ;).

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии