Шифрование и дешифровка в Java

Время на прочтение

Привет, Хабр! Представляю вашему вниманию перевод второй статьи «Java Cipher» автора Jakob Jenkov из серии статей для начинающих, желающих освоить основы криптографии в Java.

Привет, Хабр! Представляю вашему вниманию перевод статьи «Java Cryptography» автора Jakob Jenkov.

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

Содержание
  1. Введение​
  2. Сначала написание теста​
  3. Расшифровка​
  4. Расширение криптографии Java
  5. Основные классы и интерфейсы
  6. Provider (Поставщик криптографии)
  7. Cipher (Шифр)
  8. Инициализация шифра
  9. Шифрование или дешифрование данных
  10. Keys (Ключи)
  11. Безопасность ключа
  12. Генерация ключа
  13. Генерация пары ключей
  14. Хранилище ключей (Key Store)
  15. Инструмент управления ключами (Keytool)
  16. Дайджест сообщения (MessageDigest)
  17. Краткое введение в класс MessageDigest
  18. Код аутентификации сообщения (MAC)
  19. Подпись (Signature)
  20. Подпись данных
  21. Проверка подписи
  22. Полный пример подписи и проверки
  23. Java Cryptography
  24. Шифрование и дешифрование​
  25. Файл​
  26. На основе пароля​
  27. Объект​
  28. Обзор​
  29. Параметры АЭС​
  30. Входные данные​
  31. Секретный ключ​
  32. Вектор инициализации (IV)​
  33. Алгоритм AES​
  34. Java Cipher (Шифр)
  35. Создание шифра
  36. Режимы шифрования
  37. Шифрование и расшифровка данных
  38. Шифрование / Расшифровка части байтового массива
  39. Шифрование / Расшифровка в существующий байтовый массив
  40. Повторное использование экземпляра шифра
  41. Оглавление
  42. Варианты AES​
  43. ЕЦБ​
  44. Си-Би-Си​
  45. CFB​
  46. ОФБ​
  47. CTR​
  48. ГКМ​
  49. Размер данных после шифрования​
  50. Расшифровка строк​
  51. Резюме​
  52. Работа с файлами​
  53. Вывод​
  54. Заключение​

Введение​

3DES или алгоритм тройного шифрования данных — это блочный шифр с симметричным ключом, который трижды применяет алгоритм шифрования DES к каждому блоку данных.

В этом руководстве мы узнаем, как создавать ключи 3DES и использовать их для шифрования и дешифрования строк и файлов в Java.

I am writing a Java application which can «encrypt» and consequently «decrypt» whatever binary file.

I am just a beginner in the «cryptography» area so I would like to write a very simple application for the beginning.

Next, when somebody wants to «decrypt» that file, he has to enter the password («123456789») first and after that the file could be decrypted (thus «shift down every byte by 1») and consequently saved to the output file via java.io. FileOutputStream.

I am just wondering how to «store» the password information to the encrypted file so that the decrypting application knows if the entered password and the «real» password equals?

Probably it would be silly to add the password (for example the ASCII ordinal numbers of the password letters) to the beginning of the file (before the encrypted data).

So my main question is how to store the password information to the encrypted file?

Сначала написание теста​

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

Поскольку мы просто используем существующую функциональность JDK, внешние зависимости не нужны.

Во- первых, мы зашифруем содержимое с помощью вновь сгенерированного секретного ключа (в этом примере мы используем AES, Advanced Encryption Standard , в качестве алгоритма симметричного шифрования).

Также обратите внимание, что мы определяем полную строку преобразования в конструкторе ( AES/CBC/PKCS5Padding ), которая представляет собой конкатенацию используемого шифрования, режима блочного шифрования и заполнения ( алгоритм/режим/заполнение ). Реализации JDK по умолчанию поддерживают ряд различных преобразований, но обратите внимание, что не каждая комбинация может считаться криптографически безопасной по современным стандартам.

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

Всем привет. Нужно зашифровать и расшифровать файл Blowfish(ем). Я в этом деле новичек. Попробовал сделать. Получилось,что файл шифруется,но не рассшифровывается. Помогите понять в чем проблема. Заранее спасибо.

Symmetric Key Cryptography : Symmetric key uses the same key for encryption and decryption. The main challenge with this type of cryptography is the exchange of the secret key between the two parties sender and receiver.

