J'ai les lignes suivantes pour obtenir la clé privée du magasin de clés sur Android
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
// generating key pair code omitted
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) this.keyStore.getEntry("alias", null);
Tout fonctionne bien sauf que lorsque le système d'exploitation passe de Android 5.1.1 à Android 6.0.1, la 3ème ligne lancera Java.security.UnrecoverableKeyException: Failed to obtain information about private key
pour la toute première exécution. Mais cela fonctionnera bien à nouveau par la suite. Maintenant, ma solution consiste à exécuter la ligne 2 fois. En même temps, je me demande également s'il n'y a pas de meilleur moyen d'éviter l'exception.
La trace d'exception
W/System.err﹕ Java.security.UnrecoverableKeyException: Failed to obtain information about private key
W/System.err﹕ at Android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStorePublicKeyFromKeystore(AndroidKeyStoreProvider.Java:217)
W/System.err﹕ at Android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(AndroidKeyStoreProvider.Java:253)
W/System.err﹕ at Android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(AndroidKeyStoreProvider.Java:263)
W/System.err﹕ at Android.security.keystore.AndroidKeyStoreSpi.engineGetKey(AndroidKeyStoreSpi.Java:93)
W/System.err﹕ at Java.security.KeyStoreSpi.engineGetEntry(KeyStoreSpi.Java:372)
W/System.err﹕ at Java.security.KeyStore.getEntry(KeyStore.Java:645)
W/System.err﹕ at com.example.keystoretest.MainActivity.onCreate(MainActivity.Java:113)
W/System.err﹕ at Android.app.Activity.performCreate(Activity.Java:6251)
W/System.err﹕ at Android.app.Instrumentation.callActivityOnCreate(Instrumentation.Java:1107)
W/System.err﹕ at Android.app.ActivityThread.performLaunchActivity(ActivityThread.Java:2369)
W/System.err﹕ at Android.app.ActivityThread.handleLaunchActivity(ActivityThread.Java:2476)
W/System.err﹕ at Android.app.ActivityThread.-wrap11(ActivityThread.Java)
W/System.err﹕ at Android.app.ActivityThread$H.handleMessage(ActivityThread.Java:1344)
W/System.err﹕ at Android.os.Handler.dispatchMessage(Handler.Java:102)
W/System.err﹕ at Android.os.Looper.loop(Looper.Java:148)
W/System.err﹕ at Android.app.ActivityThread.main(ActivityThread.Java:5417)
W/System.err﹕ at Java.lang.reflect.Method.invoke(Native Method)
W/System.err﹕ at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:726)
W/System.err﹕ at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:616)
W/System.err﹕ Caused by: Android.security.KeyStoreException: Invalid key blob
W/System.err﹕ at Android.security.KeyStore.getKeyStoreException(KeyStore.Java:632)
W/System.err﹕ at Android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStorePublicKeyFromKeystore(AndroidKeyStoreProvider.Java:218)
W/System.err﹕ ... 18 more
Rép: Lors du chargement des clés Android et du stockage de la clé publique à partir du magasin de clés, cette erreur peut se produire si l'état est verrouillé ou non initialisé.
@NonNull
public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
@NonNull KeyStore keyStore, @NonNull String privateKeyAlias)
throws UnrecoverableKeyException {
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
int errorCode = keyStore.getKeyCharacteristics(privateKeyAlias, null,
null, keyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
throw (UnrecoverableKeyException) new UnrecoverableKeyException(
"Failed to obtain information about private key")
.initCause(KeyStore.getKeyStoreException(errorCode)); // this exception is generated
}
......
......
......
}
KeyStore a 10 codes de réponse. Elles sont
// ResponseCodes
NO_ERROR = 1;
LOCKED = 2;
UNINITIALIZED = 3;
SYSTEM_ERROR = 4;
PROTOCOL_ERROR = 5;
PERMISSION_DENIED = 6;
KEY_NOT_FOUND = 7;
VALUE_CORRUPTED = 8;
UNDEFINED_ACTION = 9;
WRONG_PASSWORD = 10;
KeyStore a 3 états. Ils sont déverrouillés, verrouillés, non initialisés
NO_ERROR ne se produit que lorsque l'état est déverrouillé. Pour votre cas de mise à niveau, l'état est VERROUILLÉ ou NON INITIALISÉ pour la première fois, donc l'erreur ne s'est produite qu'une seule fois.
public State state() {
execute('t');
switch (mError) {
case NO_ERROR:
return State.UNLOCKED;
case LOCKED:
return State.LOCKED;
case UNINITIALIZED:
return State.UNINITIALIZED;
default:
throw new AssertionError(mError);
}
}
Lien de ressource:
D'après votre journal des erreurs, il est maintenant clair que
W/System.err﹕ Caused by: Android.security.KeyStoreException: Invalid key blob
c'est le principal problème qui se produit lorsque l'utilisateur essaie de déverrouiller à partir de LOCK/UNINITIALIZED. Il est défini par défaut comme 30 secondes pour le chronométrage. Ce problème est un problème d'implémentation lié à l'API.
/**
* If the user has unlocked the device Within the last this number of seconds,
* it can be considered as an authenticator.
*/
private static final int AUTHENTICATION_DURATION_SECONDS = 30;
Pour le chiffrement/déchiffrement, certaines données avec la clé générée ne fonctionnent que si l'utilisateur vient de s'authentifier via les informations d'identification de l'appareil. L'erreur se produit à partir de
// Try encrypting something, it will only work if the user authenticated within
// the last AUTHENTICATION_DURATION_SECONDS seconds.
cipher.init(Cipher.ENCRYPT_MODE, secretKey); // error is generated from here.
Une erreur réelle est lancée à partir d'ici. Votre erreur est générée à partir de InvalidKeyException
.
Vous devez supprimer la classe InvalidKeyException
de l'argument catch. Cela vous permettra toujours de vérifier InvalidKeyException
. Après avoir vérifié, vous devez essayer pour la deuxième fois avec du code afin que le problème ne soit pas visible dans les yeux, mais une vérification 2 fois peut résoudre votre problème. Je n'ai pas testé le code mais devrait être comme ci-dessous:
try {
....
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) this.keyStore.getEntry("alias", null);
....
} catch (final Exception e) {
e.printStackTrace();
if (e instanceof InvalidKeyException) { // bypass InvalidKeyException
.......
// You can again call the method and make a counter for deadlock situation or implement your own code according to your situation
if (retry) {
keyStore.deleteEntry(keyName);
return getCypher(keyName, false);
} else {
throw e;
}
}
}