Search code examples
androidencryptionandroid-keystore

Protect private key by biometrics OR app-passwort on Android


For an app that signs data to be sent to a server, I'm researching ways to protect the private key used for signing. This private key's material should ideally never be exposed to the app and signing should be done in Android's trusted execution environment (TEE).

The app needs to protect access to the key by biometrics OR an app-password. App-password instead of device PIN/pattern/password because I can impose limits on the strength while device PIN/pattern/password may be weak.

When another biometric fingerprint/face was added, the app would alert the user and require the app-password to either sign data and allow re-enabling biometrics. The app-password would act as fallback and also authorization for users who do not want to use biometrics on their devices.

Target platform would be Android 8 and above.

I learned from here that the protection of the key material using biometrics is (relatively) straight forward:

  • Create the key using keystore instance "AndroidKeyStore"
  • Use setUserAuthenticationRequired(true) to protect the private key with biometric prompt.
  • Use setUserAuthenticationValidityDurationSeconds(-1) on the key to avoid allowing access to the key with a (potentially weak) device pattern/password/pin.
  • Use setInvalidatedByBiometricEnrollment(true) to invalidate the key when
    another fingerprint/face is enrolled to devices biometrics.
  • Finally, use the CryptoObject in the onAuthenticationSucceded callback to sign data.

The key material is never directly exposed to the app and sits nicely protected in the TEE.

Now to the part of protecting the private key using an app-password - I got struck there: Is there a way to password-protect the key (so we have a non-biometric fallback) AND have similar protection properties, i.e. private key material stays in secure TEE KeyStore and is not exposed to the app?


Solution

  • You can't do that, as that key-store has no passwords. The only option you'd have is to add 2 keys (they'd likely need to same key material); one with setUserAuthenticationRequired(true) and the other with setUserAuthenticationRequired(false) ...else you'd always need to provide biometric authentication to proceed, as this flag is not optional once set.