20

In our app we've been having issues with data in the Android Keystore suddenly becoming inaccessible. The specific exception we're seeing is here:

java.security.UnrecoverableKeyException: Failed to obtain information about private key
 at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStorePublicKeyFromKeystore(AndroidKeyStoreProvider.java:223)
 at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(AndroidKeyStoreProvider.java:259)
 at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(AndroidKeyStoreProvider.java:269)
 at android.security.keystore.AndroidKeyStoreSpi.engineGetKey(AndroidKeyStoreSpi.java:94)
 at java.security.KeyStoreSpi.engineGetEntry(KeyStoreSpi.java:474)
 at java.security.KeyStore.getEntry(KeyStore.java:1560)
 at <PACKAGE_NAME>.EncryptionInteractor.generateKeys(EncryptionInteractor.java:104)
 at <PACKAGE_NAME>.EncryptionInteractor.generateKeys(EncryptionInteractor.java:100)
 at <PACKAGE_NAME>.EncryptionInteractor.init(EncryptionInteractor.java:93)
 at <PACKAGE_NAME>.EncryptionInteractor.<init>(EncryptionInteractor.java:80)
 at <PACKAGE_NAME>.EncryptionInteractor.init(EncryptionInteractor.java:65)
 at <PACKAGE_NAME>.<APPLICATION_CLASS>.onCreate(APPLICATION_CLASS.java:17)
 at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1118)
 at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5791)
 at android.app.ActivityThread.-wrap1(Unknown Source:0)
 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1661)
 at android.os.Handler.dispatchMessage(Handler.java:105)
 at android.os.Looper.loop(Looper.java:164)
 at android.app.ActivityThread.main(ActivityThread.java:6541)
 at java.lang.reflect.Method.invoke(Native Method)
 at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: android.security.KeyStoreException: Invalid key blob
 at android.security.KeyStore.getKeyStoreException(KeyStore.java:695)
 at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStorePublicKeyFromKeystore(AndroidKeyStoreProvider.java:224)
  ... 21 more

We have not been able to come up with a reliable way of reproducing the issue. Several articles mention possible states that can cause the Keystore to "forget" a Key or become locked such as here. However, as far as I can tell we have not fallen into any of these edge cases. It seems to happen after letting the device sit for a while after the first setup of the key. We have seen this happen across multiple emulators and devices, ranging from 21 to 26. Additionally these devices have used either swipe to unlock or a PIN. Changing the PIN or security method does not seem to cause the issue. Again, this issue seems to occur after the device has been sitting unused for several days.

I have found two other SOs here and here as well as one Google issue. If I'm understanding correctly, the answer linked in both seems to rely upon the premise that the caller has called setUserAuthenticationValidityDurationSeconds when creating the Key, and we have not done so. Additionally the given solution seems to rely upon just deleting the key and generating a new one.

Below is our setup for the key on for versions >= API 23. I've left out our key generation for versions older than 23, since we've primarily seen this on APIs >= 23.

private static final int RSA_KEY_SIZE = 2048;
private static final String CERT_SUBJECT_STRING = "CN=<COMPANY_NAME> Android App O=<COMPANY_NAME>";
private static final String ANDROID_KEY_STORE = "AndroidKeyStore";

try {
        String alias = KEY_NAME;
        KeyPairGenerator generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEY_STORE);

        Calendar start = Calendar.getInstance();
        Calendar end = Calendar.getInstance();
        end.add(Calendar.YEAR, 1);
        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
            .setAlgorithmParameterSpec(new RSAKeyGenParameterSpec(RSA_KEY_SIZE, RSAKeyGenParameterSpec.F4))
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
            .setBlockModes(KeyProperties.BLOCK_MODE_ECB)
            .setCertificateNotAfter(end.getTime())
            .setCertificateNotBefore(start.getTime())
            .setCertificateSerialNumber(BigInteger.ONE)
            .setCertificateSubject(new X500Principal(CERT_SUBJECT_STRING))
            .build();
        generator.initialize(spec);
        generator.generateKeyPair();
    } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
        e.printStackTrace();
    }

We then attempt to access the keys later via keyStore.getEntry(KEY_NAME, null). Again, this works for a while, but then will begin to throw the above exception.

jinaud
  • 211
  • 1
  • 5

1 Answers1

0

I also faced issues with stability in KeyStore.

The solution was to use for private key

PrivateKey privKey = ks.getKey(alias, password)

And this to public key

PublicKey pubKey = ks.getCertificate(alias).getPublicKey();

instead of getEntry

ks.getEntry(alias, password)

The problem wasn't in the way you're creating the key but the way you're reading it.

Over more than a year never saw this issue again.

devzeze
  • 142
  • 1
  • 5