epicshaggy / capacitor-native-biometric

186 stars 95 forks source link

Keystore key generation failed #101

Open RRGT19 opened 1 year ago

RRGT19 commented 1 year ago

Description:

Using Firebase Crashlytics I have a lot of errors about Keystore key generation failed.

Library version:

"capacitor-native-biometric": "^4.2.2"

Platforms:

Devices:

Exception:

Fatal Exception: java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
       at com.getcapacitor.Bridge.lambda$callPluginMethod$0(Bridge.java:783)
       at com.getcapacitor.Bridge.$r8$lambda$ehFTi5f4HhVNFKTbCKAYDkpQYRA()
       at com.getcapacitor.Bridge$$ExternalSyntheticLambda3.run(:8)
       at android.os.Handler.handleCallback(Handler.java:938)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:211)
       at android.os.Looper.loop(Looper.java:300)
       at android.os.HandlerThread.run(HandlerThread.java:67)

Caused by java.security.ProviderException: Keystore key generation failed
       at android.security.keystore2.AndroidKeyStoreKeyGeneratorSpi.engineGenerateKey(AndroidKeyStoreKeyGeneratorSpi.java:413)
       at javax.crypto.KeyGenerator.generateKey(KeyGenerator.java:612)
       at com.epicshaggy.biometric.NativeBiometric.generateKey(NativeBiometric.java:313)
       at com.epicshaggy.biometric.NativeBiometric.generateKey(NativeBiometric.java:295)
       at com.epicshaggy.biometric.NativeBiometric.getKey(NativeBiometric.java:324)
       at com.epicshaggy.biometric.NativeBiometric.encryptString(NativeBiometric.java:265)
       at com.epicshaggy.biometric.NativeBiometric.setCredentials(NativeBiometric.java:178)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:138)
       at com.getcapacitor.Bridge.lambda$callPluginMethod$0(Bridge.java:774)
       at com.getcapacitor.Bridge.$r8$lambda$ehFTi5f4HhVNFKTbCKAYDkpQYRA()
       at com.getcapacitor.Bridge$$ExternalSyntheticLambda3.run(:8)
       at android.os.Handler.handleCallback(Handler.java:938)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:211)
       at android.os.Looper.loop(Looper.java:300)
       at android.os.HandlerThread.run(HandlerThread.java:67)

Caused by android.security.KeyStoreException: System error
       at android.security.KeyStore2.getKeyStoreException(KeyStore2.java:334)
       at android.security.KeyStoreSecurityLevel.handleExceptions(KeyStoreSecurityLevel.java:57)
       at android.security.KeyStoreSecurityLevel.generateKey(KeyStoreSecurityLevel.java:145)
       at android.security.keystore2.AndroidKeyStoreKeyGeneratorSpi.engineGenerateKey(AndroidKeyStoreKeyGeneratorSpi.java:400)
       at javax.crypto.KeyGenerator.generateKey(KeyGenerator.java:612)
       at com.epicshaggy.biometric.NativeBiometric.generateKey(NativeBiometric.java:313)
       at com.epicshaggy.biometric.NativeBiometric.generateKey(NativeBiometric.java:295)
       at com.epicshaggy.biometric.NativeBiometric.getKey(NativeBiometric.java:324)
       at com.epicshaggy.biometric.NativeBiometric.encryptString(NativeBiometric.java:265)
       at com.epicshaggy.biometric.NativeBiometric.setCredentials(NativeBiometric.java:178)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:138)
       at com.getcapacitor.Bridge.lambda$callPluginMethod$0(Bridge.java:774)
       at com.getcapacitor.Bridge.$r8$lambda$ehFTi5f4HhVNFKTbCKAYDkpQYRA()
       at com.getcapacitor.Bridge$$ExternalSyntheticLambda3.run(:8)
       at android.os.Handler.handleCallback(Handler.java:938)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:211)
       at android.os.Looper.loop(Looper.java:300)
       at android.os.HandlerThread.run(HandlerThread.java:67)

Code:

const KEYSTORE_ALIAS = 'my-app-name';

setCredentials(credentials: Credentials): void {
      const {username, password} = credentials;
      this.deleteCredentials()
        .then(() => NativeBiometric.setCredentials({username, password, server: KEYSTORE_ALIAS}));
}

private deleteCredentials(): Promise<void> {
    return NativeBiometric.deleteCredentials({server: KEYSTORE_ALIAS});
}

Conclusion:

Any advice or a fix would be appreciated.

RRGT19 commented 1 year ago

After some debugging turns out that this exception is cause when the device has not configured any biometrics. We forgot to also check if it's enabled before trying to save the credentials.

