Шифрование AES / CBC / PKCS7Padding с Bouncy Castle

В этой статье будет попытка написать шифрование сообщения алгоритмом AES, а также в добавление к нему цифровой подписи о алгоритму SHA512.

Для начала создадим новый Maven проект и в файл “pom.xml” добавим зависимость:

Создадим экземпляр AESEngine:

Пока всё просто. Но просто так использовать AES нельзя. Нам нужно ещё использовать режим сцепления блоков (Cipher Block Chaining), чтобы результат шифрования последующего блока зависел от результата шифрования предыдущего. Подобная тактика защитит от вставки злоумышленником дополнительных блоков в середине, а также затруднит поиск часто встречающихся фраз в зашифрованном тексте, что могло бы скомпрометировать все наши усилия. Cipher Block Chaining за нас будет реализовывать класс CBCBlockCipher:

Уже лучше. Теперь нужно решить ещё одну проблему. AES и CBC оперируют блоками. Мы же передаём строки произвольной длины. Что делать, если размер передаваемой нами строки не может делиться на размер блока без остатка? Как передавать оставшийся хвост, который меньше размера блока? Для этого используется дополнение или padding, то есть дополнительные данные, вставляемые в конец сообщения. Мы будем использовать PKCS7 padding:

Также мы будем подписывать зашифрованные сообщения с помощью SHA512:

Алгоритмы AES и SHA512 используют ключи, которые мы будем передавать в наш код в массиве байт:

Экземпляру HMac нужно указать ключ:

Нам ещё нужен вектор инициализации (initialization vector или IV) — случайное число, которое вместе с ключом будет использоваться в алгоритме для шифрования и расшифровки. Каждое новое сообщение будет иметь другой IV, что позволит генерировать разные результаты шифрования даже для одинаковых частей сообщений. Мы же понимаем, что в передаваемых сообщениях могут повторяться, например, имена людей, приветствие или ещё что-либо. Благодаря IV шифрование будет порождать разные результаты для этих повторяющихся кусков текста в разных сообщениях:

Обратите внимание, что для генерации IV мы использовали не обычный Random, а SecureRandom. Обычный Random не подходит для генерации случайных чисел, связанных с криптографией.

Проинициализируем наш cipher, передав ему ключи и IV:

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

Дальше всё просто. Мы вызываем cipher.processBytes и hmac.update, передавая им считанные из исходного сообщения блоки байт, а под конец вызываем cipher.doFinal вычисления и записи последнего блока, а также hmac.doFinal для вычисления подписи:

И самое главное! Под конец заполняем нулями все используемые в процессе массивы байт, чтобы они в ОЗУ не оставалось приватной информации и ключей после использования (статья на эту тему):

Конечный результат:

Отлично. С шифрованием разобрались. Теперь нам нужно реализовать расшифровку сообщения.

Инициализация cipher и hmac происходит аналогично:

Так как вектор инициализации мы записали в начале шифрованного сообщения, то считаем его обратно:

Дальше просто вызываем cipher.processBytes и hmac.update, как и в случае с шифрованием:

Затем сравниваем полученные подписи:

Очищаем массивы байт после использования:

В результате должен получиться вот такой метод:

Вот и всё. Пример вызова наших методов:

Исходники на GitHub

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *