Android 8升级后创建密钥遇KeyStoreException需用户认证问题咨询
这个问题我在帮开发者排查Android 8兼容性问题时碰到过好几次,核心原因是Android 8(API 26)对KeyStore中受用户认证保护的密钥的创建逻辑做了细节调整,再加上部分厂商定制ROM的强化验证,才导致升级前正常的代码现在出问题。下面是具体的分析和解决方案:
问题根源
Android 8之前,创建需要用户认证的密钥时,系统允许先创建密钥,等到实际使用密钥时再要求用户完成认证;但从Android 8开始,部分设备要求在创建这类密钥的瞬间,用户必须已经处于认证状态(比如设备已经解锁,或者刚完成指纹验证),否则就会抛出User authentication required异常。另外,默认的用户认证有效期设置也可能导致这个问题。
具体解决方案
1. 明确设置用户认证有效期
在构建KeyGenParameterSpec时,添加setUserAuthenticationValidityDurationSeconds(-1),这个参数表示只要用户最近解锁过设备(指纹、密码或图案解锁都算),密钥就可以被使用,不需要每次创建或使用都实时触发认证。这个参数在API 23+就支持,Android 8上可以放心使用:
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder( alias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) // 根据你的需求添加加密填充、算法等参数 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP) .setUserAuthenticationRequired(true) // 设置有效期为"永久"(只要设备解锁过就可用) .setUserAuthenticationValidityDurationSeconds(-1) .build();
2. 确保创建密钥前用户已完成认证
如果你的业务逻辑允许,可以在调用密钥创建代码之前,先触发一次设备认证流程(比如弹出指纹验证对话框,或者检查设备是否处于解锁状态)。比如可以使用KeyguardManager检查设备状态:
KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); if (!keyguardManager.isDeviceSecure()) { // 提示用户设置锁屏密码/指纹 Intent intent = keyguardManager.createConfirmDeviceCredentialIntent("需要认证", "请完成设备认证以创建密钥"); startActivityForResult(intent, REQUEST_CODE_AUTH); } else if (!keyguardManager.isKeyguardLocked()) { // 设备已经解锁,直接创建密钥 createKey(); } else { // 设备锁定,触发认证 Intent intent = keyguardManager.createConfirmDeviceCredentialIntent("需要认证", "请完成设备认证以创建密钥"); startActivityForResult(intent, REQUEST_CODE_AUTH); }
然后在onActivityResult中,当用户完成认证后再调用密钥创建方法。
3. 调整密钥用途(按需精简)
你当前设置了密钥的四个用途:ENCRYPT | DECRYPT | SIGN | VERIFY,有些设备对多用途组合的密钥会有更严格的认证要求。如果你的业务不需要同时用到所有用途,可以拆分密钥或者精简用途,比如只保留SIGN | VERIFY用于指纹签名,单独用另一个密钥处理加密解密,这样可能避免触发异常。
4. 添加异常捕获与重试逻辑
在创建密钥的代码块中捕获KeyStoreException,当异常信息包含User authentication required时,引导用户完成认证后重试:
try { mKeyStore.load(null); // 构建KeyGenParameterSpec并创建密钥 KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore"); keyGenerator.init(spec); keyGenerator.generateKey(); } catch (KeyStoreException e) { if (e.getMessage() != null && e.getMessage().contains("User authentication required")) { // 触发用户认证,完成后重试创建密钥 triggerAuthenticationAndRetry(); } else { // 处理其他KeyStore异常 e.printStackTrace(); } } catch (Exception e) { // 处理其他通用异常 e.printStackTrace(); }
额外说明
这个问题不是所有设备都出现,主要是因为部分厂商(比如小米、华为)在Android 8的基础上对KeyStore的认证逻辑做了定制化修改,而原生Android 8的部分设备可能不会有这个问题。所以测试时一定要覆盖不同品牌的设备。
内容的提问来源于stack exchange,提问作者user9706380