All the above given steps and concept are same, we just replace algorithms.

Plain Text Before Encryption: AES Symmetric Encryption Decryption
Encrypted Text After Encryption: sY6vkQrWRg0fvRzbqSAYxepeBIXg4AySj7Xh3x4vDv8TBTkNiTfca7wW/dxiMMJl
Decrypted Text After Decryption: AES Symmetric Encryption Decryption

Example: Cipher with two modes, they are encrypt and decrypt. we have to start every time after setting mode to encrypt or decrypt a text.

Расшифровка​

Для расшифровки мы также должны сначала прочитать IV. После этого мы можем инициализировать наш шифр и расшифровать содержимое.

Опять же, мы можем использовать специальный класс Java, CipherInputStream , который прозрачно заботится о фактическом расшифровывании :

Расширение криптографии Java

Java cryptography API предоставляется так называемым расширением Java Сryptography Extension(JCE). J CE уже давно является частью платформы Java. Изначально JCE был отделен от Java из-за того, что в США действовали экспортные ограничения на технологии шифрования. Поэтому самые стойкие алгоритмы шифрования не были включены в стандартную платформу Java. Эти более надежные алгоритмы шифрования можно применять, если ваша компания находится в США, но в остальных случаях придется применять более слабые алгоритмы или реализовывать свои собственные алгоритмы шифрования и подключать их к JCE.

С 2017 года правила экспорта алгоритмов шифрования в США были значительно ослаблены и в большей части мира можно пользоваться международными стандартами шифрования через Java JCE.

Архитектура криптографии Java

Java Cryptography Architecture (JCA) — название внутреннего дизайна API криптографии в Java. J CA структурирован вокруг нескольких основных классов и интерфейсов общего назначения. Реальная функциональность этих интерфейсов обеспечивается поставщиками. Таким образом, можно использовать класс Cipher (Шифр) для шифрования и расшифровки некоторых данных, но конкретная реализация шифра (алгоритм шифрования) зависит от конкретного используемого поставщика.

Также можно реализовать и подключить свои собственные провайдеры, но вы должны быть осторожны с этим. Правильно реализовать шифрование без дыр в безопасности сложно! Если вы не знаете, что делаете, вам, вероятно, лучше использовать встроенный поставщик Java или использовать надежного поставщика, такого как Bouncy Castle.

Основные классы и интерфейсы

API криптографии Java состоит из следующих пакетов Java:

Основные классы и интерфейсы этих пакетов:

Provider (Поставщик криптографии)

Класс Provider (java.security. Provider) является центральным классом в Java crypto API. Для того чтобы использовать Java crypto API, вам нужно установить поставщика криптографии. Java SDK поставляется с собственным поставщиком криптографии. Если вы явно не установите поставщик криптографии, то будет использоваться поставщик по умолчанию. Однако этот поставщик криптографии может не поддерживать алгоритмы шифрования, которые вы хотите использовать. Поэтому вам, возможно, придется установить свой собственный поставщик криптографии.

Один из самых популярных поставщиков криптографии для Java crypto API называется Bouncy Castle. Вот пример, где в качестве поставщика криптографии устанавливается BouncyCastleProvider:

Cipher (Шифр)

Класс Cipher (javax.crypto. Cipher) представляет криптографический алгоритм. Шифр может использоваться как для шифрования, так и для расшифровки данных. Класс Cipher объясняется более подробно в следующих разделах, ниже будет его краткое описание.

Создание экземпляра класса шифр, который использует алгоритм шифрования AES для внутреннего использования:

Cipher cipher = Cipher.getInstance(«AES/CBC/PKCS5Padding»);

Инициализация шифра

Перед использованием экземпляра шифра его необходимо инициализировать. Экземпляр шифра инициализируется вызывом метода init(). Метод init() принимает два параметра:

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

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

Чтобы инициализировать экземпляр шифра для расшифровки данных, вы должны использовать Cipher. DECRYPT_MODE, например:

Шифрование или дешифрование данных

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

Читайте также:  Отчеты за работников в ФНС, ПФР, ФСС, Росстат

Пример шифрования данных с помощью метода doFinal():

Чтобы расшифровать данные, нужно передать зашифрованный текст(данные) в метод doFinal() или doUpdate().

