Android生物识别AES加密正常但解密抛出IllegalBlockSizeException求助
问题根因
1. 初始化向量(IV)未持久化,取值错误
AES-CBC模式要求解密必须使用加密阶段生成的同一个IV,你的代码仅将IV存储在类的成员变量initVector中,存在以下问题:
- 应用重启、单例对象被回收时,
initVector会被清空 - 多次调用加密方法时,后生成的IV会覆盖之前的IV,解密旧数据时IV不匹配
- 加密结果仅存储了密文,没有和IV绑定存储,跨场景解密时完全无法获取正确的IV
IV错误会直接导致解密后的块长度不符合要求,触发IllegalBlockSizeException。
2. 密钥读取逻辑错误
getOrCreateSecreteKey方法中,当密钥不存在调用generateSecretKey生成新密钥后,没有重新从KeyStore读取生成好的密钥,直接返回了null,会导致后续加解密拿到空密钥,抛出异常。
3. 单例模式下的IV冲突
使用单例存储全局IV,多业务场景同时加解密时,IV会被互相覆盖,导致解密失败。
修复方案
核心思路
加密时将生成的IV和密文拼接后再做Base64编码存储,解密时先从Base64解码后的字节数组前16位取出IV,剩余部分作为密文解密,不需要额外存储IV变量,同时修复密钥读取逻辑。
修复后的代码修改点
- 修复密钥读取逻辑
private SecretKey getOrCreateSecreteKey(String aliasKeyName) { try { KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); keyStore.load(null); SecretKey key = ((SecretKey)keyStore.getKey(aliasKeyName, null)); if(key == null){ // 生成密钥后重新读取返回 generateSecretKey(aliasKeyName); key = ((SecretKey)keyStore.getKey(aliasKeyName, null)); } return key; } catch (Exception e) { e.printStackTrace(); } return null; }
- 修改加密方法,拼接IV和密文
public String encryptData(String info, Cipher cipher){ try { byte[] encryptedData = cipher.doFinal(info.getBytes("UTF-8")); byte[] iv = cipher.getIV(); // 拼接规则:前16位是IV,后续字节为密文 byte[] combined = new byte[iv.length + encryptedData.length]; System.arraycopy(iv, 0, combined, 0, iv.length); System.arraycopy(encryptedData, 0, combined, iv.length, encryptedData.length); return Base64.encodeToString(combined, Base64.NO_WRAP); } catch (Exception e) { e.printStackTrace(); } return null; }
- 修改解密方法,先拆分IV再解密
public String decryptData(String stringToDecrypt, String aliasKeyName){ try { byte[] combined = Base64.decode(stringToDecrypt, Base64.NO_WRAP); // 拆分前16位为IV IvParameterSpec ivSpec = new IvParameterSpec(combined, 0, 16); // 读取密钥初始化解密Cipher KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); keyStore.load(null); SecretKey secretKey = ((SecretKey)keyStore.getKey(aliasKeyName, null)); Cipher cipher = getCipher(); cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec); // 解密16位之后的密文部分 byte[] decryptedData = cipher.doFinal(combined, 16, combined.length - 16); return new String(decryptedData, StandardCharsets.UTF_8); } catch (Exception e) { e.printStackTrace(); } return null; }
- 可删除全局的
initVector成员变量,以及原解密方法中读取该变量初始化IV的逻辑,避免取值错误。
内容的提问来源于stack exchange,提问作者Neo
相关产品推荐
相关产品推荐

