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

5
这里是测试者:
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$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)

这些是我的函数:

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

如果检查键是否相同的断言起作用了,为什么我会收到异常提示?

你能否将异常的完整堆栈跟踪添加上去,而不仅限于它的消息? - Zoltán
这不可能是真的,这行代码:Assert.assertTrue(Arrays.equals(orgVal.getBytes(), decrypted)); 抛出了那个异常。orgVal 是一个 String,而 decrypted 只是一个 byte[]。所有的加密调用在那时都已经完成了。 - weston
@weston,你是对的。我已经编辑过了。 - jmb95
2个回答

5
您的encryptAESdecryptAES方法中出现了java.security.InvalidKeyException: Illegal key size or default parameters异常。因此,不要吃掉它们,可以声明为throws或提升为RuntimeException
事实证明,您有两个问题,由于这个原因,您不能使用256位,但是使用128位就可以解决这个问题,然后您还需要请求CBC而没有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
   ...

所以我像这样解决了它,但第一步是停止吞噬异常,这样你就能得到更好的线索,很可能自己就能解决问题。


我尝试只更改为ECB,它可以工作!我将尝试使用IvParameterSpec。我不需要更改keyLength。我没有遇到“java.security.InvalidKeyException:非法密钥大小或默认值”,因为我调试了程序并且encrypted[]不为空。 - jmb95
无论如何,saltLength也应该改为16。它可以与IvParameterSpec一起使用。 - jmb95

-2

我正在使用没有IvParameterSpec的CBC。

通过添加以下内容来加密和解密,问题得到了解决:

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

其中“getIvSpecAES256()”始终返回相同的值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接