简体   繁体   中英

Android Java AES Encryption Cipher Padding and Mode Error

This is a continuation from my question yesterday: Android Java AES Encryption

I am currently testing AES encryption on Android. In my previous question I am able to encrypt and decrypt a string using Cipher c = Cipher.getInstance("AES");

I was informed in the replies that I should specify the IV, encryption mode and padding to prevent any potential issues in the future since no specification means the program will use the system's default value. So I changed my code to c = Cipher.getInstance("AES/CBC/PKCS5Padding");

But now my code no longer works, and it will return a NullPointerException.

My code:

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

Output:

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) points to the last line of decryptFIN128AES , which is: return new String(decodedBytes, StandardCharsets.UTF_8);

I understand that the NullPointerException occurs because something went wrong with the decryption process. Since it must have went into the catch case and thus decodedBytes remains as NULL and thus causes the error when I want to return decodedBytes . So now my question is: Why does this happen and how can I fix this?

Your help is much appreciated.

Many thanks to @Thomas W for your help. I changed the catch to throw and now I can see the actual error has something to do with BadPaddingException: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt

After some googling I found the solution is the lack of IV argument in my c.init . Previously I was using "AES" in which Java defaults to "AES/ECB/PKCS5Padding" and that works fine without IV. (Source: Android: Encrypt a string with AES 256bit Encryption with iv and secret key )

But once I changed to "AES/CBC/PKCS5Padding" Java will have issues without a declared IV. Therefore by changing c.init(Cipher.ENCRYPT_MODE, sks); and c.init(Cipher.DECRYPT_MODE, sks); to c.init(Cipher.ENCRYPT_MODE, sks, new IvParameterSpec(new byte[16])); and c.init(Cipher.DECRYPT_MODE, sks, new IvParameterSpec(new byte[16])); fixed the issue.

Now my program can encrypt and decrypt properly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM