Сумасшедшее исключение при расшифровке AES - BadPaddingException

У меня возникли некоторые проблемы при попытке расшифровать зашифрованный текст. CheckpswdBasedKey всегда возвращает false, потому что из-за BadPaddingException в c.doFInal я использую AES, в основном шифрование:

public static String generatePswdBasedKey(String password){
String finalKey = null;
SecretKey sk = null;
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, IT, KEY_LENGTH);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
sk = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance(Cifrador.AES_MODE);//AES_MODE = AES/CBC/PKCS5Padding
IvParameterSpec ivParams = new IvParameterSpec(iv);//IV already initialized
cipher.init(Cipher.ENCRYPT_MODE, sk, ivParams);
byte pwdbytes[] = password.getBytes();//I also tried using Base64 to decode... without success
byte cc[] = cipher.doFinal(pwdbytes);
finalKey = Base64.encodeToString(cc, false);  //.encodeToString(byte[] sArr, boolean lineSep)
return finalKey;

Теперь режим расшифровки:

//This method compares a password received from keyboard with the decrypted password (decrypting output from generatePswdBasedKey(String password))
public static boolean checkPswdBasedKey(String password, String passwordInput){
byte bufferBytes[] = Base64.decode(password);
SecretKey sk = new SecretKeySpec(bufferBytes, 0, bufferBytes.length, "AES"); //Also tried new SecretKeySPec(bufferBytes, "AES");...
Cipher c = Cipher.getInstance(Cifrador.AES_MODE);//AES_MODE = AES/CBC/PKCS5Padding
IvParameterSpec ivParams = new IvParameterSpec(iv);//IV already initialized
c.init(Cipher.DECRYPT_MODE, sk, ivParams);
byte result[] = c.doFinal(bufferBytes);
String resultStr = Base64.encodeToString(result, false); //.encodeToString(byte[] sArr, boolean lineSep)
if(passwordInput.equalsIgnoreCase(resultStr)){
return true;
}
return false;
}

Я сравнил байты из iv @checkPswdBasedKey и iv @generatePswdBasedKey, и они все одинаковые. То же самое происходит с секретным ключом @checkPswdBasedKey (я получаю эти байты с помощью: sk.getEncoded()) и секретным ключом @generatePswdBasedKey... все они равны. Таким образом, в основном, когда я расшифровываю, я знаю, что использую тот же ключ, тот же IV и то же сообщение... и соответствующую длину (16-байтовый ключ, 16-байтовые сообщения, 16-байтовые IV, используя AES 128). Есть идеи?

1 ответ

Решение

Как прокомментировал Йоахим Исакссон, если вы хотите реализовать проверку пароля, вы должны использовать безопасное хеш- представление пароля, которое не является обратимым. Таким образом, пароль не может быть получен путем расшифровки, даже если хэш + ключ скомпрометирован.

Во всяком случае, в вашем generatePswdBasedKey вы используете PBKDF2WithHmacSHA1 алгоритм для генерации SecretKeyи затем используйте этот ключ для шифрования пароля. Теперь у вас есть две возможности подтвердить пароль в checkPswdBasedKey, Или вы:

  • зашифровать пароль так же, как в generatePswdBasedKey и сравните, что они дают одинаковую зашифрованную строку

или ты

  • расшифровать зашифрованную версию и сравнить результат с паролем в открытом виде.

Я полагаю, что вы попробуете более поздний подход, когда начнете шифрование для дешифрования с

c.init(Cipher.DECRYPT_MODE, sk, ivParams);

Однако, для такого подхода к работе вам нужно SecretKey так же, как вы сделали в generatePswdBasedKey - в настоящее время вы получаете два разных ключа.

В generatePswdBasedKey:

SecretKey sk = null;
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, IT, KEY_LENGTH);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
sk = new SecretKeySpec(keyBytes, "AES");

В checkPswdBasedKey:

byte bufferBytes[] = Base64.decode(password);
SecretKey sk = new SecretKeySpec(bufferBytes, 0, bufferBytes.length, "AES");

Когда это исправлено, вам также нужно взглянуть на свою логику сравнения. Вы не должны делать кодирование Base64 вашего результата перед сравнением - и сравнение должно быть чувствительным к регистру. Не используйте:

byte result[] = c.doFinal(bufferBytes);
String resultStr = Base64.encodeToString(result, false);
if (passwordInput.equalsIgnoreCase(resultStr)) {
    return true;
}

Но вместо этого используйте:

byte result[] = c.doFinal(bufferBytes);
String resultStr = new String(result);
if (passwordInput.equals(resultStr)) {
    return true;
}
Другие вопросы по тегам