Keys (Ключи)

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

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

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

Безопасность ключа

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

Генерация ключа

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

KeyGenerator keyGenerator = KeyGenerator.getInstance(«AES»);

SecureRandom secureRandom = new SecureRandom();
int keyBitSize = 256;
keyGenerator.init(keyBitSize, secureRandom);

SecretKey secretKey = keyGenerator.generateKey();

Полученный экземпляр SecretKey можно передать в метод Cipher.init(), например так:

Генерация пары ключей

Алгоритмы асимметричного шифрования используют пару ключей, состоящую из открытого ключа и закрытого ключа, для шифрования и дешифрования данных. Для создания асимметричной пары ключей вы можете использовать KeyPairGenerator (java.security. KeyPairGenerator). KeyPairGenerator будет более подробно описан в следующих главах, ниже простой пример использования Java KeyPairGenerator:

SecureRandom secureRandom = new SecureRandom();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(«DSA»);
KeyPair keyPair = keyPairGenerator.generateKeyPair();

Хранилище ключей (Key Store)

Java KeyStore — это база данных, которая может содержать ключи. Java KeyStore представлен классом KeyStore (java.security. KeyStore). Хранилище ключей может содержать ключи следующих типов:

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

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

Инструмент управления ключами (Keytool)

Java Keytool — это инструмент командной строки, который может работать с файлами Java KeyStore. Keytool может генерировать пары ключей в файл KeyStore, экспортировать сертификаты и импортировать сертификаты в KeyStore и некоторые другие функции. Keytool поставляется с установкой Java. Keytool более подробно описан далее в отдельной главе по Java Keytool.

Дайджест сообщения (MessageDigest)

Когда вы получаете зашифрованные данные от другой стороны, можете ли вы быть уверенными что никто не изменил зашифрованные данные по пути к вам?

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

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

Java MessageDigest (java.security. MessageDigest) можно использовать для вычисления дайджестов сообщений. Для создания экземпляра MessageDigest вызывается метод MessageDigest.getInstance(). Существует несколько различных алгоритмов дайджеста сообщений. Вам нужно указать, какой алгоритм вы хотите использовать при создании экземпляра MessageDigest. Работа с MessageDigest будет более подробно описана в главе посвященной Java MessageDigest.

Краткое введение в класс MessageDigest

MessageDigest messageDigest = MessageDigest.getInstance(«SHA-256»);

В этом примере создается экземпляр MessageDigest, который использует внутренний алгоритм криптографического хэширования SHA-256 для вычисления дайджестов сообщений.

Чтобы вычислить дайджест сообщения некоторых данных, вы вызываете метод update() или digest(). Метод update() может вызываться несколько раз, а дайджест сообщения обновляется внутри объекта. Когда вы передали все данные, которые вы хотите включить в дайджест сообщения, вы вызываете digest() и извлекаете итоговые данные дайджеста сообщения.

Пример вызова update() несколько раз с последующим вызовом digest():

Вы также можете вызвать digest() один раз, передав все данные, чтобы вычислить дайджест сообщения. Пример:

Код аутентификации сообщения (MAC)

Класс Java Mac используется для создания MAC(Message Authentication Code) из сообщения. M AC похож на дайджест сообщения, но использует дополнительный ключ для шифрования дайджеста сообщения. Только имея как исходные данные, так и ключ, вы можете проверить MAC. Таким образом, MAC является более безопасным способом защиты блока данных от модификации, чем дайджест сообщения. Класс Mac более подробно описан в главе по Java Mac, ниже приведено краткое введение.

Экземпляр Java Mac создается вызовом метода Mac.getInstance(), передавая в качестве параметра имя используемого алгоритма. Вот как это выглядит:

Mac mac = Mac.getInstance(«HmacSHA256»);

Прежде чем создать MAC из данных, вы должны инициализировать экземпляр Mac ключом. Вот пример инициализации экземпляра Mac ключом:

После инициализации экземпляра Mac вы можете вычислить MAC из данных, вызвав методы update() и doFinal(). Если у вас есть все данные для расчета MAC, вы можете сразу вызвать метод doFinal(). Вот как это выглядит:

Подпись (Signature)

