Совместное использование password
(a char[]
) и salt
(a byte[]
- 8 байтов, выбранных SecureRandom
, дает хорошую соль - которую не нужно хранить в секрете) с получатель внеполосный. Затем, чтобы получить хороший ключ из этой информации:
/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Магические числа (которые могут быть определены где-то как константы) 65536 и 256 - это число итераций деривации ключей и размер ключа соответственно.
Функция создания ключа повторяется, чтобы потребовать значительных вычислительных усилий, что не позволяет злоумышленникам быстро пробовать множество различных паролей. Количество итераций может быть изменено в зависимости от доступных вычислительных ресурсов.
Размер ключа может быть уменьшен до 128 бит, что все еще считается «сильным» шифрованием, но оно не дает большой запас прочности, если обнаружены атаки, которые ослабляют AES.
При правильном режиме цепочки блоков один и тот же производный ключ может использоваться для шифрования многих сообщений. В Cipher Block Chaining (CBC) случайный вектор инициализации (IV) генерируется для каждого сообщения, давая различный зашифрованный текст, даже если обычный текст идентичен. CBC может быть не самым безопасным режимом, доступным для вас (см. AEAD ниже); Есть много других режимов с различными свойствами безопасности, но все они используют одинаковый случайный ввод. В любом случае выходные данные каждой операции шифрования представляют собой зашифрованный текст и вектор инициализации:
/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal("Hello, World!".getBytes("UTF-8"));
Храните ciphertext
и iv
. При расшифровке SecretKey
регенерируется точно таким же образом, используя пароль с теми же параметрами соли и итерации. Инициализируйте шифр этим ключом и вектором инициализации, сохраненным с сообщением:
/* Decrypt the message, given derived key and initialization vector. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
String plaintext = new String(cipher.doFinal(ciphertext), "UTF-8");
System.out.println(plaintext);
Java 7 включает API поддержка режимов шифрования AEAD , и поставщик "SunJCE", включенный в дистрибутивы OpenJDK и Oracle, реализует их начиная с Java 8. Один из этих режимов настоятельно рекомендуется вместо CBC; это защитит целостность данных, а также их конфиденциальность.
A java.security.InvalidKeyException
с сообщением «Недопустимый размер ключа или параметры по умолчанию» означает, что уровень криптографии ограничен ; файлы политики неограниченной юрисдикции находятся не в правильном месте. В JDK они должны быть размещены под ${jdk}/jre/lib/security
Судя по описанию проблемы, файлы политики установлены неправильно. Системы могут легко иметь несколько сред выполнения Java; перепроверьте, чтобы убедиться, что используется правильное местоположение.