java.security.UnrecoverableKeyException: no se pudo obtener información sobre la clave privada
android keystore (1)
Tengo las siguientes líneas para obtener la clave privada del almacén de claves en Android
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
// generating key pair code omitted
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) this.keyStore.getEntry("alias", null);
Todo funciona bien, excepto que cuando el sistema operativo se actualiza de Android 5.1.1 a Android 6.0.1, la tercera línea lanzará java.security.UnrecoverableKeyException: Failed to obtain information about private key
para la primera ejecución. Pero funcionará bien de nuevo después. Ahora mi solución es ejecutar la línea 2 veces. Al mismo tiempo, también me pregunto si hay una mejor manera de evitar la excepción.
Actualizar
La traza de excepción
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
¿Cuándo ocurre este error y por qué?
Respuesta: Cuando se cargan las claves de Android y se almacena una clave pública desde el Almacén de claves, este error puede ocurrir si el estado está bloqueado o sin inicializar.
El error que genera el código de la parte se da a continuación:
@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 tiene 10 códigos de respuesta. Son
// 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 tiene 3 estados. Están DESBLOQUEADOS, BLOQUEADOS, ININITIALIZADOS.
NO_ERROR solo ocurre cuando el estado está DESBLOQUEADO. Para su caso de actualización, el estado es BLOQUEADO o SININICIALIZADO por primera vez, por lo que el error se produce solo una vez.
El código de verificación del estado se da a continuación:
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);
}
}
Enlace de recursos:
ACTUALIZAR:
De su registro de errores, ahora está claro que
W/System.err﹕ Caused by: android.security.KeyStoreException: Invalid key blob
este es el problema principal que se produce cuando el usuario intenta DESBLOQUEAR desde BLOQUEO / UNINITIALIZADO. Se define de forma predeterminada como 30 segundos para el tiempo. Este problema es su problema de implementación relacionado con la 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;
Para el cifrado / descifrado, algunos datos con la clave generada solo funcionan si el usuario acaba de autenticarse mediante las credenciales del dispositivo. El error se produce desde
// 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.
El error real se produce desde aquí. Su error se genera desde InvalidKeyException
.
Solución:
InvalidKeyException
eliminar la clase InvalidKeyException
del argumento catch. Esto aún le permitirá verificar si hay InvalidKeyException
. Después de verificar, debe intentar el código por segunda vez para que el problema no se muestre a simple vista, pero si lo hace dos veces, puede resolver su problema. No he probado el código pero debería ser como a continuación:
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;
}
}
}