Класс Signature (java.security. Signature) используется для цифровой подписи данных. Когда данные подписаны, цифровая подпись создается из этих данных. Таким образом, подпись отделена от данных.

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

Signature signature = Signature.getInstance(«SHA256WithDSA»);

Подпись данных

После инициализации экземпляра подписи, его можно использовать для подписи данных. Это делается вызовом метода update(), передавая данные для подписи в качестве параметра. Можно вызывать метод update() несколько раз, чтобы дополнить данные для создании подписи. После передачи всех данных в метод update(), вызывается метод sign() для получения цифровой подписи. Вот как это выглядит:

Проверка подписи

Signature signature = Signature.getInstance(«SHA256WithDSA»);
signature.initVerify(keyPair.getPublic());

После инициализации в режиме проверки в метод update() передаются данные, которые подписаны подписью. Вызов метода verify(), возвращает значение true или false в зависимости от того, можно ли проверить подпись или нет. Вот как выглядит проверка подписи:

Полный пример подписи и проверки

Генерация секретного ключа 3DES требует нескольких шагов. Во-первых, нам нужно сгенерировать секретный ключ, который будет использоваться для процесса шифрования-дешифрования. В нашем случае мы будем использовать 24-байтовый ключ, составленный из случайных чисел и букв:

Обратите внимание, что секретный ключ не должен быть общедоступным .

Теперь мы завернем наш ключ в SecretKeySpec , объединив его с выбранным алгоритмом:

В нашем случае мы используем TripleDES , который является одним из стандартных алгоритмов безопасности Java .

Еще один элемент, который мы должны сгенерировать заранее, — это вектор инициализации для нашего ключа . Мы будем использовать 8-байтовый массив случайных чисел и букв:

А затем мы обернем его в класс IvParameterSpec :

Java Cryptography

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

В этой статье объясняются основы того, как пользоваться Java Cryptography API для выполнения различных задач в которых требуется безопасное шифрование.

Читайте также:  Адрес ФСС Новый Уренгой и ФСС Новый Уренгой

В этой статье не объясняются основы криптографической теории. Вам придется посмотреть эту информацию где-нибудь еще.

Шифрование и дешифрование​

Чтобы реализовать шифрование входной строки, нам сначала нужно сгенерировать секретный ключ и IV в соответствии с предыдущим разделом. На следующем этапе мы создаем экземпляр класса Cipher с помощью метода getInstance() .

Кроме того, мы настраиваем экземпляр шифра, используя метод init() с секретным ключом, IV и режимом шифрования. Наконец, мы шифруем входную строку, вызывая метод doFinal() . Этот метод получает байты ввода и возвращает зашифрованный текст в байтах:

Для расшифровки входной строки мы можем инициализировать наш шифр, используя DECRYPT_MODE для расшифровки содержимого:

Давайте напишем тестовый метод для шифрования и дешифрования строкового ввода:

Файл​

Теперь давайте зашифруем файл с помощью алгоритма AES. Шаги те же, но нам нужны некоторые классы ввода -вывода для работы с файлами. Давайте зашифруем текстовый файл:

Обратите внимание, что не рекомендуется пытаться прочитать весь файл, особенно если он большой, в память. Вместо этого мы шифруем буфер за раз.

Для расшифровки файла мы используем аналогичные шаги и инициализируем наш шифр, используя DECRYPT_MODE , как мы видели ранее.

Опять же, давайте определим тестовый метод для шифрования и дешифрования текстового файла. В этом методе мы читаем файл foreach.txt из каталога тестовых ресурсов, шифруем его в файл с именем foreach.encrypted , а затем расшифровываем файл в новый файл:

На основе пароля​

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

Для генерации секретного ключа мы используем метод getKeyFromPassword() . Шаги шифрования и дешифрования такие же, как показано в разделе ввода строки. Затем мы можем использовать созданный шифр и предоставленный секретный ключ для выполнения шифрования.

Напишем тестовый метод:

Объект​

Для шифрования объекта Java нам нужно использовать класс SealedObject . Объект должен быть Serializable . Начнем с определения класса Student :

// standard setters and getters

Далее давайте зашифруем объект Student :

Зашифрованный объект впоследствии можно расшифровать с помощью правильного шифра:

Теперь давайте напишем тестовый пример:

Обзор​

