3

我試圖在用戶通過Android M Fingerprint API進行身份驗證後解密加密的文本。我一直主要試圖遵循Android Security samples以及KeyGenParameterSpec文檔中提供的示例。我已經能夠成功地加密與公鑰的文本,但是當我打電話cipher.doFinal使用私鑰在DECRYPT_MODE一個Cipher,我得到一個KeyStoreException「未知錯誤」:Android KeyStoreException未知錯誤

03-15 10:06:58.074 14702-14702/com.example.app E/LoginFragment: Failed to decrypt password 
     javax.crypto.IllegalBlockSizeException 
      at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:486) 
      at javax.crypto.Cipher.doFinal(Cipher.java:1502) 
      at com.example.app.ui.fragment.util.LoginFragment.onAuthenticationSucceeded(LoginFragment.java:251) 
      at com.example.app.ui.controller.FingerprintCallback.onAuthenticationSucceeded(FingerprintCallback.java:21) 
      at android.support.v4.hardware.fingerprint.FingerprintManagerCompat$Api23FingerprintManagerCompatImpl$1.onAuthenticationSucceeded(FingerprintManagerCompat.java:301) 
      at android.support.v4.hardware.fingerprint.FingerprintManagerCompatApi23$1.onAuthenticationSucceeded(FingerprintManagerCompatApi23.java:96) 
      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:5417) 
      at java.lang.reflect.Method.invoke(Native Method) 
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
    Caused by: android.security.KeyStoreException: Unknown error 
      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) 
      at javax.crypto.Cipher.doFinal(Cipher.java:1502) 
      at com.example.app.ui.fragment.util.LoginFragment.onAuthenticationSucceeded(LoginFragment.java:251) 
      at com.example.app.ui.controller.FingerprintCallback.onAuthenticationSucceeded(FingerprintCallback.java:21) 
      at android.support.v4.hardware.fingerprint.FingerprintManagerCompat$Api23FingerprintManagerCompatImpl$1.onAuthenticationSucceeded(FingerprintManagerCompat.java:301) 
      at android.support.v4.hardware.fingerprint.FingerprintManagerCompatApi23$1.onAuthenticationSucceeded(FingerprintManagerCompatApi23.java:96) 
      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:5417) 
      at java.lang.reflect.Method.invoke(Native Method) 
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

有關當前的代碼如下:

public KeyStore getKeyStore() { 
    try { 
     return KeyStore.getInstance("AndroidKeyStore"); 
    } catch (KeyStoreException exception) { 
     throw new RuntimeException("Failed to get an instance of KeyStore", exception); 
    } 
} 

public KeyPairGenerator getKeyPairGenerator() { 
    try { 
     return KeyPairGenerator.getInstance("RSA", "AndroidKeyStore"); 
    } catch(NoSuchAlgorithmException | NoSuchProviderException exception) { 
     throw new RuntimeException("Failed to get an instance of KeyPairGenerator", exception); 
    } 
} 

public Cipher getCipher() { 
    try { 
     return Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); 
    } catch(NoSuchAlgorithmException | NoSuchPaddingException exception) { 
     throw new RuntimeException("Failed to get an instance of Cipher", exception); 
    } 
} 

private void createKeyPair() { 
    try { 
     mKeyPairGenerator.initialize(
       new KeyGenParameterSpec.Builder(KEY_ALIAS, KeyProperties.PURPOSE_DECRYPT) 
         .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) 
         .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP) 
         .setUserAuthenticationRequired(true) 
         .build()); 
     mKeyPairGenerator.generateKeyPair(); 
    } catch(InvalidAlgorithmParameterException exception) { 
     throw new RuntimeException("Failed to generate key pair", exception); 
    } 
} 

private boolean initCipher(int opmode) { 
    try { 
     mKeyStore.load(null); 

     if(opmode == Cipher.ENCRYPT_MODE) { 
      PublicKey key = mKeyStore.getCertificate(KEY_ALIAS).getPublicKey(); 

      PublicKey unrestricted = KeyFactory.getInstance(key.getAlgorithm()) 
        .generatePublic(new X509EncodedKeySpec(key.getEncoded())); 

      mCipher.init(opmode, unrestricted); 
     } else { 
      PrivateKey key = (PrivateKey) mKeyStore.getKey(KEY_ALIAS, null); 
      mCipher.init(opmode, key); 
     } 

     return true; 
    } catch (KeyPermanentlyInvalidatedException exception) { 
     return false; 
    } catch(KeyStoreException | CertificateException | UnrecoverableKeyException 
      | IOException | NoSuchAlgorithmException | InvalidKeyException 
      | InvalidAlgorithmParameterException exception) { 
     throw new RuntimeException("Failed to initialize Cipher", exception); 
    } 
} 

private void encrypt(String password) { 
    try { 
     initCipher(Cipher.ENCRYPT_MODE); 
     byte[] bytes = mCipher.doFinal(password.getBytes()); 
     String encryptedPassword = Base64.encodeToString(bytes, Base64.NO_WRAP); 
     mPreferences.getString("password").set(encryptedPassword); 
    } catch(IllegalBlockSizeException | BadPaddingException exception) { 
     throw new RuntimeException("Failed to encrypt password", exception); 
    } 
} 

private String decrypt(Cipher cipher) { 
    try { 
     String encryptedPassword = mPreferences.getString("password").get(); 
     byte[] bytes = Base64.decode(encryptedPassword, Base64.NO_WRAP); 
     return new String(cipher.doFinal(bytes)); 
    } catch (IllegalBlockSizeException | BadPaddingException exception) { 
     throw new RuntimeException("Failed to decrypt password", exception); 
    } 
} 

什麼可能導致這個「未知錯誤」?我排除了setUserAuthenticationRequired,但我無法弄清楚可能是什麼原因造成的。我也嘗試使用全球mCipher而不是FragmentManagerCompat.AuthenticationCallback.onAuthenticationSucceeded提供的Cipher(儘管我認爲它們應該是一個一樣的),結果相同。


我在找到another one of my questions的答案時遇到了這個問題。

+0

[Android的API指紋加密和解密(的可能的複製https://stackoverflow.com/questions/35992681/android-fingerprint- api-encryption-and-decryption) – petrsyn

+0

@petrsyn雖然兩個問題最終都是通過問題跟蹤器上發現的相同問題來回答的,但它們本質上是不同的問題。第一個問題特別要求*如何執行加密和解密,並強調我在嘗試尋找解決方案時遇到的(多個)錯誤。而這個問題關注的只是這些錯誤中的一個。我認爲如果我們爲了解決其他問題而將其刪除,可能對其他用戶試圖找到解決此特定錯誤的問題有害。 – Bryan

回答

5

我發現了Android Issue Tracker我的回答,從我的理解,無限制PublicKey,創建來解決另一個known issue,成爲當前Cipher不兼容。造成這種情況的解決辦法是指定一個OAEPParameterSpecCipher初始化:

OAEPParameterSpec spec = new OAEPParameterSpec(
     "SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); 

mCipher.init(opmode, unrestricted, spec);