I'm seeing app crashes in production on Android 12 and 13 (so far) as follows when calling Signature.initSign
:
android.security.keystore.UserNotAuthenticatedException: User not authenticated
at android.security.keystore2.KeyStoreCryptoOperationUtils.getInvalidKeyException(KeyStoreCryptoOperationUtils.java:128)
at android.security.keystore2.AndroidKeyStoreSignatureSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreSignatureSpiBase.java:217)
at android.security.keystore2.AndroidKeyStoreSignatureSpiBase.engineInitSign(AndroidKeyStoreSignatureSpiBase.java:123)
at android.security.keystore2.AndroidKeyStoreSignatureSpiBase.engineInitSign(AndroidKeyStoreSignatureSpiBase.java:101)
at java.security.Signature$Delegate.init(Signature.java:1357)
at java.security.Signature$Delegate.chooseProvider(Signature.java:1310)
at java.security.Signature$Delegate.engineInitSign(Signature.java:1385)
at java.security.Signature.initSign(Signature.java:679)
The flow I'm following is:
- generate a key pair in the android keystore with a sole purpose of
KeyProperties.PURPOSE_SIGN
. The code is similar to theKeyPairGenerator
sample here: https://developer.android.com/training/articles/keystore#UsingAndroidKeyStore (In my caseUserAuthenticationRequired
is true,invalidatedByBiometricEnrollment
is false) - later, when signing is required, retrieve the private key from the keystore and call initSign(
privateKey
) - Now launch the BiometricPrompt (passing in the initialised Signature as part of the Cipher object)
- Once the user has successfully authenticated, then call
Signature.update
andSignature.sign
on the returned Signature.
I might expect the crash at step 4 if the user had failed to authenticate, or if a weak authenticator was used, but this crash is happening at step 2 when calling initSign
. The user can't have authenticated here, as initSign must be called on the signature before passing it to the biometric prompt.
What could cause this? The crashes occur across multiple devices and once users experience this it repeats consistently, so doesn't seem to be a rare edge case. I cannot replicate this locally on a variety of devices on Android 12 and 13 so in theory the flow works.