Блочный шифр с симметричным ключом играет важную роль в шифровании данных. Это означает, что один и тот же ключ используется как для шифрования, так и для дешифрования. Advanced Encryption Standard ( AES) — это широко используемый алгоритм шифрования с симметричным ключом.

В этом руководстве мы узнаем, как реализовать шифрование и дешифрование AES с использованием архитектуры криптографии Java (JCA) в JDK.

Параметры АЭС​

В алгоритме AES нам нужны три параметра: входные данные, секретный ключ и IV. IV не используется в режиме ECB.

Входные данные​

Входными данными для AES могут быть строки, файлы, объекты и пароли.

Секретный ключ​

Существует два способа генерации секретного ключа в AES: генерация из случайного числа или получение из заданного пароля.

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

Для генерации секретного ключа мы можем использовать класс KeyGenerator . Определим метод генерации ключа AES размером n (128, 192 и 256) бит:

При втором подходе секретный ключ AES может быть получен из заданного пароля с помощью функции получения ключа на основе пароля, такой как PBKDF2 . Нам также нужно солт-значение для превращения пароля в секретный ключ. Соль также является случайным значением.

Мы можем использовать класс SecretKeyFactory с алгоритмом PBKDF2WithHmacSHA256 для генерации ключа из заданного пароля.

Давайте определим метод генерации ключа AES из заданного пароля с 65 536 итерациями и длиной ключа 256 бит:

Вектор инициализации (IV)​

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

Давайте определим метод для генерации IV:

Алгоритм AES​

Алгоритм AES — это итеративный блочный шифр с симметричным ключом, который поддерживает криптографические ключи (секретные ключи) длиной 128, 192 и 256 бит для шифрования и дешифрования данных блоками по 128 бит . На рисунке ниже показан высокоуровневый алгоритм AES:

Если данные, которые нужно зашифровать, не соответствуют требованиям к размеру блока в 128 бит, они должны быть дополнены. Заполнение — это процесс заполнения последнего блока до 128 бит.

Java Cipher (Шифр)

Класс Java Cipher (javax.crypto. Cipher) представляет собой алгоритм шифрования. Термин «Шифр» является стандартным термином для алгоритма шифрования в мире криптографии. Вот почему класс Java называется Шифр(Cipher), а не Шифратор / Дешифратор или как-то еще. Вы можете использовать экземпляр Cipher для шифрования и расшифровки данных в Java. В этой главе объясняется, как работает класс Cipher.

Создание шифра

Прежде чем использовать шифр, необходимо создать экземпляр класса Cipher, вызывая его метод getInstance() с параметром, указывающим, какой тип алгоритма шифрования вы хотите использовать. Вот пример создания экземпляра Java Cipher:

Cipher cipher = Cipher.getInstance(«AES»);

В этом примере создается экземпляр Cipher с использованием алгоритма шифрования AES.

Режимы шифрования

Некоторые алгоритмы шифрования могут работать в разных режимах. Режим шифрования определяет детали того, как будут зашифрованы данные. Таким образом, режим шифрования частично влияет на алгоритм шифрования. Режимы шифрования могут иногда использоваться в нескольких различных алгоритмах шифрования — как метод, который добавляется к основному алгоритму шифрования. Вот почему режимы рассматриваются отдельно от самих алгоритмов шифрования, а скорее как «дополнения» к алгоритмам шифрования. Вот некоторые из наиболее известных режимов шифрования:

Поскольку режим сцепления блоков шифрования также требует «схемы дополнения», схема дополнения (PKCS5Padding) добавляется в конец строки имени алгоритма шифрования.
Важно знать, что не все алгоритмы и режимы шифрования поддерживаются поставщиком шифрования Java SDK по умолчанию. Для создания нужного вам экземпляра шифра с требуемым режимом и схемой заполнения может понадобиться установить сторонний провайдер, такой как Bouncy Castle.

Прежде чем использовать экземпляр Cipher, его необходимо инициализировать. Инициализация шифра выполняется путем вызова его метода init(). Метод init() принимает два параметра:

Пример инициализации экземпляра Cipher в режиме шифрования:

А вот пример инициализации экземпляра Cipher уже в режиме расшифровки:

Шифрование и расшифровка данных

Чтобы зашифровать или расшифровать данные с помощью экземпляра Cipher, вызывается один из этих двух методов:

