Android Java AES шифрование обивка шифра и ошибка режима

Это продолжение моего вчерашнего вопроса:
Android Java AES шифрование

В настоящее время я тестирую шифрование AES на Android. В моем предыдущем вопросе я могу зашифровать и расшифровать строку, используя шифр 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.access0(DisplayQRActivity.java:29)
                      at testapp.ttyi.nfcapp.DisplayQRActivity

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.

.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 ответ

  1. Большое спасибо @Thomas W за вашу помощь. Я изменил catchна throwи теперь я вижу, что фактическая ошибка имеет какое — то отношение к BadPaddingException: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt

    После некоторого гугления я нашел решение-отсутствие аргумента IV в моем c.init. Ранее я использовал"AES", в котором Java по умолчанию"AES/ECB/PKCS5Padding", и это прекрасно работает без IV.
    (Источник: Android: шифрование строки с AES 256bit шифрование с iv и секретным ключом)

    Но как только я перешел на "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]));Исправлена ошибка.

    Теперь моя программа может шифровать и расшифровывать должным образом.