Android 9 - KeyStore exception android.os.ServiceSpecificException
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
}
}
Comments
-
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 over 5 yearsThanks! Using the
getKey()
andgetCertificate()
methods instead ofgetEntry()
indeed solved the problem. -
giroxiii over 5 yearsAlthough 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 over 5 yearsAnd 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 about 5 yearsIt does not solve the issue, at least on Android Q, beta 2.
-
Gabor about 5 yearsAlso, the exception persists, so not only happening first time you start the app (reference to the above comment which states so).
-
Tharkius over 4 yearsThis 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.