Существует несколько переопределенных версий методов update() и doFinal(), которые принимают разные параметры. Рассмотрим наиболее часто используемые здесь. Если вам необходимо зашифровать или расшифровать один блок данных, просто вызовите doFinal() с данными для шифрования или расшифровки. Пример:

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

Если вам нужно зашифровать или расшифровать большой файл разбитый на несколько блоков, вызывается update() один раз для каждого блока данных и завершается вызовом метода doFinal() с последним блоком данных. Вот пример шифрования нескольких блоков данных:

Причина, по которой вызов doFinal() необходим для последнего блока данных, заключается в том, что некоторые алгоритмы шифрования должны дополнять данные, чтобы соответствовать определенному размеру блока шифра (например, 8-байтовой границе). Дополнять же промежуточные зашифрованные данные нет необходимости. Следовательно, вызывается метод update() для промежуточных блоков данных и вызов doFinal() для последнего блока данных.
При расшифровке нескольких блоков данных вы также вызываете метод update() для промежуточных блоков данных и метод doFinal() для последнего блока. Пример расшифровки нескольких блоков данных:

Опять же, экземпляр шифра должен быть инициализирован в режиме дешифровки, чтобы этот пример работал.

Шифрование / Расшифровка части байтового массива

Методы шифрования и расшифровки класса Cipher могут шифровать или расшифровывать часть данных, хранящихся в байтовом массиве. Методу update() и/или doFinal() нужно передать смещение и длину.

В данном примере будут зашифрованы (или расшифрованы, в зависимости от инициализации шифра) байты с 10 индекса и на 24 байта вперед.

Читайте также:  КЛАДР скачать бесплатно 2021, кладр для 1с 8.3 - программа

Шифрование / Расшифровка в существующий байтовый массив

Все приведенные в этой главе примеры шифрования и дешифрования возвращают зашифрованные или дешифрованные данные в новом байтовом массиве. Однако также возможно зашифровать или расшифровать данные в существующий байтовый массив. Это может быть полезно для уменьшения количества созданных байтовых массивов. Для этого необходимо передать целевой массив байтов в качестве параметра методу update() и/или doFinal().

В этом примере данные шифруются с 10 индекса на 24 байта вперед в байтовый массив dest со смещением 0. Если вы хотите установить другое смещение для байтового массива dest, существуют версии update() и doFinal(), которые принимают дополнительный параметр смещения. Пример вызова метода doFinal() со смещением в массиве dest:

Повторное использование экземпляра шифра

Инициализация экземпляра Cipher — дорогостоящая операция и хорошей идеей будет повторное использование экземпляров Cipher. К счастью, класс Cipher был разработан с учетом возможности повторного использования. Когда вы вызываете метод doFinal() для экземпляра Cipher, он возвращается в состояние, в котором находился сразу после инициализации. Экземпляр Cipher может затем использоваться для шифрования или дешифрования большего количества данных.

Пример повторного использования экземпляра Java Cipher:

Сначала создается и инициализируется экземпляр Cipher, а затем используется для шифрования двух блоков согласованных данных. Обратите внимание на вызов update(), а затем doFinal() для этих двух блоков данных. После этого экземпляр Cipher может быть снова использован для шифрования данных. Это делается с помощью вызова doFinal() с третьим блоком данных. После этого вызова doFinal() вы можете зашифровать еще один блок данных с тем же экземпляром Java Cipher.

Оглавление

Теперь мы готовы зашифровать простые значения String . Давайте сначала определим строку , с которой мы будем работать:

«ForEach secret message»

Далее нам понадобится объект Cipher , инициализированный режимом шифрования, секретным ключом и вектором инициализации , который мы сгенерировали ранее:

Обратите внимание, что мы используем алгоритм TripleDES с CBC и схемой заполнения PKCS#5 .

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

Теперь наше сообщение успешно зашифровано. Если мы хотим сохранить его в базе данных или отправить через REST API , было бы удобнее закодировать его с помощью алфавита Base64 :

Кодировка Base64 делает сообщение более читабельным и удобным для работы.

Варианты AES​

Алгоритм AES имеет шесть режимов работы:

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

ЕЦБ​

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

Си-Би-Си​

