9

I cannot obtain a (private) key from KeyStore on Android. Problem occurs mainly on Samsung devices (S6, S6 Edge) and Android 6.

android.security.KeyStoreException: Invalid key blob

is thrown when following line is called (where alias is name for store key).

KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);

The KeyStore itself is obtained by

KeyStore.getInstance("AndroidKeyStore");

And key is generated by the following method:

private static void createKey(String alias, String subject, KeyStore keyStore, BigInteger serialNumber, Date startDate, Date endDate, String algorithm, String keyStoreProvider, Context context)
            throws KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException {
    if (keyStore.containsAlias(alias)) {
        // Key already exists.
        return;
    }

    // Generate keys.
    KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
            .setAlias(alias)
            .setSubject(new X500Principal(subject))
            .setSerialNumber(serialNumber)
            .setStartDate(startDate)
            .setEndDate(endDate)
            .build();

    KeyPairGenerator generator = KeyPairGenerator.getInstance(algorithm, keyStoreProvider);
    generator.initialize(spec);

    KeyPair keyPair = generator.generateKeyPair();
}

Where algorithm is "RSA" and keyStoreProvider is "AndroidKeyStore".

The part of the stacktrace:

android.security.KeyStoreException: Invalid key blob
       at android.security.KeyStore.getKeyStoreException(KeyStore.java:939)
       at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStorePublicKeyFromKeystore(AndroidKeyStoreProvider.java:216)
       at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(AndroidKeyStoreProvider.java:252)
       at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(AndroidKeyStoreProvider.java:263)
       at android.security.keystore.AndroidKeyStoreSpi.engineGetKey(AndroidKeyStoreSpi.java:93)
       at java.security.KeyStoreSpi.engineGetEntry(KeyStoreSpi.java:372)
       at java.security.KeyStore.getEntry(KeyStore.java:645)

The exception causes java.security.UnrecoverableKeyException: Failed to obtain information about private key to be thrown.

I was not able to find any closer information about "Invalid key blob", only that the message itself is defined here: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/security/keymaster/KeymasterDefs.java

Ankis
  • 143
  • 2
  • 6
  • This sounds like an issue with hardware-backed keymaster (crypto provider for Android Keystore) on these devices. Is there anything interesting in the system log in the couple of seconds prior to this exception? For example, any mention of "keymaster", errors to do with keys/blobs, qseecom? – Alex Klyubin Apr 07 '16 at 23:09
  • @AlexKlyubin Thanks for the suggestion, I'll to have a look. – Ankis Apr 08 '16 at 08:04
  • @Ankis, I am having the same issue. Did you find out the solution already? – Dino Tw Apr 14 '16 at 17:19
  • @DinoTw Not yet. I have information about the issue from Crashlytics, on the device I managed to test on I was not able to reproduce it yet. Should get access to additional devices so hopefully there will be some progress. Do you happen to know how to reproduce it step by step? Did you notice any device state that could be causing it (screen lock setting, ...)? – Ankis Apr 18 '16 at 08:09
  • The issue happens to me when I upgrade the OS from Android 5 to Android 6, I posted my question and talked about my current workaround here, http://stackoverflow.com/q/36652675/691626. Upgrading from 4 to 5 works fine, I guess it's because of the API change. – Dino Tw Apr 18 '16 at 16:40

1 Answers1

3

This problem is occurred when user tries to UNLOCK from LOCK/UNINITIALIZED. It is by default defined as 30 secs for timing. This problem is it's API related implementation issue.

This error is generated from InvalidKeyException. By bypassing this exception and call the method again, you can get rid of this error.

You have to remove the InvalidKeyException class from the catch argument. This will still allow you to check for InvalidKeyException. After checking you have to try for second time with code so that the problem is not shown in eye but doing 2 times checking it may solve your issue. Code is given below.

try {
    KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) this.keyStore
            .getEntry("alias", null);

} catch (InvalidKeyException ex) {
    ex.printStackTrace();
    if (ex 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 ex;
        }
    }
} catch (final Exception e) {
    e.printStackTrace();
    throw e;
}

You can see my another answer that describes one by one occurring issue and solution.

UPDATE from @Ankis:

As you solved the issue by changing InvalidKeyException to UnrecoverableKeyException. So I have updated as per your suggestion so that world can know the actual answer. Thanks for sharing :).

try {
    KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) this.keyStore
            .getEntry("alias", null);

} catch (UnrecoverableKeyException ex) {
    ex.printStackTrace();
        // 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);
        }
} catch (final Exception e) {
    e.printStackTrace();
    throw e;
}
Community
  • 1
  • 1
SkyWalker
  • 28,384
  • 14
  • 74
  • 132
  • Sorry for a late reaction. I am using the similar solution as the poster in your linked question - catching the exception, determining whether the cause matches this specific case and then attempting to retrieve the key again. If you update your answer code (instanceof check is not needed, change from InvalidKeyException to UnrecoverableKeyException) I will accepted the answer. – Ankis May 19 '16 at 08:39
  • @Ankis I have updated answer. If any change needed, please inform. I will update. – SkyWalker May 19 '16 at 10:03
  • What is the purpose of the `keyStore.deleteEntry(keyName)` call? Surely if you delete the key and try get it again it won't be there anymore?? – Adil Hussain May 22 '18 at 15:23
  • @SkyWalker is there something in the keystore documentation that supports what you are suggesting here? – Efren Jun 08 '18 at 22:22
  • @Efren Actually, it's a hacking way to solve this issue. It's a pattern. – SkyWalker Jun 08 '18 at 22:27
  • @SkyWalker Thanks for the quick answer. I understand the approach. Just to make sure I understand what you posted for your particular case. I see you are deleting the entry if getting it throws the UnrecoverableKeyException, then calling the method again. So the next time that you call getEntry you will get null, isn't it? – Efren Jun 08 '18 at 22:34
  • @Efren You are right. On second call `keyStore.containsAlias` will return false and we can generate key as usual without app crash – Dmitriy Grachev Feb 06 '19 at 21:20
  • 1
    How does deleting the entry solve the problem? It will be impossible to decrypt the user's data after that. – Apfelsaft Feb 19 '19 at 13:56
  • 1
    when I am trying to delete ALIAS it still throws UnrecoverableKeyException. This is happening on Android 8 Samsung device. – Mangesh Kadam Apr 12 '19 at 07:31