2、Android生物识别API概览:BiometricPrompt介绍、BiometricManager介绍、CryptoObject介绍

好,咱们直接进入正题。Android官方在Android 9(API 28)之后,把生物识别的API统一了。以前各家厂商各玩各的,指纹归指纹,人脸归人脸,代码写起来那叫一个痛苦。现在好了,一个BiometricPrompt搞定所有。

我个人习惯把这三个核心API分成三个角色来理解:检查员执行者保镖。你想想看,是不是很形象?

核心三件套速览:

  • BiometricManager — 检查设备是否支持、是否已录入
  • BiometricPrompt — 弹出系统对话框,执行认证
  • CryptoObject — 加密绑定,防止中间人攻击
Android 生物识别 API 核心三件套 生物识别认证 BiometricManager 检查员:能否用? BiometricPrompt 执行者:弹窗认证 CryptoObject 保镖:加密绑定 canAuthenticate() 判断硬件/录入状态 authenticate() 回调:成功/失败/错误 Cipher/Signature 密钥与认证结果绑定 三者配合:先检查 → 再认证 → 加密保护

2.1 BiometricManager — 先问问设备行不行

说白了,BiometricManager就是你的侦察兵。在弹出认证对话框之前,你得先确认三件事:设备有没有指纹/人脸硬件?系统版本够不够?用户有没有录入生物信息?

我记得有一次在项目里,没做这个检查就直接调了BiometricPrompt,结果在模拟器上直接崩了。嗯,从那以后我再也不敢跳过这一步了。

// 获取 BiometricManager 实例
val biometricManager = BiometricManager.from(context)

// 检查设备是否支持生物识别
when (biometricManager.canAuthenticate(
    BiometricManager.Authenticators.BIOMETRIC_STRONG
        or BiometricManager.Authenticators.DEVICE_CREDENTIAL
)) {
    BiometricManager.BIOMETRIC_SUCCESS ->
        // 可以正常使用
        Log.d("Biometric", "设备支持,可以认证")

    BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE ->
        // 没有硬件
        Log.e("Biometric", "设备没有生物识别硬件")

    BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE ->
        // 硬件暂时不可用
        Log.e("Biometric", "硬件暂时不可用,稍后再试")

    BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED ->
        // 用户没有录入
        Log.w("Biometric", "请先录入指纹或人脸")
}

💡 我的小建议:

检查结果如果是BIOMETRIC_ERROR_NONE_ENROLLED,最好引导用户去系统设置里录入。别直接弹个Toast就完事了,用户体验会很差。

2.2 BiometricPrompt — 真正的认证执行者

这个才是主角。BiometricPrompt会弹出一个系统级的对话框,风格统一,安全性高。你不需要自己画UI,系统帮你搞定。

你想想看,如果每个App都自己画一个指纹/人脸界面,那得多乱?而且很容易被钓鱼。系统对话框的好处是——用户一看就知道这是真的系统认证,不是假的。

// 创建 BiometricPrompt 实例
val biometricPrompt = BiometricPrompt(
    this, // FragmentActivity 或 Fragment
    ContextCompat.getMainExecutor(this),
    object : BiometricPrompt.AuthenticationCallback() {
        override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
            // 认证成功
            Log.d("Biometric", "认证通过!")
            // 可以在这里获取 CryptoObject
            val cryptoObject = result.cryptoObject
        }

        override fun onAuthenticationFailed() {
            // 认证失败(非致命,可重试)
            Log.w("Biometric", "认证失败,请重试")
        }

        override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
            // 认证错误(致命,不可重试)
            Log.e("Biometric", "认证错误: $errString")
        }
    }
)

// 配置认证参数
val promptInfo = BiometricPrompt.PromptInfo.Builder()
    .setTitle("验证身份")
    .setSubtitle("请使用指纹或人脸认证")
    .setDescription("用于登录您的账户")
    .setAllowedAuthenticators(
        BiometricManager.Authenticators.BIOMETRIC_STRONG
            or BiometricManager.Authenticators.DEVICE_CREDENTIAL
    )
    .setConfirmationRequired(false) // 人脸识别是否需要眨眼确认
    .build()

// 启动认证
biometricPrompt.authenticate(promptInfo)

⚠️ 避坑指南:

我曾经在onAuthenticationError里直接弹了一个Dialog,结果因为回调线程和UI线程的问题导致崩溃。记住:回调默认在主线程,但如果你用了自定义Executor,记得切回主线程再更新UI。

2.3 CryptoObject — 给认证加把锁

这个可能是初学者最容易忽略的。CryptoObject说白了就是把加密操作和生物识别绑定在一起。为什么要这么做?

假设你的App里有个功能:用户通过指纹后才能查看银行卡号。如果没有CryptoObject,黑客可以伪造一个"认证成功"的回调。但有了它,只有真正的生物识别通过后,才能拿到加密密钥去解密数据。

// 生成一个加密对象
val keyGenParameterSpec = KeyGenParameterSpec.Builder(
    "my_biometric_key",
    KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
    .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
    .setUserAuthenticationRequired(true) // 必须通过生物识别才能使用
    .setInvalidatedByBiometricEnrollment(true) // 新增指纹时失效
    .build()

val keyGenerator = KeyGenerator.getInstance(
    KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"
)
keyGenerator.init(keyGenParameterSpec)
keyGenerator.generateKey()

// 创建 Cipher 对象
val cipher = Cipher.getInstance(
    "${KeyProperties.KEY_ALGORITHM_AES}/${KeyProperties.BLOCK_MODE_CBC}/${KeyProperties.ENCRYPTION_PADDING_PKCS7}"
)
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val secretKey = keyStore.getKey("my_biometric_key", null) as SecretKey
cipher.init(Cipher.ENCRYPT_MODE, secretKey)

// 创建 CryptoObject
val cryptoObject = BiometricPrompt.CryptoObject(cipher)

// 使用 CryptoObject 进行认证
biometricPrompt.authenticate(promptInfo, cryptoObject)

🔐 关键点:

  • setUserAuthenticationRequired(true) — 密钥必须通过生物认证才能解锁
  • setInvalidatedByBiometricEnrollment(true) — 用户新增指纹/人脸后,旧密钥失效,防止绕过
  • 认证成功后,通过AuthenticationResult.getCryptoObject()拿到已解锁的加密对象

2.4 三者如何配合?

我习惯的流程是这样的:

  1. 先用BiometricManager检查 — 不行就直接降级或提示
  2. 准备好CryptoObject — 如果需要加密操作,提前生成密钥和Cipher
  3. 调用BiometricPrompt.authenticate() — 传入PromptInfo和可选的CryptoObject
  4. 在回调中处理结果 — 成功则使用CryptoObject解密,失败则提示重试

其实说白了,这三者缺一不可。少了BiometricManager,你可能会在没硬件的设备上崩溃;少了CryptoObject,你的认证结果可能被篡改;少了BiometricPrompt,你连认证对话框都弹不出来。

📌 我的经验:

如果你只是做一个"登录时验证指纹"的功能,可以不用CryptoObject。但如果是"查看敏感信息"或"支付确认",一定要加上。我在一个金融类项目里吃过亏,后来补上了CryptoObject才过审。


公众号:蓝海资料掘金营,微信deep3321