Search code examples
androidkotlinencryptionaes

AES BadTag Exception when I try to decrypt in android


I'm trying to encrypt a simple string using AES encryption. Currently I'm generating a secrect key using KeyGenerator and I'm generating a random IV with 16 bytes using Secure Random

The problem is, when I run this code:

  @RequiresApi(Build.VERSION_CODES.M)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val random = SecureRandom()
        val iv = ByteArray(12)
        random.nextBytes(iv)
        aesKeystoreAESWrapper = AES_WRAPPER()
        aesKeystoreAESWrapper.createSymmetricKey()
        val teste = aesKeystoreAESWrapper.encrypt("OLA MALTA", iv)
        val result = aesKeystoreAESWrapper.decrypt(teste, iv)


        ola.text = result

    }

I get a RunTime Execption: javax.crypto.AEADBadTagException

I can't figure out where the problem is, I tried to search in multiple websites but I couldn't find the awnser.

This is my AES code:

class AES_WRAPPER {

    fun ByteArray.fromBytetoString() = String(this,Charsets.UTF_8)
    companion object{
        const val AES_NOPAD_TRANS = "AES/GCM/NoPadding" //Format - ”Algorithm/Mode/Padding”
        const val ANDROID_KEYSTORE = "AndroidKeyStore"
        const val KEY_ALIAS = "Keyalaisasf"
    }

    private fun createKeyStore(): KeyStore {
        val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE)
        keyStore.load(null)
        return keyStore
    }

    @RequiresApi(23)
    fun createSymmetricKey() : SecretKey {
        try{
            val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEYSTORE)

            val keyGenParameterSpec = KeyGenParameterSpec.Builder(
                KEY_ALIAS,
                KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                .setRandomizedEncryptionRequired(false)
                .build()
            keyGenerator.init(keyGenParameterSpec)
            return keyGenerator.generateKey()
        } catch (e: NoSuchAlgorithmException) {
            throw RuntimeException("Failed to create a symmetric key", e)
        } catch (e: NoSuchProviderException) {
            throw RuntimeException("Failed to create a symmetric key", e)
        } catch (e: InvalidAlgorithmParameterException) {
            throw RuntimeException("Failed to create a symmetric key", e)
        }
    }

    fun encrypt(data: String, initVector: ByteArray) : ByteArray{
        val iv = GCMParameterSpec(128, initVector)

        val cipher = Cipher.getInstance(AES_NOPAD_TRANS)
        cipher.init(Cipher.ENCRYPT_MODE, getSymmetricKey(), iv)

        val encrypted = cipher.doFinal(data.toByteArray())

        return encrypted
    }

    fun decrypt(data: ByteArray, initVector: ByteArray) : String{
        val iv = GCMParameterSpec(128, initVector)

        val cipher = Cipher.getInstance(AES_NOPAD_TRANS)
        cipher.init(Cipher.DECRYPT_MODE, getSymmetricKey(), iv)

        val decrypted = cipher.doFinal(data)

        return decrypted.fromBytetoString()
    }

    @SuppressLint("NewApi")
    fun getSymmetricKey(): SecretKey {
        /*val keysore = keyStore.getEntry(KEY_ALIAS, null) as KeyStore.SecretKeyEntry
        return keysore.secretKey*/

        val keyStore = createKeyStore()

        if(!isKeyExists(keyStore)){
            createSymmetricKey()
        }

        return keyStore.getKey(KEY_ALIAS,null) as SecretKey
    }

    fun isKeyExists(keyStore : KeyStore): Boolean {
        val aliases = keyStore.aliases()
        while (aliases.hasMoreElements()) {
            return (KEY_ALIAS == aliases.nextElement())
        }
        return false
    }

}

UPDATE, LOGCAT:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: io.github.andre00nogueira.myapplication, PID: 10135
    java.lang.RuntimeException: Unable to start activity ComponentInfo{io.github.andre00nogueira.myapplication/io.github.andre00nogueira.myapplication.MainActivity}: javax.crypto.AEADBadTagException
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3270)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
     Caused by: javax.crypto.AEADBadTagException
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:517)
        at javax.crypto.Cipher.doFinal(Cipher.java:2055)
        at io.github.andre00nogueira.myapplication.WRAPPER.decrypt(WRAPPER.kt:73)
        at io.github.andre00nogueira.myapplication.MainActivity.onCreate(MainActivity.kt:29)
        at android.app.Activity.performCreate(Activity.java:7802)
        at android.app.Activity.performCreate(Activity.java:7791)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016) 
        at android.os.Handler.dispatchMessage(Handler.java:107) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.app.ActivityThread.main(ActivityThread.java:7356) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) 
     Caused by: android.security.KeyStoreException: Signature/MAC verification failed
        at android.security.KeyStore.getKeyStoreException(KeyStore.java:1292)
        at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:224)
        at android.security.keystore.AndroidKeyStoreAuthenticatedAESCipherSpi$BufferAllOutputUntilDoFinalStreamer.doFinal(AndroidKeyStoreAuthenticatedAESCipherSpi.java:373)
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:506)

Solution

  • The problem is caused by the isKeyExists() function, which should check if KEY_ALIAS is contained in the keystore. However, the current implementation only checks the first alias found by aliases.nextElement() against KEY_ALIAS and returns with this result. Therefore this function only works reliably if there is exactly one alias in the keystore. With more than one alias this leads to a false negative result if KEY_ALIAS is in the keystore but not found as first by aliases.nextElement(). As a consequence, different keys are created for encryption and decryption, which raises the AEADBadTagException during decryption.

    The problem can be solved if in isKeyExists() the return statement in the while loop is replaced by

    if (KEY_ALIAS == aliases.nextElement()) return true
    

    or alternatively, as suggested in Michael's comment, if isKeyExists(keyStore) is replaced by keyStore.containsAlias(KEY_ALIAS) in getSymmetricKey().