You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用BIOMETRIC_WEAK存储AndroidKeyStore时遇UserNotAuthenticated错误

问题分析

核心原因是使用BIOMETRIC_WEAK时,BiometricPrompt认证未关联CryptoObject。Android Keystore明确要求:任何需要用户认证才能调用的密钥,必须在生物识别认证流程中通过CryptoObject将密钥操作与认证行为绑定,这样Keystore才会生成对应的认证令牌,允许后续的加密/解密操作。

你的代码中,BIOMETRIC_STRONG分支会生成Cipher并传入CryptoObject完成认证,但BIOMETRIC_WEAK分支直接调用biometricPrompt.authenticate(promptInfo),没有关联密钥操作,导致Keystore未收到认证令牌,后续使用密钥加密时就会抛出Key user not authenticated错误。

指纹能正常工作属于部分设备的兼容特例(部分厂商对指纹认证的令牌处理更宽松),但面部识别作为BIOMETRIC_WEAK的典型场景,严格遵循了Keystore的认证绑定要求,因此触发了规范报错。

解决方案

修改launchBiometricPromptEncrypt方法,统一为所有认证类型(包括BIOMETRIC_WEAK)绑定CryptoObject,移除STRONG和WEAK的分支判断:

public static void launchBiometricPromptEncrypt(BiometricPromptEncryptListener listener, Context context, String keyAlias, char[] value, int validationTimeout, BiometricPrompt.PromptInfo promptInfo) {

    ShowBiometricPromptActivityListener showBiometricPromptlistener = new ShowBiometricPromptActivityListener() {
        @Override
        public void onSuccessful() {

            BiometricPromptListener biometricPromptListener = new BiometricPromptListener() {
                @Override
                public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
                    try {
                        // 直接使用认证结果中绑定好令牌的Cipher,禁止重新初始化
                        Cipher cipher = result.getCryptoObject().getCipher();

                        String dat = CryptographicUtils.encryptData(value, cipher);
                        String iv = CryptographicUtils.getIV(cipher);
                        listener.onFinished(dat, iv);
                    } catch (Exception ex) {
                        listener.onError(new SiaKeystoreInternalError(SiaKeystoreInternalError.KEYSTORE_INTERNAL_ERROR, "", ex.getMessage()));
                    }
                }

                @Override
                public void onError(SiaKeystoreInternalError error) {
                    listener.onError(error);
                }
            };

            BiometricPrompt biometricPrompt = generateBiometricPrompt(biometricPromptListener, context);

            try {
                // 统一生成Cipher并绑定CryptoObject,所有认证类型共用此逻辑
                Cipher cipher = CryptographicUtils.getEncryptCipher(context, keyAlias, true, validationTimeout);
                BiometricPrompt.CryptoObject cryptoObject = new BiometricPrompt.CryptoObject(cipher);
                biometricPrompt.authenticate(promptInfo, cryptoObject);
            } catch ( Exception e) {
                NavigationUtils.finishHostTransparentFragmentActivity();
                listener.onError(new SiaKeystoreInternalError(SiaKeystoreInternalError.KEYSTORE_INTERNAL_ERROR, "", e.getMessage()));
            }
        }

        @Override
        public void onError(SiaKeystoreInternalError error) {
                listener.onError(error);
        }
    };

    showBiometricPromptActivity(showBiometricPromptlistener, context, promptInfo);

}
额外注意事项
  • 禁止重新初始化Cipher:认证成功后必须直接使用result.getCryptoObject().getCipher(),重新调用getEncryptCipher会生成未绑定认证令牌的Cipher,仍会触发相同错误。
  • 密钥参数一致性:生成密钥时的userAuthenticationValidityDurationSeconds参数,要和认证时传入的validationTimeout保持一致,确保密钥有效时长配置匹配。
  • StrongBox兼容性:当前代码中setIsStrongBoxBacked的逻辑无需修改,设备支持StrongBox时会自动适配,不影响认证绑定流程。

内容的提问来源于stack exchange,提问作者Víctor Martín

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 16:30:04