Недопустимая длина ключа AES: 20 байт (Java 11)

Я пытаюсь сгенерировать ключ с помощью 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);

Есть ли универсальное решение для этого? Или это просто мое непонимание?

Любая помощь будет оценена по достоинству.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваш ключ имеет длину 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));
...

Две другие проблемы:

  • Вы используете соль размером 256 байт, но при объединении сохраняете только 16 байтов. Чтобы не противоречить конкатенации, примените 16-байтовую соль: byte[] salt = new byte[16].
  • Вывод finalCiphertext.toString() возвращает только класс и шестнадцатеричный хэш-код объекта, s. здесь. Для значимого вывода вместо этого используйте Base64 или шестнадцатеричное кодирование byte[], например. Base64.getEncoder().encodeToString(finalCiphertext).

Запустите оба подхода онлайн jdoodle.com/ia/z5J

Topaco 10.11.2022 12:40

Удивительно. Также очень полезно с объяснением. Спасибо!

hell_storm2004 10.11.2022 14:27

Другие вопросы по теме