Android cipher.doFinal在重新打开应用程序后尝试解密时出现BadPaddingException

时间:2016-10-24 08:25:36

标签: android encryption badpaddingexception

问题可能很长,但我会尝试详细描述。

这是demo问题,就像我的一样。

我有一个Android应用程序,我想添加一个功能,允许用户在SharedPreferences中加密和保存他们的密码,并从SharedPreferences读取和解密它们。它仅在指纹登记时可用,指纹有效可用作获取这些密码的验证方式。

存储时

  
      
  1. 用户输入密码
  2.   
  3. 通过AndroidKeyStore生成的SecretKey创建加密模式密码
  4.   
public Cipher getEncryptCipher() {
    try {
        Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
                + KeyProperties.BLOCK_MODE_CBC + "/"
                + KeyProperties.ENCRYPTION_PADDING_PKCS7);
        mKeyStore.load(null);
        SecretKey key = (SecretKey) mKeyStore.getKey(KEY_NAME, null);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        return cipher;
    } catch (Exception e) {
        throw new RuntimeException("Failed to encrypt pin ", e);
    }
}
  
      
  1. FingerprintManager使密码有效(密码A)并获得真正的加密密码(密码B)
  2.   
//mCryptoObject is generated by cipher in step 2
mFingerprintManager.authenticate(mCryptoObject, mCancellationSignal, 0 /* flags */, FingerprintManager.AuthenticationCallback, null);


//in FingerprintManager.AuthenticationCallback
javax.crypto.Cipher cipher = result.getCryptoObject().getCipher();
  
      
  1. 由FingerprintManager支持的密码(密码B)加密密码
  2.   
  3. 在SharedPreferences中存储加密密码和密码iv
  4.   
//In my app, we have many device, every could have one password. Pin is password.
public void encryptPin(String deviceId, String pin, javax.crypto.Cipher cipher) {
    try {
        if (cipher == null) return;
        byte[] encryptedBytes = cipher.doFinal(pin.getBytes("utf-8"));
        byte[] cipherIv = cipher.getIV();
        String encodedPin = Base64.encodeToString(encryptedBytes, Base64.DEFAULT);
        String encodeCipherIv = Base64.encodeToString(cipherIv, Base64.DEFAULT);
        SharedPreferences.Editor editor = mSharedPreferences.edit();
        editor.putString(createPinKey(deviceId), encodedPin);
        editor.putString(createIvKey(deviceId), encodeCipherIv);
        editor.apply();
    } catch (IOException | IllegalBlockSizeException | BadPaddingException e) {
        throw new RuntimeException("Failed to encrypt pin ", e);
    }
}
阅读时

  
      
  1. 读取密码iv并创建decrpty模式密码(Cipher C)
  2.   
public Cipher getDecryptCipher(String deviceId) {
    try {
        String encodedIv = mSharedPreferences.getString(createIvKey(deviceId), "");
        byte[] cipherIv = Base64.decode(encodedIv, Base64.DEFAULT);
        Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
                + KeyProperties.BLOCK_MODE_CBC + "/"
                + KeyProperties.ENCRYPTION_PADDING_PKCS7);
        mKeyStore.load(null);
        SecretKey key = (SecretKey) mKeyStore.getKey(KEY_NAME, null);
        cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(cipherIv));
        return cipher;
    } catch (Exception e) {
        throw new RuntimeException("Failed to encrypt pin ", e);
    }
}
  
      
  1. 有效指纹并获得真正的解密密码(Cipher D)
  2.   
//mCryptoObject is generated by cipher in step 1
mFingerprintManager.authenticate(mCryptoObject, mCancellationSignal, 0 /* flags */, FingerprintManager.AuthenticationCallback, null);


//in FingerprintManager.AuthenticationCallback
javax.crypto.Cipher cipher = result.getCryptoObject().getCipher();
  
      
  1. 读取加密密码并通过真实解密密码(密码D)
  2. 解密   
public String decryptPin(String deviceId, javax.crypto.Cipher cipher) {
    String encryptedPin = mSharedPreferences.getString(createPinKey(deviceId), "");
    if (TextUtils.isEmpty(encryptedPin)) {
        return "";
    }
    try {
        if (cipher == null) return "";
        byte[] decodedBytes = Base64.decode(encryptedPin, Base64.DEFAULT);
        //BadPaddingException in this line 
        byte[] decryptBytes = cipher.doFinal(decodedBytes);
        return new String(decryptBytes, Charset.forName("UTF8"));
    } catch (Exception e) {
        MyLog.d(TAG, "Failed to decrypt the data with the generated key." + e.getMessage());
        e.printStackTrace();
        return "";
    }
}

我的初始化方法:

private void init() {
    try {
        mKeyStore = KeyStore.getInstance("AndroidKeyStore");
        mKeyStore.load(null);
        KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(KEY_NAME,
                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                .setUserAuthenticationRequired(true)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            builder.setInvalidatedByBiometricEnrollment(true);
        }


        KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
        keyGenerator.init(
                    builder.build());
        keyGenerator.generateKey();


    } catch (Exception e) {
        throw new RuntimeException("Fail to init:" + e);
    }
}

如果不关闭app,一切都好!

然而,当我终止进程并重新启动它时,我在decrypt.doFinal()时在decryptPin()方法中得到了BadPaddingException。

System.err: javax.crypto.BadPaddingException
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:482)
at javax.crypto.Cipher.doFinal(Cipher.java:1502)
at com.xiaomi.smarthome.framework.page.verify.DeviceVerifyConfigCache.decryptPin(DeviceVerifyConfigCache.java:156)
at com.xiaomi.smarthome.framework.page.verify.VerifyManager.decryptPin(VerifyManager.java:173)
at com.xiaomi.smarthome.framework.page.verify.FingerPrintVerifyActivity$1.onAuthenticated(FingerPrintVerifyActivity.java:62)
at com.xiaomi.smarthome.framework.page.verify.view.FingerPrintOpenVerifyDialog.onAuthenticationSucceeded(FingerPrintOpenVerifyDialog.java:136)
at android.hardware.fingerprint.FingerprintManager$MyHandler.sendAuthenticatedSucceeded(FingerprintManager.java:805)
at android.hardware.fingerprint.FingerprintManager$MyHandler.handleMessage(FingerprintManager.java:757)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5458)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)
Caused by: android.security.KeyStoreException: Invalid argument
at android.security.KeyStore.getKeyStoreException(KeyStore.java:632)
at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:224)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:473)
... 13 more

有没有人可以帮我解决这个问题?是由SecretKey引起的吗?谢谢!!!

1 个答案:

答案 0 :(得分:4)

我找到了我的问题。在我的init()方法中使用相同的别名生成Key是错误的。我通过添加判断条件来解决问题。

        if (!mKeyStore.containsAlias(KEY_NAME)) {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
            keyGenerator.init(
                    builder.build());
            keyGenerator.generateKey();
        }