Still, I understand that the generateKey method should return an error that can be trapped inside the catch instead of crashing the app.

wk-mihai commented 1 year ago

I have a similar problem on my Google Pixel 7, using Capacitor v5. I noticed that if I unlock the phone using face unlock, I can't use my fingerprint to log into the app. When I unlock the phone using my fingerprint or pin, the app authentication works fine.

It was fine in previous Capacitor version (v4)

Any solutions?

Thanks!

kito99 commented 1 year ago

We're running into the same problem. Here's another stack trace we're seeing on some devices:

09-18 20:46:07.940 E/AndroidRuntime(24230): FATAL EXCEPTION: CapacitorPlugins
09-18 20:46:07.940 E/AndroidRuntime(24230): Process: com.germania.mobile.app, PID: 24230
09-18 20:46:07.940 E/AndroidRuntime(24230): java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
09-18 20:46:07.940 E/AndroidRuntime(24230):  at com.getcapacitor.Bridge.lambda$callPluginMethod$0$com-getcapacitor-Bridge(Bridge.java:770)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at com.getcapacitor.Bridge$$ExternalSyntheticLambda5.run(Unknown Source:8)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at android.os.Handler.handleCallback(Handler.java:942)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at android.os.Handler.dispatchMessage(Handler.java:99)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at android.os.Looper.loopOnce(Looper.java:240)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at android.os.Looper.loop(Looper.java:351)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at android.os.HandlerThread.run(HandlerThread.java:67)
09-18 20:46:07.940 E/AndroidRuntime(24230): Caused by: java.lang.reflect.InvocationTargetException
09-18 20:46:07.940 E/AndroidRuntime(24230):  at java.lang.reflect.Method.invoke(Native Method)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:138)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at com.getcapacitor.Bridge.lambda$callPluginMethod$0$com-getcapacitor-Bridge(Bridge.java:761)
09-18 20:46:07.940 E/AndroidRuntime(24230):  ... 6 more
09-18 20:46:07.940 E/AndroidRuntime(24230): Caused by: java.security.ProviderException: Keystore key generation failed
09-18 20:46:07.940 E/AndroidRuntime(24230):  at android.security.keystore2.AndroidKeyStoreKeyGeneratorSpi.engineGenerateKey(AndroidKeyStoreKeyGeneratorSpi.java:413)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at javax.crypto.KeyGenerator.generateKey(KeyGenerator.java:612)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at com.epicshaggy.biometric.NativeBiometric.generateKey(NativeBiometric.java:313)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at com.epicshaggy.biometric.NativeBiometric.generateKey(NativeBiometric.java:295)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at com.epicshaggy.biometric.NativeBiometric.getKey(NativeBiometric.java:324)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at com.epicshaggy.biometric.NativeBiometric.encryptString(NativeBiometric.java:265)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at com.epicshaggy.biometric.NativeBiometric.setCredentials(NativeBiometric.java:178)
09-18 20:46:07.940 E/AndroidRuntime(24230):  ... 9 more
09-18 20:46:07.940 E/AndroidRuntime(24230): Caused by: android.security.KeyStoreException: System error (internal Keystore code: 4 message: In generate_key.
09-18 20:46:07.940 E/AndroidRuntime(24230): 
09-18 20:46:07.940 E/AndroidRuntime(24230): Caused by:
09-18 20:46:07.940 E/AndroidRuntime(24230):     0: In store_new_key.
09-18 20:46:07.940 E/AndroidRuntime(24230):     1: In store_new_key. Failed to handle super encryption.
09-18 20:46:07.940 E/AndroidRuntime(24230):     2: In handle_super_encryption_on_key_init: User ECDH key missing.
09-18 20:46:07.940 E/AndroidRuntime(24230):     3: Error::Rc(ResponseCode(4))) (public error code: 4 internal Keystore code: 4)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at android.security.KeyStore2.getKeyStoreException(KeyStore2.java:336)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at android.security.KeyStoreSecurityLevel.handleExceptions(KeyStoreSecurityLevel.java:57)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at android.security.KeyStoreSecurityLevel.generateKey(KeyStoreSecurityLevel.java:145)
09-18 20:46:07.940 E/AndroidRuntime(24230):  at android.security.keystore2.AndroidKeyStoreKeyGeneratorSpi.engineGenerateKey(AndroidKeyStoreKeyGeneratorSpi.java:400)
09-18 20:46:07.940 E/AndroidRuntime(24230):  ... 15 more

Further testing on many other devices reveals the following facts:

kito99 commented 1 year ago

I ended up refactoring the Android support to use EncryptedSharedPreferences. That seems to resolve the issue. See #103.