Android Java AES加密密码填充和模式错误

时间:2017-01-17 04:14:17

标签: java android encryption cryptography aes

这是我昨天提出的问题的延续: Android Java AES Encryption

我目前正在Android上测试AES加密。在我之前的问题中,我能够使用Cipher c = Cipher.getInstance("AES");

加密和解密字符串

我在回复中被告知我应该指定IV,加密模式和填充以防止将来出现任何潜在问题,因为没有规范意味着程序将使用系统的默认值。所以我将代码更改为c = Cipher.getInstance("AES/CBC/PKCS5Padding");

但现在我的代码不再有效,它将返回NullPointerException。

我的代码:

byte[] a = encryptFIN128AES("pls");
String b = decryptFIN128AES(a);
Log.e("AES_Test", "b = " + b);

/**
     * Encrypts a string with AES (128 bit key)
     * @param fin
     * @return the AES encrypted string
     */
    private byte[] encryptFIN128AES(String fin) {

        SecretKeySpec sks = null;

        try {
            sks = new SecretKeySpec(generateKey(PASSPHRASE, SALT.getBytes(StandardCharsets.UTF_8)).getEncoded(), "AES");
        } catch (Exception e) {
            Log.e("encryptFIN128AES", "AES key generation error");
        }

        // Encode the original data with AES
        byte[] encodedBytes = null;
        try {
            Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
            c.init(Cipher.ENCRYPT_MODE, sks);
            encodedBytes = c.doFinal(fin.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            Log.e("encryptFIN128AES", "AES encryption error");
        }

        return encodedBytes;

    }


    /**
     * Decrypts a string with AES (128 bit key)
     * @param encodedBytes
     * @return the decrypted String
     */
    private String decryptFIN128AES(byte[] encodedBytes) {

        SecretKeySpec sks = null;

        try {
            sks = new SecretKeySpec(generateKey(PASSPHRASE, SALT.getBytes(StandardCharsets.UTF_8)).getEncoded(), "AES");
        } catch (Exception e) {
            Log.e("decryptFIN128AES", "AES key generation error");
        }

        // Decode the encoded data with AES
        byte[] decodedBytes = null;
        try {
            Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
            c.init(Cipher.DECRYPT_MODE, sks);
            decodedBytes = c.doFinal(encodedBytes);
        } catch (Exception e) {
            Log.e("decryptFIN128AES", "AES decryption error");
        }

        //return Base64.encodeToString(decodedBytes, Base64.DEFAULT);
        return new String(decodedBytes, StandardCharsets.UTF_8);
    }


/**
     * Build private key from a passpharase/PIN (incl. key derivation (Uses PBKDF2))
     * @param passphraseOrPin
     * @param salt
     * @return The generated SecretKey (Used for AES-encryption, key size specified in outputKeyLength)
     */
    public static SecretKey generateKey(char[] passphraseOrPin, byte[] salt)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        // Number of PBKDF2 hardening rounds to use. Larger values increase
        // computation time. You should select a value that causes computation
        // to take >100ms.
        final int iterations = 1000;

        // Generate a 256-bit key
        final int outputKeyLength = 128;

        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec keySpec = new PBEKeySpec(passphraseOrPin, salt, iterations, outputKeyLength);
        SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
        return secretKey;
    }

输出:

E/decryptFIN128AES: AES decryption error
E/AndroidRuntime: FATAL EXCEPTION: Thread-176
                  Process: testapp.ttyi.nfcapp, PID: 2920
                  java.lang.NullPointerException: Attempt to get length of null array
                      at java.lang.String.<init>(String.java:371)
                      at testapp.ttyi.nfcapp.DisplayQRActivity.decryptFIN128AES(DisplayQRActivity.java:254)
                      at testapp.ttyi.nfcapp.DisplayQRActivity.access$100(DisplayQRActivity.java:29)
                      at testapp.ttyi.nfcapp.DisplayQRActivity$1.run(DisplayQRActivity.java:77)
                      at java.lang.Thread.run(Thread.java:818)

testapp.ttyi.nfcapp.DisplayQRActivity.decryptFIN128AES(DisplayQRActivity.java:254)指向decryptFIN128AES的最后一行,即: return new String(decodedBytes, StandardCharsets.UTF_8);

我知道发生NullPointerException是因为解密过程出了问题。由于它必须进入catch案例,因此decodedBytes仍为NULL,因此当我想要返回decodedBytes时会导致错误。所以现在我的问题是:为什么会发生这种情况,我该如何解决这个问题?

非常感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

非常感谢@Thomas W的帮助。我将catch更改为throw,现在我可以看到实际错误与BadPaddingException: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt

有关

经过一些谷歌搜索后,我发现解决方案是c.init中缺少IV参数。以前我使用的是"AES",其中Java默认为"AES/ECB/PKCS5Padding",并且在没有IV的情况下工作正常。 (来源:Android: Encrypt a string with AES 256bit Encryption with iv and secret key

但是一旦我改为"AES/CBC/PKCS5Padding",Java就会出现没有声明IV的问题。因此,将c.init(Cipher.ENCRYPT_MODE, sks);c.init(Cipher.DECRYPT_MODE, sks);更改为 c.init(Cipher.ENCRYPT_MODE, sks, new IvParameterSpec(new byte[16]));c.init(Cipher.DECRYPT_MODE, sks, new IvParameterSpec(new byte[16]));解决了这个问题。

现在我的程序可以正确加密和解​​密。