В этом ответе я предпочитаю подходить к основной теме «Простой пример шифрования / дешифрования Java AES», а не к конкретному вопросу отладки, потому что я думаю, что это принесет пользу большинству читателей.
Это краткое изложение моего сообщения в блоге о шифровании AES в Java, поэтому я рекомендую прочитать его, прежде чем что-либо реализовывать. Тем не менее, я все же приведу простой пример для использования и дам несколько указателей, на что следует обращать внимание.
В этом примере я выберу использовать аутентификацию шифрование с Галуа Mode / счетчика или GCM режимом. Причина в том, что в большинстве случаев вам нужна целостность и аутентичность в сочетании с конфиденциальностью (подробнее читайте в блоге ).
Руководство по шифрованию / дешифрованию AES-GCM
Вот шаги, необходимые для шифрования / дешифрования с помощью AES-GCM с архитектурой криптографии Java (JCA) . Не смешивайте с другими примерами , так как тонкие различия могут сделать ваш код совершенно небезопасным.
1. Создать ключ
Поскольку это зависит от вашего варианта использования, я предполагаю самый простой случай: случайный секретный ключ.
SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[16];
secureRandom.nextBytes(key);
SecretKey secretKey = SecretKeySpec(key, "AES");
Важный:
2. Создайте вектор инициализации.
Вектор инициализации (IV) используется для того , что тот же самый секретный ключ будет создавать различные тексты шифров .
byte[] iv = new byte[12]; //NEVER REUSE THIS IV WITH SAME KEY
secureRandom.nextBytes(iv);
Важный:
3. Зашифровать с помощью IV и ключа
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); //128 bit auth tag length
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
byte[] cipherText = cipher.doFinal(plainText);
Важный:
- использовать тег аутентификации 16 байт / 128 бит (используется для проверки целостности / аутентичности)
- тег аутентификации будет автоматически добавлен к зашифрованному тексту (в реализации JCA)
- Поскольку GCM ведет себя как потоковый шифр, заполнение не требуется
- использовать
CipherInputStream
при шифровании больших блоков данных
- хотите проверить дополнительные (несекретные) данные, если они были изменены? Вы можете использовать соответствующие данные с
cipher.updateAAD(associatedData);
Подробнее здесь.
3. Сериализовать в одно сообщение
Просто добавьте IV и зашифрованный текст. Как было сказано выше, IV не обязательно должен быть секретным.
ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + cipherText.length);
byteBuffer.put(iv);
byteBuffer.put(cipherText);
byte[] cipherMessage = byteBuffer.array();
При необходимости закодируйте с помощью Base64, если вам нужно строковое представление. Используйте встроенную реализацию Android или Java 8 (не используйте кодек Apache Commons - это ужасная реализация). Кодирование используется для «преобразования» байтовых массивов в строковое представление, чтобы сделать его безопасным ASCII, например:
String base64CipherMessage = Base64.getEncoder().encodeToString(cipherMessage);
4. Подготовка к расшифровке: десериализация
Если вы закодировали сообщение, сначала декодируйте его в массив байтов:
byte[] cipherMessage = Base64.getDecoder().decode(base64CipherMessage)
Важный:
5. Расшифровать
Инициализируйте шифр и установите те же параметры, что и при шифровании:
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
//use first 12 bytes for iv
AlgorithmParameterSpec gcmIv = new GCMParameterSpec(128, cipherMessage, 0, 12);
cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmIv);
//use everything from 12 bytes on as ciphertext
byte[] plainText = cipher.doFinal(cipherMessage, 12, cipherMessage.length - 12);
Важный:
- не забудьте добавить соответствующие данные с ,
cipher.updateAAD(associatedData);
если вы добавили его во время шифрования.
В этом суть можно найти рабочий фрагмент кода.
Обратите внимание, что самые последние реализации Android (SDK 21+) и Java (7+) должны иметь AES-GCM. В более старых версиях он может отсутствовать. Я по-прежнему выбираю этот режим, поскольку его проще реализовать, и он более эффективен по сравнению с аналогичным режимом Encrypt-then-Mac (например, с AES-CBC + HMAC ). См. Эту статью о том, как реализовать AES-CBC с HMAC .