Нет проверки целостности, по этим конкретным причинам
- Необходимость не очевидна из варианта использования.
"AES/GCM/NoPadding"
Режим доступен только изJava 7 и выше - От пользователя зависит, хотят ли они развернуть, например, HMAC и / или AESCMAC (рекомендуется).
- Для этого потребуется как минимум дополнительный ключ и два полных прохода.
Если у вас есть реализация режима GCM с обеих сторон - например, с помощью Bouncy Castle на Java 6 - пожалуйста, сделайте это, так как он намного более безопасен (если «IV» действительноуникальный).Изменение реализации должно быть действительно простым.
Замечания по реализации, касающиеся шифрования
- Эта реализация небезопасна при использовании в неограниченной роли клиент / сервер, посколькуатак оракулового отступа (в среднем требуется 128 попыток на байт или меньше, независимо от алгоритма или размера ключа).Вам нужно будет использовать MAC, HMAC или Signature поверх зашифрованных данных и проверить их перед расшифровкой, чтобы развернуть их в режиме клиент / сервер.
- Decrypt вернет ноль, если расшифровка не удалась.Это может указывать только на исключение заполнения, которое должно быть надлежащим образом обработано (предупреждал ли я о нападениях оракула заполнения)
- Недопустимые ключи будут возвращены как
InvalidArgumentException
. - Все другие исключения, связанные с безопасностью"подметаются под таблицей", поскольку это означает, что среда выполнения Java недопустима.Например, поддержка
"UTF-8"
и "AES/CBC/PKCS5Padding"
требуется для каждой реализации Java SE.
Некоторые другие примечания
- Пожалуйста, не пытайтесь делать обратное и вставляйте байты непосредственно во входную строку метода шифрования (например, используя
new String(byte[])
).Метод может молча завершиться сбоем! - Оптимизирован для удобства чтения.Если вам больше нравятся скорость и лучший объем памяти, перейдите к реализации потока Base64 и
CipherStream
. - Для запуска этого кода требуется по крайней мере Java 6 SE или совместимый.
- Шифрование / дешифрование может завершиться неудачей для ключей AES размером более 128 бит, так как вам может нужны файлы политик для неограниченного шифрования (доступно в Oracle)
- Остерегайтесь правительственных правил при экспорте шифрования.
- Эта реализация использует шестнадцатеричные ключи вместо ключей base64, так как они достаточно малы, а шестнадцатеричный код легче редактировать / проверять вручную.
- Используется кодирование / декодирование шестнадцатеричного и base64, извлеченного из JDK, без внешних библиотекнужно что бы то ни было.
- Uber прост в использовании, но, конечно, не очень объектно-ориентирован, нет кэширования экземпляров объектов, используемых при шифровании / дешифровании.Рефакторинг по желанию.
ОК, вот код ...
public static String encrypt(final String plainMessage,
final String symKeyHex) {
final byte[] symKeyData = DatatypeConverter.parseHexBinary(symKeyHex);
final byte[] encodedMessage = plainMessage.getBytes(Charset
.forName("UTF-8"));
try {
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final int blockSize = cipher.getBlockSize();
// create the key
final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES");
// generate random IV using block size (possibly create a method for
// this)
final byte[] ivData = new byte[blockSize];
final SecureRandom rnd = SecureRandom.getInstance("SHA1PRNG");
rnd.nextBytes(ivData);
final IvParameterSpec iv = new IvParameterSpec(ivData);
cipher.init(Cipher.ENCRYPT_MODE, symKey, iv);
final byte[] encryptedMessage = cipher.doFinal(encodedMessage);
// concatenate IV and encrypted message
final byte[] ivAndEncryptedMessage = new byte[ivData.length
+ encryptedMessage.length];
System.arraycopy(ivData, 0, ivAndEncryptedMessage, 0, blockSize);
System.arraycopy(encryptedMessage, 0, ivAndEncryptedMessage,
blockSize, encryptedMessage.length);
final String ivAndEncryptedMessageBase64 = DatatypeConverter
.printBase64Binary(ivAndEncryptedMessage);
return ivAndEncryptedMessageBase64;
} catch (InvalidKeyException e) {
throw new IllegalArgumentException(
"key argument does not contain a valid AES key");
} catch (GeneralSecurityException e) {
throw new IllegalStateException(
"Unexpected exception during encryption", e);
}
}
public static String decrypt(final String ivAndEncryptedMessageBase64,
final String symKeyHex) {
final byte[] symKeyData = DatatypeConverter.parseHexBinary(symKeyHex);
final byte[] ivAndEncryptedMessage = DatatypeConverter
.parseBase64Binary(ivAndEncryptedMessageBase64);
try {
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final int blockSize = cipher.getBlockSize();
// create the key
final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES");
// retrieve random IV from start of the received message
final byte[] ivData = new byte[blockSize];
System.arraycopy(ivAndEncryptedMessage, 0, ivData, 0, blockSize);
final IvParameterSpec iv = new IvParameterSpec(ivData);
// retrieve the encrypted message itself
final byte[] encryptedMessage = new byte[ivAndEncryptedMessage.length
- blockSize];
System.arraycopy(ivAndEncryptedMessage, blockSize,
encryptedMessage, 0, encryptedMessage.length);
cipher.init(Cipher.DECRYPT_MODE, symKey, iv);
final byte[] encodedMessage = cipher.doFinal(encryptedMessage);
// concatenate IV and encrypted message
final String message = new String(encodedMessage,
Charset.forName("UTF-8"));
return message;
} catch (InvalidKeyException e) {
throw new IllegalArgumentException(
"key argument does not contain a valid AES key");
} catch (BadPaddingException e) {
// you'd better know about padding oracle attacks
return null;
} catch (GeneralSecurityException e) {
throw new IllegalStateException(
"Unexpected exception during decryption", e);
}
}
Использование:
String plain = "Zaphod's just zis guy, ya knöw?";
String encrypted = encrypt(plain, "000102030405060708090A0B0C0D0E0F");
System.out.println(encrypted);
String decrypted = decrypt(encrypted, "000102030405060708090A0B0C0D0E0F");
if (decrypted != null && decrypted.equals(plain)) {
System.out.println("Hey! " + decrypted);
} else {
System.out.println("Bummer!");
}