3

Je tente de décrypter le texte chiffré après que l'utilisateur est authentifié par l'API Android M Fingerprint. J'ai surtout essayé de suivre les Android Security samples, et les exemples fournis dans la documentation KeyGenParameterSpec. Je suis en mesure de chiffrer avec succès le texte avec la clé publique, mais quand je l'appelle cipher.doFinal avec un Cipher en utilisant la clé privée dans DECRYPT_MODE, je reçois un KeyStoreException « Erreur inconnue »:Android KeyStoreException Inconnu Erreur

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) 

Le code actuel concerné est comme suit:

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

Ce qui pourrait être à l'origine de cette "erreur inconnue"? J'ai exclu le setUserAuthenticationRequired, mais je ne peux pas comprendre ce qui pourrait causer cela. J'ai également essayé d'utiliser le mCipher global au lieu du Cipher fourni par FragmentManagerCompat.AuthenticationCallback.onAuthenticationSucceeded (bien que, je pense qu'ils devraient être un et identique) avec le même résultat. J'ai rencontré ce problème en cherchant une réponse à another one of my questions.

+0

double possible de [API Android d'empreintes digitales Chiffrage et déchiffrage] (https://stackoverflow.com/questions/35992681/android-fingerprint- api-encryption-and-decryption) – petrsyn

+0

@petrsyn Bien que les deux questions aient finalement été résolues par le même problème que celui trouvé dans Issue Tracker, elles sont intrinsèquement différentes. La première demande spécifiquement * comment * effectuer le cryptage et le décryptage, et met en évidence les (multiples) erreurs que j'ai rencontrées en essayant de trouver une solution. Attendu que cette question se concentre sur juste une de ces erreurs. Je pense que cela pourrait être préjudiciable aux autres utilisateurs qui essaient de trouver une solution à cette erreur spécifique si nous devions la supprimer en faveur de mon autre question. – Bryan

Répondre

5

J'ai trouvé ma réponse sur la Android Issue Tracker, d'après ce que je comprends, le PublicKey sans restriction, créé pour travailler autour d'un autre known issue, devient incompatible avec le Cipher actuel. Le travail autour de cela est de spécifier un OAEPParameterSpec lorsque le Cipher est initialisé:

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

mCipher.init(opmode, unrestricted, spec);