UnrecoverableKeyException after software or security updates

Hello!

We have developed a support of biometric authentication in our mobile app using BiometricPrompt API. We decided to go with CryptoObjects as it allows us to comply with regulations.

After implementation we are facing an issue that on Samsung devices after some device software updates and after almost every Security Patch our Key is getting corrupted/invalidated. This forces us generating a new key pair, leading to a terrible user experience (having to re-enable a feature again and again).

Error log:

Caused by java.security.UnrecoverableKeyException: Failed to obtain information about key
at android.security.keystore.AndroidKeyStoreProvider.getKeyCharacteristics(AndroidKeyStoreProvider.java:238)
at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(AndroidKeyStoreProvider.java:360)
at android.security.keystore.AndroidKeyStoreSpi.engineGetKey(AndroidKeyStoreSpi.java:116)
at java.security.KeyStore.getKey(KeyStore.java:1062)
at com.app_name.mobile.data.crypto.CryptoRepositoryImpl.getSignature(CryptoRepositoryImpl.java:527)
at com.app_name.mobile.business.biometric.authentication.BiometricAuthenticationPromptInteractorImpl$getSignatureForAuthentication$1.subscribe(BiometricAuthenticationPromptInteractorImpl.java:52)

Caused by android.security.KeyStoreException: User authentication required
at android.security.KeyStore.getKeyStoreException(KeyStore.java:1151)
at android.security.keystore.AndroidKeyStoreProvider.getKeyCharacteristics(AndroidKeyStoreProvider.java:240)
at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(AndroidKeyStoreProvider.java:360)
at android.security.keystore.AndroidKeyStoreSpi.engineGetKey(AndroidKeyStoreSpi.java:116)
at java.security.KeyStore.getKey(KeyStore.java:1062)

Has anyone experienced similar issues? How to prevent our key getting invalidated/corrupted after Samsung security patches? Is it actually a normal behavior and outcome of Security patches?

We are generating a key pair using following code (fallback is used as we discovered that cheaper Samsung devices not supporting EC, hence we use RSA on such devices) :

@RequiresApi(Build.VERSION_CODES.M)
override fun createSigningKey(keyBaseName: KeyBaseName, useFallbackAlgorithm: Boolean): Either<Throwable, JavaPublicKey> = try {
deleteKey(keyBaseName)
when {
useFallbackAlgorithm → KeyPairGenerator
.getInstance(KEY_ALGORITHM_RSA, ANDROID_KEY_STORE_PROVIDER)
.apply {
initialize(KeyGenParameterSpec.Builder(keyBaseName, PURPOSE_SIGN or PURPOSE_VERIFY)
.setAlgorithmParameterSpec(RSAKeyGenParameterSpec(KEY_SIZE, RSAKeyGenParameterSpec.F4))
.setDigests(DIGEST_SHA256, DIGEST_SHA512)
.setSignaturePaddings(SIGNATURE_PADDING_RSA_PKCS1)
.setUserAuthenticationRequired(true)
.build())
}
else → KeyPairGenerator
.getInstance(KEY_ALGORITHM_EC, ANDROID_KEY_STORE_PROVIDER)
.apply {
initialize(KeyGenParameterSpec.Builder(keyBaseName, PURPOSE_SIGN or PURPOSE_VERIFY)
.setDigests(DIGEST_SHA256, DIGEST_SHA512)
.setUserAuthenticationRequired(true)
.build())
}
}.generateKeyPair()
.public
.right()
} catch (e: Exception) {
firebaseRepository.logException(RuntimeException(“createSigningKey”, e))
e.left()
}

Thank you!