On this page
Biometrics on Android don't send fingerprints or face data to your app. The system verifies the user and tells your app the result. How you use that result determines security: as a UI gate, or as the key that unlocks cryptographic operations.
Two levels of biometric security
Setup and availability
dependencies { implementation("androidx.biometric:biometric:<version>") }fun canUseStrongBiometrics(context: Context): Boolean =
BiometricManager.from(context)
.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG) == BiometricManager.BIOMETRIC_SUCCESS
// Other results: BIOMETRIC_ERROR_NONE_ENROLLED (offer enrollment), NO_HARDWARE, HW_UNAVAILABLEβ¦| Authenticator | Meaning | Use with crypto? |
|---|---|---|
BIOMETRIC_STRONG | Class 3 biometrics (meet strong security requirements) | Yes |
BIOMETRIC_WEAK | Class 2, including some face unlock implementations | No |
DEVICE_CREDENTIAL | PIN, pattern, password | Yes, on newer Android versions |
Crypto-bound authentication (recommended)
class BiometricVault(private val activity: FragmentActivity) {
private val alias = "vault_key"
private fun key(): SecretKey {
val ks = KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
(ks.getKey(alias, null) as? SecretKey)?.let { return it }
val spec = KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG) // every use
.setInvalidatedByBiometricEnrollment(true)
.build()
return KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore").apply { init(spec) }.generateKey()
}
/** Decrypts [data] (IV + ciphertext) only after a successful strong biometric. */
fun decrypt(data: ByteArray, onResult: (ByteArray?) -> Unit) {
val cipher = Cipher.getInstance("AES/GCM/NoPadding").apply {
init(Cipher.DECRYPT_MODE, key(), GCMParameterSpec(128, data.copyOfRange(0, 12)))
}
val prompt = BiometricPrompt(activity, ContextCompat.getMainExecutor(activity),
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
val unlocked = result.cryptoObject?.cipher ?: return onResult(null)
onResult(unlocked.doFinal(data, 12, data.size - 12))
}
override fun onAuthenticationError(code: Int, msg: CharSequence) = onResult(null)
})
prompt.authenticate(
BiometricPrompt.PromptInfo.Builder()
.setTitle("Unlock your vault")
.setSubtitle("Confirm it's you")
.setNegativeButtonText("Cancel")
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
.build(),
BiometricPrompt.CryptoObject(cipher),
)
}
}Encrypting works the same way: initialise the cipher in ENCRYPT_MODE and store cipher.iv + ciphertext. Handle KeyPermanentlyInvalidatedException when init runs: delete the key and ask the user to sign in again.
Server-verified biometric confirmation
For high-value actions such as payment confirmation, generate an auth-bound signing key pair, register the public key with your server, then sign a server-issued challenge after BiometricPrompt. The server verifies the signature, which proves both the device and the user's presence.
UX guidelines
- Offer biometrics as an opt-in convenience after a normal sign-in, and explain what it protects.
- Provide a fallback (device credential or password) for users without biometrics, or when it fails.
- Don't prompt repeatedly. Use reasonable session timeouts for app-lock scenarios.
- Never claim biometric data is stored by your app. It isn't, and saying so confuses users.
Use cases
App lock
Crypto-bound unlock of locally encrypted data when opening a notes or finance app.
Confirm a transfer
Biometric-signed challenge verified by the server.
Quick sign-in
Decrypt a stored refresh token after biometric authentication. See Secure Token Storage.
Frequently asked questions
Can my app access fingerprint images?
No. Biometric data stays within secure hardware and the system. Apps receive only an authentication result.
Does face unlock count as strong?
It depends on the device implementation's class. Query BIOMETRIC_STRONG to know whether a strong method is available.
Should biometrics replace passwords?
Use biometrics to unlock locally stored credentials or confirm actions. Account recovery still needs another factor.
Conclusion
Use BiometricPrompt with BIOMETRIC_STRONG and a Keystore-bound CryptoObject, so authentication actually unlocks data or signs server challenges. Provide fallbacks, handle key invalidation, and keep biometrics as a convenient layer on top of solid account security.
Next step: protect user data in AI features with Protecting User Data in AI-Powered Android Apps.
Official references
π± Apps built by Yunok Tech
Real Android apps on Google Play, free to download.
NotiVaultNotification history: recover dismissed notificationsGet it βΈ
Walzi4K wallpapers for lock and home screenGet it βΈ
Ready to build real Android applications?
Security is easiest to get right when it is part of how you build from the first screen. Strengthen the Android fundamentals that secure, privacy-respecting apps are built on.
Move beyond theory and strengthen your Android development skills with Yunoktech's Android course. Learn core concepts, practical implementation, modern Android development techniques, and the reasoning behind building maintainable applications.
Explore the Android course and take your next step as an Android developer.