2

I have found many similar questions here, but none of the suggestions worked for me. I am encrypting user password with keystore while logging to the application and trying to decrypt in some other part of my application. Encryption is working proper but while trying to decrypt the value, I am getting AEADBadTagException.

My code is as follows.

Encryption method.

@RequiresApi(api = Build.VERSION_CODES.M)
private void encryptText() {
    try {
        final byte[] encryptedText = encryptor
                .encryptText("MY_ALIAS",mPsd.toString());
    } catch (UnrecoverableEntryException | NoSuchAlgorithmException | NoSuchProviderException |
            KeyStoreException | IOException | NoSuchPaddingException | InvalidKeyException e) {
    } catch (InvalidAlgorithmParameterException | SignatureException |
            IllegalBlockSizeException | BadPaddingException e) {
        e.printStackTrace();
    }
}

Encryptor class :

public class EnCryptor {
private static final String TRANSFORMATION = "AES/GCM/NoPadding";
private static final String ANDROID_KEY_STORE = "AndroidKeyStore";

private byte[] encryption;
private byte[] iv;

public EnCryptor() {
}
@RequiresApi(api = Build.VERSION_CODES.M)
public byte[] encryptText(final String alias, final String textToEncrypt)
        throws UnrecoverableEntryException, NoSuchAlgorithmException, KeyStoreException,
        NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, IOException,
        InvalidAlgorithmParameterException, SignatureException, BadPaddingException,
        IllegalBlockSizeException {

    final Cipher cipher = Cipher.getInstance(TRANSFORMATION);
    cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(alias));

    iv = cipher.getIV();
    encryption = cipher.doFinal(textToEncrypt.getBytes("UTF-8"));
    SharedPreferencesManager.getInstance().saveEncrypt(Base64.encodeToString(encryption, Base64.DEFAULT));
    SharedPreferencesManager.getInstance().saveEncrypted_iv(Base64.encodeToString(iv, Base64.DEFAULT));
    return (encryption);

}

@RequiresApi(api = Build.VERSION_CODES.M)
@NonNull
private SecretKey getSecretKey(final String alias) throws NoSuchAlgorithmException,
        NoSuchProviderException, InvalidAlgorithmParameterException {

    final KeyGenerator keyGenerator = KeyGenerator
            .getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
    keyGenerator.init(new KeyGenParameterSpec.Builder(alias,
            KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
            .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
            .build());
    return keyGenerator.generateKey();
}
public byte[] getEncryption() {
    return encryption;
}
public byte[] getIv() {
    return iv;
}
}

The above part for encryption work properly. how I am trying to decrypt the value later in my application is like follows:

Method call from Fragment:

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private char[] decryptText() {
    try {
        String txt = decryptor.decryptData("MY_ALIAS",
                Base64.decode(SharedPreferencesManager.getInstance().getEncrypt(), Base64.DEFAULT),
                Base64.decode(SharedPreferencesManager.getInstance().getEncrypted_iv(), Base64.DEFAULT));
        return txt.toCharArray();
    } catch (UnrecoverableEntryException | NoSuchAlgorithmException |
            KeyStoreException | NoSuchPaddingException | NoSuchProviderException |
            IOException | InvalidKeyException e) {
    } catch (IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) {
        e.printStackTrace();
    }
    return "".toCharArray();
}

DeCryptor class is as follows. Here only I am getting the exception

public class DeCryptor {

private static final String TRANSFORMATION = "AES/GCM/NoPadding";
private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
private KeyStore keyStore;
public DeCryptor() throws CertificateException, NoSuchAlgorithmException, KeyStoreException,
        IOException {
    initKeyStore();
}
private void initKeyStore() throws KeyStoreException, CertificateException,
        NoSuchAlgorithmException, IOException {
    keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
    try {
        keyStore.load(null);
    } catch (java.security.cert.CertificateException e) {
        e.printStackTrace();
    }
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public String decryptData(final String alias, final byte[] encryptedData, final byte[] encryptionIv)
        throws UnrecoverableEntryException, NoSuchAlgorithmException, KeyStoreException,
        NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, IOException,
        BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {

    final Cipher cipher = Cipher.getInstance(TRANSFORMATION);
    final GCMParameterSpec spec = new GCMParameterSpec(128, encryptionIv);
    cipher.init(Cipher.DECRYPT_MODE, getSecretKey(alias), spec);
    return new String(cipher.doFinal(encryptedData), "UTF-8");
}
private SecretKey getSecretKey(final String alias) throws NoSuchAlgorithmException,
        UnrecoverableEntryException, KeyStoreException {
    return ((KeyStore.SecretKeyEntry) keyStore.getEntry(alias, null)).getSecretKey();
}
}

While encrypting , the encrypted data and iv are stored to shared preference so that it can be used while the time of decrypt as well. When I try to encrypt and decrypt from same activity, its working properly, this issue is happening only when trying to decrypt in some other part of the application. Any help are appreciated.

visakh r
  • 175
  • 1
  • 11
  • Can you share a complete test app that demonstrates the problem? – divegeek Aug 24 '19 at 02:43
  • having the same issue. works if i encrypt without IV. save it in shared prefs. I then get the IV from sharedPrefs. create GCMParameterSpec with it. and call doFinal() but i get AEADBadTagException. works fine when i execute the excryption and decyption sequentially without resart of the activity. – filthy_wizard Mar 12 '20 at 22:33

1 Answers1

0

If the issue is only happening on this device, not on another, it could be the Android keystore is locked for your application because you didn't take care the thread-safety issue. Android keystore is not thread-safe. Here is the solutions worked for me:

  1. Add allowBackup false to AndroidManifest.xml file
<application
    android:allowBackup="false"
...
>
  1. Wrap all your Android keystore operations with synchronized.
Weidian Huang
  • 2,787
  • 2
  • 20
  • 29
  • For more detail, please refer to https://medium.com/@weidianhuang/how-to-write-thread-safe-code-for-android-keystore-40a0fa17f416 – Weidian Huang Sep 06 '20 at 05:18