samples-android icon indicating copy to clipboard operation
samples-android copied to clipboard

java.security.KeyStoreException: the master key android-keystore exists but is unusable

Open rajeshjadidminc opened this issue 2 years ago • 4 comments

Describe the bug?

Application crashed due to java.security.KeyStoreException: the master key android-keystore exists but is unusable

What is expected to happen?

It should be run as usual when running with the check box enable for biometric.

What is the actual behaviour?

java.security.KeyStoreException: the master key android-keystore://com_okta_sample_storage exists but is unusable at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.readOrGenerateNewMasterKey(AndroidKeysetManager.java:276) at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.build(AndroidKeysetManager.java:237) at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:165) at com.cfna.app.okta.storage.SharedPreferencesModule.createSharedPreferences(SharedPreferencesModule.kt:47) at com.cfna.app.okta.storage.SharedPreferencesModule.providesBiometricCredentialSharedPrefs(SharedPreferencesModule.kt:94) at com.cfna.app.okta.storage.SharedPreferencesModule_ProvidesBiometricCredentialSharedPrefsFactory.providesBiometricCredentialSharedPrefs(SharedPreferencesModule_ProvidesBiometricCredentialSharedPrefsFactory.java:42) at com.cfna.app.DaggerCFNAApplication_HiltComponents_SingletonC$SingletonCImpl$SwitchingProvider.get(DaggerCFNAApplication_HiltComponents_SingletonC.java:757) at dagger.internal.DoubleCheck.get(DoubleCheck.java:47) at com.cfna.app.okta.biometric.BiometricCredentialsManager.useBiometricCredentialStorage(BiometricCredentialsManager.kt:64) at com.cfna.app.ui.home.HomeKt$launchBiometricPrompt$2$onAuthenticationSucceeded$1.invokeSuspend(Home.kt:269) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) at androidx.compose.ui.platform.AndroidUiDispatcher.performTrampolineDispatch(AndroidUiDispatcher.android.kt:81) at androidx.compose.ui.platform.AndroidUiDispatcher.access$performTrampolineDispatch(AndroidUiDispatcher.android.kt:41) at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.run(AndroidUiDispatcher.android.kt:57) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:233) at android.app.ActivityThread.main(ActivityThread.java:8068) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:631) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:978) Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.ui.platform.MotionDurationScaleImpl@f6488b6, androidx.compose.runtime.BroadcastFrameClock@239d9b7, StandaloneCoroutine{Cancelling}@43057b4, AndroidUiDispatcher@e5c788d] Caused by: android.security.keystore.UserNotAuthenticatedException: User not authenticated at android.security.KeyStore.getInvalidKeyException(KeyStore.java:1369) at android.security.KeyStore.getInvalidKeyException(KeyStore.java:1388) at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:54) at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:89) at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:265) at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:109) at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2984) at javax.crypto.Cipher.tryCombinations(Cipher.java:2891) at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796) at javax.crypto.Cipher.chooseProvider(Cipher.java:773) at javax.crypto.Cipher.init(Cipher.java:1143) at javax.crypto.Cipher.init(Cipher.java:1084) at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.encryptInternal(AndroidKeystoreAesGcm.java:84) at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.encrypt(AndroidKeystoreAesGcm.java:72) 2022-12-23 21:19:03.914 15506-15506 AndroidRuntime com.cfna.app.debug E at com.google.crypto.tink.integration.android.AndroidKeystoreKmsClient.validateAead(AndroidKeystoreKmsClient.java:259) at com.google.crypto.tink.integration.android.AndroidKeystoreKmsClient.getAead(AndroidKeystoreKmsClient.java:175) at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.readOrGenerateNewMasterKey(AndroidKeysetManager.java:268) ... 21 more

Reproduction Steps?

  • Login without biometric (remove biometric setup from device)
  • Go dashboard
  • Enable checkbox( i have managed the code with if biometric is not enable then it will go setting and come back with enabling the biometric and check again in activity result if its enable run the same which you have mention for checkbox)
  • application crashed.

Additional Information?

  • please try to resolve this asap we are planning to release the build to the customer.

SDK Version(s)

latest sdk version

coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' implementation(platform('com.okta.kotlin:bom:1.1.1')) implementation('com.okta.kotlin:auth-foundation-bootstrap') implementation('com.okta.kotlin:web-authentication-ui') implementation 'androidx.security:security-crypto-ktx:1.1.0-alpha04' implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.2" implementation 'androidx.biometric:biometric:1.1.0'

Build Information

No response

rajeshjadidminc avatar Dec 23 '22 16:12 rajeshjadidminc

Hey @rajeshjadidminc,

From your stacktrace, your logic for requesting user to add biometrics in settings, and then coming back to the app seems correct and onAuthenticationSucceeded callback is getting called correctly. Is it possible that biometric SharedPrefs were opened at any point before biometric prompt was successfully authenticated?

rajdeepnanua-okta avatar Dec 23 '22 18:12 rajdeepnanua-okta

I looked into this issue more and realized that the biometric keystore can be invalidated if the user adds or removes fingerprints from the device. I've added a fix for this, which is invalidating the biometric key and trying again. That should fix this issue. Biometric support is also merged into master branch of this repo now.

rajdeepnanua-okta avatar Dec 23 '22 21:12 rajdeepnanua-okta

Hi,

I am a little bit confused about the 2 variables used in the share preference class

_biometricEnabled _biometricAuthenticated

rajeshjadidminc avatar Dec 26 '22 08:12 rajeshjadidminc

Hi @rajeshjadidminc,

biometricEnabled is for used to check if the app is currently using biometric-backed storage or regular storage for storing credentials.

biometricAuthenticated is used to check if the user has successfully authenticated a biometric prompt. I use biometricAuthenticated to:

  1. Display the biometric prompt only if the user hasn't already authenticated earlier
  2. Throw an IllegalStateException in case the code attempts to open biometric storage without authenticating first

rajdeepnanua-okta avatar Jan 03 '23 15:01 rajdeepnanua-okta