Проблема шифрования и дешифрования с шифром на java

#java #encryption #cryptography

Вопрос:

Привет, я столкнулся с проблемой расшифровки. Расшифрованное значение не совпадает с исходным.

Вот моя логика для encryption :

 public byte[] encrypt(String plainText) {

    byte iv[] = new byte[ENCRYPTION_PARAM_SIZE];
    SecureRandom secRandom = new SecureRandom();
    secRandom.nextBytes(iv);
    
    Cipher cipher = Cipher.getInstance(ENCRYPTION_INSTANCE);
    SecretKeySpec key = new SecretKeySpec(fixSecret(encryptionKey), ENCRYPTION_ALGORITHM);
    cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
    return cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
}
 

И это моя логика для Decryption

 public String decrypt(byte[] cipherText) {

    byte iv[] = new byte[ENCRYPTION_PARAM_SIZE];
    SecureRandom secRandom = new SecureRandom();
    secRandom.nextBytes(iv);
    
    Cipher cipher = Cipher.getInstance(ENCRYPTION_INSTANCE);
    SecretKeySpec key = new SecretKeySpec(fixSecret(encryptionKey), ENCRYPTION_ALGORITHM);
    cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
    return new String(cipher.doFinal(cipherText), StandardCharsets.UTF_8);

}
 

Параметры шифрования :

 ENCRYPTION_ALGORITHM = "DESede";
ENCRYPTION_INSTANCE = "DESede/CBC/PKCS5Padding";
Integer ENCRYPTION_PARAM_SIZE = 8;
 

Вот как я пытаюсь проверить :

 public static void main(String[] args){    
    Long value = 9123456L;
    String strval = value.toString();
    byte[] encryptedVal = encrypt(strval);
    String decryptedVal = decrypt(encryptedVal);
    
    System.out.println("Original  value : "  strval);
    System.out.println("Encrypted value : "  encryptedVal.toString());
    System.out.println("Decrypted value : "  decryptedVal);
    System.out.println("Final     value : "  Long.parseLong(decryptedVal));
}
 

Что мне нужно сделать здесь, чтобы это сработало.

Примечание : Приведенный выше код работает нормально, если я использую приведенную ниже логику без SecureRandom :

 cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[ENCRYPTION_PARAM_SIZE]));
 

Комментарии:

1. fixSecret(encryptionKey) Что здесь происходит? Вы не опубликовали это

2. Вы используете разные капельницы для шифрования и дешифрования. Чтобы расшифровка работала, IV, применяемый для шифрования, также должен использоваться для расшифровки. Обычно IV (который не является секретным) отправляется вместе с зашифрованным текстом на сторону расшифровки (обычно объединенную).

3. Также обратите внимание, что ваш обходной путь не является реальным решением, поскольку статический IV (например, нулевой IV) небезопасен.

4. @Topaco : Как я могу реализовать динамический IV здесь и заставить работать шифрование и дешифрование .. что и является моим главным намерением.

5. IV не должен быть секретным, поэтому вы можете создать новый, используя SecureRandom для шифрования, затем зашифровать данные, затем добавить IV к зашифрованным данным и отправить эту объединенную дату iv зашифрованную одноранговому узлу. Затем одноранговый узел снимает капельницу и использует ее для расшифровки.

Ответ №1:

Как уже говорили другие, ваша проблема заключается в том, что капельница, которую вы используете для шифрования, отличается от той, которую вы используете для расшифровки. IV не является чувствительным (с точки зрения конфиденциальности), поэтому его можно передавать вместе с зашифрованным текстом. Некоторые приложения добавляют IV к зашифрованному тексту, в то время как другие используют более стандартные форматы, такие как зашифрованные данные CMS. Поскольку вы используете Java, вы можете создавать зашифрованные структуры данных CMS, используя класс CMSEncryptedDataGenerator от Bouncy Castle.

Есть еще две проблемы с вашим кодом:

  1. Выбор криптографических примитивов
  2. Управление ключами

Криптографические примитивы

Криптографическое преобразование, которое вы используете, называется «DESede/CBC/PKCS5Padding». Если у вас нет веских причин использовать DES/3DES, вам следует подумать о переходе на AES. Кроме того, я бы рекомендовал использовать AEAD, такой как AES-GCM или AES-GCM-SIV.

Управление Ключами

Вся криптография в мире мало что значит, если ключами управляют неправильно. Из вашего кода похоже, что вы создаете объект SecretKeySpec из байтов ключа. Если вы можете, попробуйте использовать аппаратный модуль безопасности (HSM) или систему управления ключами (KMS), в которой ключ никогда не экспортируется из границ несанкционированного доступа устройства в текстовом формате. AWS KMS, хранилище ключей Azure и Google KMS предлагают для этого очень доступные цены. Конечно, есть и другие варианты.

Ответ №2:

В функции расшифровки вы генерируете случайный вектор инициализации (IV), так что это никогда не сработает. Вам нужно сохранить IV из функции шифрования и предоставить его в качестве входных данных для функции дешифрования.

Вот пример:

 public byte[] encryptAndDecrypt(String plainText) {

  byte iv[] = new byte[ENCRYPTION_PARAM_SIZE];
  SecureRandom secRandom = new SecureRandom();
  secRandom.nextBytes(iv);

  Cipher cipher = Cipher.getInstance(ENCRYPTION_INSTANCE);
  SecretKeySpec key = new SecretKeySpec(fixSecret(encryptionKey), ENCRYPTION_ALGORITHM);
  cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
  byte[] cipherText=cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
  return decrypt(cipherText, iv)
}


public String decrypt(byte[] cipherText, byte[] iv) {

 Cipher cipher = Cipher.getInstance(ENCRYPTION_INSTANCE);
 SecretKeySpec key = new SecretKeySpec(fixSecret(encryptionKey), ENCRYPTION_ALGORITHM);
 cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
 return cipher.doFinal(cipherText);
}
 

Обратите внимание, что по определению IV должен быть случайным, но не должен рассматриваться как секрет, поэтому вы можете хранить его как простые данные без какой-либо защиты.
Идея IV состоит в том, чтобы рандомизировать зашифрованный текст, поэтому, если вы не используете IV или используете константу IV и шифруете «X», зашифрованный текст — «Y», вы можете легко преобразовать зашифрованный текст в обычный текст, в то время как при случайном IV зашифрованный текст каждый раз отличается.