Чтобы преодолеть слабость ECB, режим CBC использует вектор инициализации (IV) для усиления шифрования. Во-первых, CBC использует блок открытого текста xor с IV. Затем он шифрует результат в блок зашифрованного текста. В следующем блоке он использует результат шифрования для операции xor с блоком открытого текста до последнего блока.

В этом режиме шифрование нельзя распараллелить, но можно распараллелить расшифровку. Он также требует заполнения данных.

CFB​

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

В этом режиме можно распараллелить расшифровку, но нельзя распараллелить шифрование.

ОФБ​

Этот режим можно использовать как потоковый шифр. Во-первых, он шифрует IV. Затем он использует результаты шифрования для удаления открытого текста, чтобы получить зашифрованный текст.

Он не требует заполнения данных и не будет затронут зашумленным блоком.

CTR​

Этот режим использует значение счетчика в качестве IV. Он очень похож на OFB, но каждый раз использует счетчик для шифрования вместо IV.

Этот режим имеет две сильные стороны, в том числе распараллеливание шифрования/дешифрования, и шум в одном блоке не влияет на другие блоки.

ГКМ​

Этот режим является расширением режима CTR. GCM привлек значительное внимание и рекомендован NIST. Модель GCM выводит зашифрованный текст и тег аутентификации. Основным преимуществом этого режима по сравнению с другими режимами работы алгоритма является его эффективность.

В этом руководстве мы будем использовать алгоритм AES/CBC/PKCS5Padding , так как он широко используется во многих проектах.

Размер данных после шифрования​

Как упоминалось ранее, AES имеет размер блока 128 бит или 16 байт. AES не изменяет размер, а размер зашифрованного текста равен размеру открытого текста. Кроме того, в режимах ECB и CBC мы должны использовать алгоритм заполнения, такой как PKCS 5. Таким образом, размер данных после шифрования составляет:

ciphertext_size (bytes) = cleartext_size + (16 — (cleartext_size % 16))

Для хранения IV с зашифрованным текстом нам нужно добавить еще 16 байт.

Расшифровка строк​

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

Далее мы запустим метод doFinal :

Теперь мы декодируем результат в строковую переменную:

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

Мы инициализируем шифр в конструкторе нашего класса FileEncrypterDecrypter , используя указанную строку преобразования.

Это позволяет нам ошибиться раньше, если было указано неправильное преобразование:

Затем мы можем использовать созданный шифр и предоставленный секретный ключ для выполнения шифрования:

Java позволяет нам использовать удобный класс CipherOutputStream для записи зашифрованного содержимого в другой OutputStream .

Обратите внимание, что мы записываем IV ( вектор инициализации ) в начало выходного файла. В этом примере IV автоматически генерируется при инициализации Cipher .

Использование IV обязательно при использовании режима CBC, чтобы рандомизировать зашифрованный вывод. Однако IV не считается секретом, поэтому можно написать его в начале файла.

В этом руководстве мы рассмотрим, как зашифровать и расшифровать файл с помощью существующих API-интерфейсов JDK.

Резюме​

В этой статье мы узнали, как создать ключ 3DES в Java и как использовать его для шифрования и дешифрования строк и файлов.

Как всегда, весь исходный код доступен на GitHub .

Работа с файлами​

Мы также можем зашифровать целые файлы. В качестве примера создадим временный файл с текстовым содержимым:

«Secret ForEach message»

Далее преобразуем его содержимое в однобайтовый массив:

Теперь мы можем использовать шифровальный шифр так же, как мы это делали со строкой :

Наконец, давайте перезапишем содержимое файла новыми зашифрованными данными:

Процесс расшифровки очень похож. Единственное отличие состоит в том, что шифр инициализируется в режиме расшифровки:

Еще раз перезапишем содержимое файла — на этот раз расшифрованными данными:

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

Вывод​

Мы видели, что можем выполнять базовое шифрование и дешифрование, используя стандартные классы JDK, такие как Cipher , CipherOutputStream и CipherInputStream .

Как обычно, полный код этой статьи доступен в нашем репозитории GitHub .

Кроме того, вы можете найти список шифров, доступных в JDK , здесь .

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

Заключение​

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

Как всегда, полный исходный код статьи доступен на GitHub .

Оцените статью
ФСС Help
Добавить комментарий