解密时出现AEADBadTagException

时间:2019-07-26 05:02:16

标签: android android-keystore aes-gcm

我在这里发现了许多类似的问题,但是没有任何建议对我有用。我在登录应用程序并尝试在应用程序的其他部分解密时使用密钥库对用户密码进行加密。加密工作正常,但是尝试解密该值时,我遇到了AEADBadTagException。

我的代码如下。

加密方法。

@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();
    }
}

加密器类:

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

以上加密部分正常工作。我稍后在应用程序中尝试解密值的方式如下:

来自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类如下。只有我在这里得到例外

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

在加密的同时,加密的数据和iv被存储为共享的首选项,以便在解密时也可以使用它。 当我尝试从同一活动进行加密和解密时,它的工作正常,仅当尝试在应用程序的其他部分进行解密时,才会发生此问题。 任何帮助表示赞赏。

1 个答案:

答案 0 :(得分:0)

如果问题仅在此设备上发生,而不是在其他设备上发生,则可能是Android密钥库已为您的应用程序锁定,因为您没有注意线程安全问题。 Android密钥库不是线程安全的。 这是为我工作的解决方案:

  1. 向AndroidManifest.xml文件添加allowBackup
<application
    android:allowBackup="false"
...
>
  1. synchronized包装所有Android密钥库操作。