Search code examples
exceptionencryptionaespaddingbadpaddingexception

Crazy exception at AES Decryption - BadPaddingException


I'm having some trouble here while trying to decode some encrypted text. CheckpswdBasedKey is always returning false, because of the BadPaddingException at c.doFInal I'm using AES, basicaly the encryption:

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;

Now decrypt mode:

//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;
}

I compared bytes from iv @checkPswdBasedKey and iv @generatePswdBasedKey and they are all the same. Same happens to the secretkey @checkPswdBasedKey (i get those bytes with: sk.getEncoded() ) and secretkey @generatePswdBasedKey... they are all equal. So basically when i decrypt i know i'm using the same key, same IV and same message... and an appropiate length (16 bytes key, 16 bytes msg, 16 bytes iv, using AES 128) Any idea?


Solution

  • As Joachim Isaksson commented, if you want to implement a password check, you ought to use a secure hash representation of the password, that is not reversible. This way, the password can't be obtained by decryption even if the hash + key is compromised.

    Anyway, in your generatePswdBasedKey you use the PBKDF2WithHmacSHA1 algorithm to generate a SecretKey, and then use that key to encrypt the password. Now you have two options to verify the password in checkPswdBasedKey. Either you:

    • encrypt the password the same way as in generatePswdBasedKey and compare that they give the same encrypted string

    or you

    • decrypt the encrypted version and compare the result with the password in clear.

    I presume that you try the later approach as you init your cipher for decrypt with:

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

    Hovewer, for that approach to work you need to instantiate your SecretKey the same way as you did in generatePswdBasedKey - currently you end up with two different keys.

    In 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");
    

    In checkPswdBasedKey:

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

    When that is fixed, you also need to look at your compare logic. You should not do a Base64 encoding of your result before the compare - and the compare ought to be case sensitive. Don't use:

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

    But instead use:

    byte result[] = c.doFinal(bufferBytes);
    String resultStr = new String(result);
    if (passwordInput.equals(resultStr)) {
        return true;
    }