Android BiometricPrompt绑定加密抛IllegalBlockSizeException问题排查
你遇到的这个问题其实是加密和解密的Cipher初始化参数不匹配导致的,而且当开启setUserAuthenticationRequired(true)时,AndroidKeyStore对密钥的使用约束会更严格,这才引发了这个异常,而非你预期的UserNotAuthenticatedException。下面具体拆解原因和解决方案:
问题根源分析
参数不匹配导致解密失败
你的加密方法getEncodeCipher中,初始化Cipher时明确指定了OAEPParameterSpec参数:val spec = OAEPParameterSpec( "SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT ) cipher.init(Cipher.ENCRYPT_MODE, unrestricted, spec)但在解密方法
getDecodeCipher中,初始化Cipher时完全没有指定这个参数:cipher.init(Cipher.DECRYPT_MODE, key)当
setUserAuthenticationRequired(false)时,AndroidKeyStore可能会兼容默认参数,所以解密能正常工作;但开启用户验证要求后,系统强制要求加密和解密的参数必须完全一致,否则就会抛出IllegalBlockSizeException——因为此时密钥虽然通过了身份验证可以访问,但解密的块格式不符合加密时的约定。为什么不是UserNotAuthenticatedException?
这个异常只有在未通过身份验证就尝试使用受保护密钥时才会抛出。你的场景中,用户已经通过了BiometricPrompt的验证,密钥是合法可用的,只是解密参数不对,所以系统抛出的是解密格式相关的异常,而非身份验证异常。
解决方案
核心就是让加密和解密的Cipher初始化参数完全一致,修改你的getDecodeCipher方法,加入和加密时相同的OAEPParameterSpec:
override fun getDecodeCipher(keyAlias: String): Cipher { val keyStore: KeyStore = loadKeyStore() val key: PrivateKey = keyStore.getKey(keyAlias, null) as PrivateKey val cipher: Cipher = getCipherInstance() // 加入和加密时完全一致的OAEP参数 val spec = OAEPParameterSpec( "SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT ) cipher.init(Cipher.DECRYPT_MODE, key, spec) return cipher }
额外优化建议
- 你在加密时将KeyStore中的PublicKey转换为了"unrestricted"版本,其实完全可以直接使用
keyStore.getCertificate(keyAlias).publicKey初始化Cipher,不需要额外转换,这样能减少潜在的参数不一致问题。 - 确保密钥生成时的参数(比如加密填充、摘要算法)和加密解密时的参数完全匹配,你的
generateKey方法中已经指定了ENCRYPTION_PADDING_RSA_OAEP和DIGEST_SHA256,这部分是没问题的,保持一致即可。
这样修改后,再开启setUserAuthenticationRequired(true),解密操作就能正常通过BiometricPrompt验证后执行了。
内容的提问来源于stack exchange,提问作者Macs

