Android 12/13调用Signature.initSign触发UserNotAuthenticatedException崩溃排查
在Android 12和13的生产环境中,调用Signature.initSign时出现如下崩溃:
android.security.keystore.UserNotAuthenticatedException: User not authenticated at android.security.keystore2.KeyStoreCryptoOperationUtils.getInvalidKeyException(KeyStoreCryptoOperationUtils.java:128) at android.security.keystore2.AndroidKeyStoreSignatureSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreSignatureSpiBase.java:217) at android.security.keystore2.AndroidKeyStoreSignatureSpiBase.engineInitSign(AndroidKeyStoreSignatureSpiBase.java:123) at android.security.keystore2.AndroidKeyStoreSignatureSpiBase.engineInitSign(AndroidKeyStoreSignatureSpiBase.java:101) at java.security.Signature$Delegate.init(Signature.java:1357) at java.security.Signature$Delegate.chooseProvider(Signature.java:1310) at java.security.Signature$Delegate.engineInitSign(Signature.java:1385) at java.security.Signature.initSign(Signature.java:679)
我的实现流程:
- 在Android Keystore中生成仅用于
KeyProperties.PURPOSE_SIGN的密钥对,配置UserAuthenticationRequired为true,invalidatedByBiometricEnrollment为false; - 需要签名时,从Keystore中取出私钥并调用
initSign(privateKey); - 启动BiometricPrompt,将初始化后的Signature作为Cipher对象的一部分传入;
- 用户成功认证后,对返回的Signature调用
Signature.update和Signature.sign。
原本预期用户认证失败或使用弱认证器时,崩溃会发生在步骤4,但实际崩溃出现在步骤2(调用initSign时)。此时用户尚未完成认证,而按流程必须先调用initSign才能传入BiometricPrompt。该崩溃在多款设备上出现,且受影响用户会持续触发,并非偶发边缘情况,但我在多款Android 12、13设备上无法本地复现,理论上流程是可行的。请问该问题的可能原因是什么?
1. 厂商定制的Keystore 2行为偏差
Android 12起默认启用Keystore 2,部分设备厂商对其认证触发逻辑做了定制。标准流程中initSign仅初始化操作上下文,不会立即校验用户认证,但部分厂商的实现可能强制在initSign阶段就检查认证状态,而非延迟到sign操作时,导致未认证时直接抛出异常。
2. 密钥配置缺少UserAuthenticationValidityDurationSeconds参数
生成密钥时仅设置UserAuthenticationRequired=true,但未指定UserAuthenticationValidityDurationSeconds(哪怕设为-1表示仅本次会话有效),部分Keystore 2实现可能将其判定为“必须立即完成认证”,而非允许延迟到操作执行时。虽然官方文档标注该参数默认值为-1,但部分厂商的实现存在默认值偏差,引发initSign阶段的认证校验。
3. 未明确指定允许的认证方式
生成密钥时如果未通过setAllowedUserAuthenticationMethods明确配置支持的认证类型(如生物识别、设备密码),部分设备的Keystore会默认要求“即时认证”,不允许通过BiometricPrompt后续触发认证,导致initSign因无有效认证上下文抛出异常。
4. 应用进程的认证会话已过期
如果应用在调用initSign前曾触发过用户认证,但进程后台存活过久导致会话过期,部分Keystore实现会判定当前进程无有效认证上下文,直接拒绝initSign操作。本地测试时进程生命周期较短,会话未过期,因此无法复现该场景。
5. BiometricPrompt与Keystore的交互逻辑差异
部分厂商定制的BiometricPrompt实现要求传入的Signature实例为“未初始化”状态,而非提前initSign后的对象。虽然标准流程允许提前初始化,但这类定制实现可能在绑定操作时提前触发Keystore的认证校验,间接导致initSign阶段抛出异常。
- 生成密钥时明确添加
setUserAuthenticationValidityDurationSeconds(-1),确保允许延迟认证; - 尝试将
initSign操作延迟到BiometricPrompt认证成功后执行,跳过提前初始化步骤; - 明确配置
setAllowedUserAuthenticationMethods为支持的类型,例如KeyProperties.AUTH_BIOMETRIC_STRONG | KeyProperties.AUTH_DEVICE_CREDENTIAL; - 捕获
UserNotAuthenticatedException,在异常触发时重新启动BiometricPrompt流程。
内容的提问来源于stack exchange,提问作者user1173706

