android.security.KeyStoreException: Invalid key blob
Asked Answered
D

1

10

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

Dentate answered 7/4, 2016 at 22:19 Comment(5)
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?Malinowski
@AlexKlyubin Thanks for the suggestion, I'll to have a look.Dentate
@Ankis, I am having the same issue. Did you find out the solution already?Herr
@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, ...)?Dentate
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, https://mcmap.net/q/465481/-java-security-unrecoverablekeyexception-failed-to-obtain-information-about-private-key/691626. Upgrading from 4 to 5 works fine, I guess it's because of the API change.Herr
A
2

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;
}
Arrogate answered 25/4, 2016 at 16:27 Comment(10)
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.Dentate
@Dentate I have updated answer. If any change needed, please inform. I will update.Arrogate
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??Knudsen
@Arrogate is there something in the keystore documentation that supports what you are suggesting here?Reciprocity
@Reciprocity Actually, it's a hacking way to solve this issue. It's a pattern.Arrogate
@Arrogate 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?Reciprocity
@Reciprocity You are right. On second call keyStore.containsAlias will return false and we can generate key as usual without app crashCane
How does deleting the entry solve the problem? It will be impossible to decrypt the user's data after that.Skip
when I am trying to delete ALIAS it still throws UnrecoverableKeyException. This is happening on Android 8 Samsung device.Bernitabernj
Really bad suggestion TBH. This will delete the key causing loss of highly sensitive data.Nisen

© 2022 - 2024 — McMap. All rights reserved.