Android Empreintes digitales de l'API de Chiffrement et de Déchiffrement

Je suis en utilisant le Android M d'Empreintes digitales API pour permettre aux utilisateurs de se connecter à l'application. Pour ce faire j'aurais besoin de stocker le nom d'utilisateur et mot de passe sur l'appareil. Actuellement, j'ai le login de travail, ainsi que les Empreintes digitales de l'API, mais le nom d'utilisateur et le mot de passe sont stockés en clair. Je voudrais crypter le mot de passe avant que je le stocker, et être en mesure de le récupérer une fois que l'utilisateur s'authentifie auprès de leurs empreintes digitales.

Je vais avoir une grande quantité de difficulté se présente au travail. J'ai essayé d'appliquer ce que je peux à partir de la Android de Sécurité des échantillons, mais chaque exemple, semble être la seule à gérer le chiffrement ou la signature, et jamais de déchiffrement.

Ce que j'ai est que j'ai pour obtenir une instance de la AndroidKeyStore, un KeyPairGenerator et un Cipher, en utilisant la cryptographie asymétrique pour permettre l'utilisation de l'Android KeyGenParameterSpec.Constructeur().setUserAuthenticationRequired(true). La raison de cryptographie asymétrique est parce que le setUserAuthenticationRequired méthode bloc tout utilisation de la clé si l'utilisateur n'est pas authentifié, mais:

Cette autorisation ne s'applique qu'à clé secrète et à clé privée de l'exploitation. La clé publique activités ne sont pas limitées.

Cela devrait me permettre de crypter le mot de passe à l'aide de la clé publique avant que l'utilisateur s'authentifie auprès de leurs empreintes digitales, puis déchiffrer à l'aide de la clé privée seulement après que l'utilisateur est authentifié.

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("EC", "AndroidKeyStore");
} catch(NoSuchAlgorithmException | NoSuchProviderException exception) {
throw new RuntimeException("Failed to get an instance of KeyPairGenerator", exception);
}
}
public Cipher getCipher() {
try {
return Cipher.getInstance("EC");
} catch(NoSuchAlgorithmException | NoSuchPaddingException exception) {
throw new RuntimeException("Failed to get an instance of Cipher", exception);
}
}
private void createKey() {
try {
mKeyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")
.setUserAuthenticationRequired(true)
.build());
mKeyPairGenerator.generateKeyPair();
} catch(InvalidAlgorithmParameterException exception) {
throw new RuntimeException(exception);
}
}
private boolean initCipher(int opmode) {
try {
mKeyStore.load(null);
if(opmode == Cipher.ENCRYPT_MODE) {
PublicKey key = mKeyStore.getCertificate(KEY_ALIAS).getPublicKey();
mCipher.init(opmode, key);
} 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 decryptPassword(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);
}
}

Pour être honnête, je ne sais pas si tout cela est vrai, c'est des morceaux de tout ce que j'ai pu trouver sur le sujet. Tout ce que j'ai changer jette un autre exception, et ce build n'est pas exécuté parce que je ne peut pas instancier la Cipher, il jette un NoSuchAlgorithmException: No provider found for EC. J'ai essayé de passer à RSA ainsi, mais j'obtiens des erreurs similaires.

Donc ma question est essentiellement ceci, je fais comment pour chiffrer le texte clair sur Android, et le rendre disponible pour le déchiffrement après que l'utilisateur est authentifié par l'Empreinte de l'API?


J'ai fait quelques progrès, principalement en raison de la découverte de l'information sur le KeyGenParameterSpec page de documentation.

J'ai gardé getKeyStore, encryptePassword, decryptPassword, getKeyPairGenerator et getCipher pour la plupart les mêmes, mais j'ai changé le KeyPairGenerator.getInstance et Cipher.getInstance à "RSA" et "RSA/ECB/OAEPWithSHA-256AndMGF1Padding" respectivement.

J'ai aussi changé le reste du code, le RSA au lieu de la Courbe Elliptique, parce que de ce que je comprends, Java 1.7 (et, par conséquent, Android) ne prend pas en charge le chiffrement et le déchiffrement de CE. J'ai changé mon createKeyPair méthode basée sur la "paire de clés RSA pour le chiffrement/déchiffrement à l'aide de RSA OAEP." exemple sur la page de documentation:

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

J'ai aussi modifié mon initCipher méthode basée sur la problème connu dans le KeyGenParameterSpec documentation:

Un bogue connu dans Android 6.0 (API Level 23) provoque l'authentification de l'utilisateur liées à des autorisations d'être appliquée même pour les clés publiques. Pour contourner ce problème extraire la clé publique de matériel pour une utilisation en dehors de l'Android fichier de clés.

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

Maintenant, je peux crypter le mot de passe et enregistrer le mot de passe crypté. Mais lorsque je obtenir le mot de passe crypté et essayer de décrypter, je reçois un KeyStoreException erreur Inconnue...

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)
  • Hey souhaitez-vous être en mesure de créer un résumé de l'intégralité du code nécessaire pour faire cela? Ive été la recherche d'une solution à ce pour environ 2 semaines maintenant
  • Pour être honnête, je n'ai pas de séparation entre le code de l'INTERFACE utilisateur de chiffrement/déchiffrement du code très bien, et le code s'appuie sur la Dague et RxJava, afin de faire un facilement réutilisables gist peuvent pas être trivial. Je vais voir ce que je peux trouver. Mais pour l'instant, la plupart du code (sans Croix) est dans une autre question de la mienne: Comment Utiliser non pris en charge Exception pour la Plate-forme Inférieure de la Version.
  • seriez-vous capable de répondre à mes question? stackoverflow.com/questions/40724749/...
InformationsquelleAutor Bryan | 2016-03-14