Я пытаюсь сгенерировать ключ с помощью Java. Честно говоря, я не очень разбираюсь в ключах, паролях, шифрах и шифровании.
И из того, что я искал на этом сайте, я вижу, что это очень распространенная проблема. Я немного почитал и придумал этот код, который я написал:
Security.setProperty("crypto.policy", "unlimited");
String valueToEncode = "some_random_text";
SecureRandom secureRandom = new SecureRandom();
byte[] salt = new byte[256];
secureRandom.nextBytes(salt);
KeySpec keySpec = new PBEKeySpec("some_random_password".toCharArray(), salt, 65536, 256); // AES-256
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBEWITHHMACSHA512ANDAES_256");
byte[] key = secretKeyFactory.generateSecret(keySpec).getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
byte[] ivBytes = new byte[16];
secureRandom.nextBytes(ivBytes);
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encValue = cipher.doFinal(valueToEncode.getBytes(StandardCharsets.UTF_8));
byte[] finalCiphertext = new byte[encValue.length + 2 * 16];
System.arraycopy(ivBytes, 0, finalCiphertext, 0, 16);
System.arraycopy(salt, 0, finalCiphertext, 16, 16);
System.arraycopy(encValue, 0, finalCiphertext, 32, encValue.length);
System.out.println(finalCiphertext.toString());
Это изменено из ответа, который я видел в другом сообщении. Но я все еще получаю ошибку «недопустимая длина».
Ошибка, которую я получаю:
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 20 bytes
at com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:87)
at com.sun.crypto.provider.CipherBlockChaining.init(CipherBlockChaining.java:93)
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:591)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:346)
at javax.crypto.Cipher.implInit(Cipher.java:805)
at javax.crypto.Cipher.chooseProvider(Cipher.java:863)
at javax.crypto.Cipher.init(Cipher.java:1395)
at javax.crypto.Cipher.init(Cipher.java:1326)
at com.att.logicalprovisioning.simulators.Trial.main(Trial.java:47)
Trial.java:47
быть линией: cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
Есть ли универсальное решение для этого? Или это просто мое непонимание?
Любая помощь будет оценена по достоинству.
Ваш ключ имеет длину 20 байт, потому что secretKeyFactory.generateSecret(keySpec).getEncoded()
возвращает пароль some_random_password
.
Простой способ исправить код — использовать вывод ключа PBKDF2WithHmacSHA512
вместо PBEWITHHMACSHA512ANDAES_256
. Это генерирует ключ указанной длины на основе пароля и соли:
...
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
...
Тем не менее, PBEWITHHMACSHA512ANDAES_256
также может быть применен. Этот алгоритм определяет получение ключа с помощью PBKDF2WithHmacSHA512
и последующего шифрования AES. Реализация функционально идентична вашей, но требует еще нескольких изменений в коде:
...
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, 65536, ivParameterSpec);
PBEKeySpec keySpec = new PBEKeySpec("some_random_password".toCharArray());
SecretKeyFactory kf = SecretKeyFactory.getInstance("PBEWITHHMACSHA512ANDAES_256");
SecretKey secretKey = kf.generateSecret(keySpec);
Cipher cipher = Cipher.getInstance("PBEWITHHMACSHA512ANDAES_256");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
byte[] encValue = cipher.doFinal(valueToEncode.getBytes(StandardCharsets.UTF_8));
...
Две другие проблемы:
byte[] salt = new byte[16]
.finalCiphertext.toString()
возвращает только класс и шестнадцатеричный хэш-код объекта, s. здесь. Для значимого вывода вместо этого используйте Base64 или шестнадцатеричное кодирование byte[]
, например. Base64.getEncoder().encodeToString(finalCiphertext)
.Удивительно. Также очень полезно с объяснением. Спасибо!
Запустите оба подхода онлайн jdoodle.com/ia/z5J