Android 9 - KeyStore exception android.os.ServiceSpecificException

17,710

Solution 1

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();

Solution 2

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

Solution 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
        }
}
Share:
17,710
phnmnn
Author by

phnmnn

Android and iOS developer

Updated on June 14, 2022

Comments

  • phnmnn
    phnmnn almost 2 years

    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.

  • mreichelt
    mreichelt over 5 years
    Thanks! Using the getKey() and getCertificate() methods instead of getEntry() indeed solved the problem.
  • giroxiii
    giroxiii over 5 years
    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.
  • wildcat12
    wildcat12 over 5 years
    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.
  • Gabor
    Gabor about 5 years
    It does not solve the issue, at least on Android Q, beta 2.
  • Gabor
    Gabor about 5 years
    Also, the exception persists, so not only happening first time you start the app (reference to the above comment which states so).
  • Tharkius
    Tharkius over 4 years
    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.