BadPaddingException when decrypting AES with the same key

jmb95 picture jmb95 · Jul 8, 2015 · Viewed 9.3k times · Source

This is the tester:

public class CryptographySimpleTests extends ActivityTestCase
{
    public void testsCryptographyClass_encryptAndDecrypt()
    {
        final String orgVal     = "hi world! :D";
        final String key        = "key";

        try
        {
            final byte[] encryptKey       = Cryptography.deriveAES256Key(key);
            final byte[] decryptKey       = Cryptography.deriveAES256Key(key);

            //Deviation method
            Assert.assertTrue(Arrays.equals(encryptKey, decryptKey));

            byte[] encrypted = Cryptography.encryptAES(encryptKey, orgVal.getBytes());

            Assert.assertFalse(Arrays.equals(encrypted, orgVal.getBytes()));

            byte[] decrypted = Cryptography.decryptAES(decryptKey, encrypted);

            Assert.assertTrue(Arrays.equals(orgVal.getBytes(), decrypted));
        }
        catch (Exception e) {
            Assert.fail(e.getMessage());
        }
    }
}

Which fails because of the last assert:

Assert.fail(e.getMessage());

When trying to execute:

byte[] decrypted = Cryptography.decryptAES(decryptKey, encrypted);

Gives this stack trace:

javax.crypto.BadPaddingException: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
        at com.android.org.conscrypt.NativeCrypto.EVP_CipherFinal_ex(Native Method)
        at com.android.org.conscrypt.OpenSSLCipher.doFinalInternal(OpenSSLCipher.java:430)
        at com.android.org.conscrypt.OpenSSLCipher.engineDoFinal(OpenSSLCipher.java:466)
        at javax.crypto.Cipher.doFinal(Cipher.java:1340)
        at bdevel.encuentralo.utils.Cryptography.decryptAES(Cryptography.java:59)
        at bdevel.encuentralo.CryptographySimpleTests.testsCryptographyClass_encryptAndDecrypt(CryptographySimpleTests.java:32)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
        at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
        at junit.framework.TestCase.runBare(TestCase.java:134)
        at junit.framework.TestResult$1.protect(TestResult.java:115)
        at junit.framework.TestResult.runProtected(TestResult.java:133)
        at junit.framework.TestResult.run(TestResult.java:118)
        at junit.framework.TestCase.run(TestCase.java:124)
        at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
        at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
        at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555)
        at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1837)

These are my functions:

public class Cryptography {

    /**
     * @param key           AES Key
     * @param inputValue    Data to encrypt
     * @return Can return null if something goes wrong
     */
    public static byte[] encryptAES(byte[] key, byte[] inputValue)
            throws NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException
    {
        SecretKeySpec sKeyS = new SecretKeySpec(key, "AES");

        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, sKeyS);
        }
        catch (NoSuchAlgorithmException | InvalidKeyException i) {
            cipher = null;
        }

        return cipher != null ? cipher.doFinal(inputValue) : null;
    }

    public static byte[] decryptAES(byte[] key, byte[] encryptedData)
            throws NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException
    {
        SecretKeySpec sKeyS = new SecretKeySpec(key, "AES");

        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, sKeyS);
        }
        catch (NoSuchAlgorithmException | InvalidKeyException i) {
            cipher = null;
        }

        return cipher != null ? cipher.doFinal(encryptedData) : null;
    }

    private static byte[] deriveAES256KeySalt = null;
    public static byte[] deriveAES256Key(String password)
            throws InvalidKeySpecException, NoSuchAlgorithmException
    {

    /* Store these things on disk used to derive key later: */
        int iterationCount = 1000;
        int saltLength = 32; // bytes; should be the same size as the output (256 / 8 = 32)
        int keyLength = 256; // 256-bits for AES-256, 128-bits for AES-128, etc

    /* When first creating the key, obtain a salt with this: */
    if(deriveAES256KeySalt == null) {
        SecureRandom random = new SecureRandom();
        deriveAES256KeySalt = new byte[saltLength];
        random.nextBytes(deriveAES256KeySalt);
    }

    /* Use this to derive the key from the password: */
        KeySpec keySpec = new PBEKeySpec(password.toCharArray(), deriveAES256KeySalt, iterationCount, keyLength);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();

        return keyBytes;
    }
}

If the assert that checks if the keys are the same works, why do I get that exception?

Answer

weston picture weston · Jul 8, 2015

You are eating an java.security.InvalidKeyException: Illegal key size or default parameters exception in your encryptAES and decryptAES methods. So don't eat them, either declare as throws or promote to RuntimeException.

Turns out you have two problems, for this reason, you can't do 256, but 128 solves that, then you are also requesting CBC without an IvParameterSpec (Which is causing java.security.InvalidKeyException: Parameters missing). So supply that or change to ECB:

public static byte[] encryptAES(byte[] key, byte[] inputValue)
        throws NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, InvalidKeyException {
    SecretKeySpec sKeyS = new SecretKeySpec(key, "AES");

    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, sKeyS);

    return cipher.doFinal(inputValue);
}

public static byte[] decryptAES(byte[] key, byte[] encryptedData)
        throws NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, InvalidKeyException {
    SecretKeySpec sKeyS = new SecretKeySpec(key, "AES");

    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, sKeyS);

    return cipher.doFinal(encryptedData);
}

Key length:

public static byte[] deriveAES256Key(String password)
                throws InvalidKeySpecException, NoSuchAlgorithmException {

   ...
    int keyLength = 128; // 256-bits for AES-256, 128-bits for AES
   ...

So I got it working like that, but step one is to stop eating the exception and you'll get better clues and probably work out on own.