使用相同密钥解密 AES 时出现 BadPaddingException

BadPaddingException when decrypting AES with the same key

这是测试人员:

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());
        }
    }
}

由于最后一个断言而失败:

Assert.fail(e.getMessage());

尝试执行时:

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

给出此堆栈跟踪:

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.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)

这些是我的职能:

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

如果检查键是否相同的断言有效,为什么会出现该异常?

您正在 encryptAESdecryptAES 方法中处理 java.security.InvalidKeyException: Illegal key size or default parameters 异常。所以不要吃它们,要么声明为 throws 要么提升为 RuntimeException.

原来你有两个问题,for this reason, you can't do 256, but 128 solves that, then you are also requesting CBC without an IvParameterSpec(导致 java.security.InvalidKeyException: Parameters missing)。所以提供或更改为 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);
}

密钥长度:

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

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

所以我让它像那样工作,但第一步是停止吃异常,你会得到更好的线索,可能会自己解决。

我正在使用没有 IvParameterSpec 的 CBC。

加解密解决了:

cipher.init(Cipher."mode here", sKeyS, getIvSpecAES256()); 

其中 "getIvSpecAES256()" return 总是相同的值。