Android 9 - KeyStore exception android.os.ServiceSpecificException
Asked Answered
Y

3

40

If I run this code on Android 9, I receive the follow exception:

private static KeyStore.PrivateKeyEntry getPrivateKeyEntry(String alias) {
        try {
            KeyStore ks = KeyStore
                    .getInstance(SecurityConstants.KEYSTORE_PROVIDER_ANDROID_KEYSTORE);
            ks.load(null);
            KeyStore.Entry entry = ks.getEntry(alias, null);

            if (entry == null) {
                Log.w(TAG, "No key found under alias: " + alias);
                Log.w(TAG, "Exiting signData()...");
                return null;
            }

            if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
                Log.w(TAG, "Not an instance of a PrivateKeyEntry");
                Log.w(TAG, "Exiting signData()...");
                return null;
            }
            return (KeyStore.PrivateKeyEntry) entry;
        } catch (Exception e) {
            Log.e(TAG, e.getMessage(), e);
            return null;
        }
    }

Exception:

KeyStore exception android.os.ServiceSpecificException: (code 7) at android.os.Parcel.createException(Parcel.java:1956) at android.os.Parcel.readException(Parcel.java:1910) at android.os.Parcel.readException(Parcel.java:1860) at android.security.IKeystoreService$Stub$Proxy.get(IKeystoreService.java:786) at android.security.KeyStore.get(KeyStore.java:195) at android.security.keystore.AndroidKeyStoreSpi.engineGetCertificateChain(AndroidKeyStoreSpi.java:118) at java.security.KeyStoreSpi.engineGetEntry(KeyStoreSpi.java:484) at java.security.KeyStore.getEntry(KeyStore.java:1560) at com.phenodev.testenc.KeyStoreHelper.getPrivateKeyEntry(KeyStoreHelper.java:151) at com.phenodev.testenc.KeyStoreHelper.encrypt(KeyStoreHelper.java:173) at com.phenodev.testenc.KeyStoreEncryptor.encrypt(KeyStoreEncryptor.java:19)

Please help to fix it.

Yellow answered 26/8, 2018 at 9:1 Comment(1)
i have the same problem :-(Guesswarp
N
50

Finally I found a solution. It looks like since Android P (KeyStore.PrivateKeyEntry) keyStore.getEntry("alias", null) is not a proper way to get private key.

I was able to get rid of this warning by accessing private/public key this way

KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);

PrivateKey privateKey = (PrivateKey) keyStore.getKey("alias", null);
PublicKey publicKey = keyStore.getCertificate("alias").getPublicKey();
Nietzsche answered 12/9, 2018 at 12:42 Comment(8)
Thanks! Using the getKey() and getCertificate() methods instead of getEntry() indeed solved the problem.Negrito
Although I'm currently extracting the keys this way, in an emulator with API 29 I'm still receiving the warning the first time I try to get the key (only the first time). If I kill the process and start the app again, the warning is gone.Donelu
And just for clarity, I tested it on API level 21 (to see if we need to use different logic conditional on the API version), but it seems to work fine there as well.Armilda
It does not solve the issue, at least on Android Q, beta 2.Blackamoor
Also, the exception persists, so not only happening first time you start the app (reference to the above comment which states so).Blackamoor
This can't be the final solution, even with this code the exception is still thrown every now and then in Android 9 (1 out of 7 times the app is opened, according to my tests). And it isn't just a warning, as it is always accompanied by this NPE, which results in no data getting encrypted or decrypted: Attempt to invoke virtual method 'java.security.PublicKey java.security.cert.Certificate.getPublicKey()' on a null object reference.Flameout
@Flameout You're absolutely right, I'm facing this issue in a later version of Android as well. Were you able to find any solution for the same? I asked a similar question here statics.teams.cdn.office.net/evergreen-assets/safelinks/1/…Polygynous
@KushalVijay Initially I tried switching to EncryptedSharedPreferences, but then other crashes started to appear. Eventually I realized the only solution is to bite the bullet and restart everything. I commented some code in this issue: github.com/google/tink/issues/535#issuecomment-915428069Flameout
M
8

I had the same problem retrieving an asymmetricKey from AndroidKeyStore

My solution based on Dr Glass answer to get keys is the following: (aliasKey is your alias string)

PublicKey:

val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)

val asymmetricPublicKey = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    keyStore.getCertificate(aliasKey).publicKey
} else {
    val asymmetricKey = keyStore.getEntry(aliasKey, null) as KeyStore.PrivateKeyEntry
    asymmetricKey.certificate.publicKey
}

PrivateKey:

val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)

val asymmetricPrivateKey = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    keyStore.getKey(aliasKey, null) as PrivateKey
} else {
    val asymmetricKey = keyStore.getEntry(aliasKey, null) as KeyStore.PrivateKeyEntry
    asymmetricKey.privateKey
}

and with this code I have no warning in the emulator and/or device with Android P

Menander answered 29/7, 2019 at 10:31 Comment(0)
M
3

I found a solution how to remove the warning, would be great if you can test it all as well. Actually the order of calling the methods is the problem.

val privateKey = keyStore.getKey(alias, null)
val publicKey = if (privateKey != null) keyStore.getCertificate(alias).publicKey else null

if (privateKey != null && publicKey != null) {
    KeyPair(publicKey, privateKey as PrivateKey)
}

This is the correct ordering of how to call the methods.

When you do it like this:

val privateKey = keyStore.getKey(alias, null)
val certificate = keyStore.getCertificate(alias)

if (privateKey != null && certificate != null) {
    KeyPair(certificate.publicKey, privateKey as PrivateKey)
}

You will receive the following warning (because of keyStore.getCertificate(alias):

KeyStore exception
android.os.ServiceSpecificException:  (code 7)
    at android.os.Parcel.createException(Parcel.java:2085)
    at android.os.Parcel.readException(Parcel.java:2039)
    at android.os.Parcel.readException(Parcel.java:1987)
    at android.security.keystore.IKeystoreService$Stub$Proxy.get(IKeystoreService.java:978)
    at android.security.KeyStore.get(KeyStore.java:236)
    at android.security.KeyStore.get(KeyStore.java:225)
    at android.security.keystore.AndroidKeyStoreSpi.engineGetCertificate(AndroidKeyStoreSpi.java:160)
    at java.security.KeyStore.getCertificate(KeyStore.java:1120)

This means, that there is no private key and you should first create and store a keypair before you search for it in the key store.

Now with the answer of MatPag it should look like this:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        val privateKey = keyStore.getKey(alias, null)
        val publicKey = if (privateKey != null) keyStore.getCertificate(alias).publicKey else null

        return if (privateKey != null && publicKey != null) {
            KeyPair(publicKey, privateKey as PrivateKey)
        } else {
            null
        }
} else {
        val asymmetricKey = keyStore.getEntry(alias, null) as KeyStore.PrivateKeyEntry
        val privateKey = asymmetricKey.privateKey
        val publicKey = if(privateKey != null) asymmetricKey.certificate.publicKey else null

        return if(privateKey != null && publicKey != null) {
            KeyPair(publicKey, privateKey as PrivateKey)
        } else {
            null
        }
}
Meador answered 27/11, 2019 at 14:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.