Flutter中使用pointycastle实现AES解密时出现RangeError求助
问题:Flutter中使用PointyCastle实现AES解密时出现RangeError
我是密码学领域的新手,正在尝试在Flutter中使用pointycastle(不打算更换包)实现AES加解密功能,但解密时出现问题。以下是我的代码:
Future<String> aesEncrypt(String plainText, key, iv) async { final convertedText = utf8.encode(plainText); final keyBytes = base64Decode(key); final ivbytes = base64Decode(iv); final keyParams = KeyParameter(keyBytes); final params = ParametersWithIV(keyParams, ivbytes); final cipher = BlockCipher('AES/CBC')..init(true, params); final paddedData = _padData(convertedText); final encryptedBytes = cipher.process(paddedData); return base64Encode(encryptedBytes); } Future<String> aesDecrypt(String encryptedText, key, iv) async { final data = base64Decode(encryptedText); final keyBytes = base64Decode(key); final ivbytes = base64Decode(iv); final keyParams = KeyParameter(keyBytes); final params = ParametersWithIV(keyParams, ivbytes); final cipher = BlockCipher('AES/CBC')..init(false, params); final decryptedBytes = cipher.process(data); final unpaddedData = _unpadData(decryptedBytes); return utf8.decode(unpaddedData); } Uint8List _padData(Uint8List data) { final blockSize = 16; final padLength = blockSize - (data.length % blockSize); final paddedData = Uint8List(data.length + padLength)..setAll(0, data); for (int i = data.length; i < paddedData.length; i++) { paddedData[i] = padLength; } return paddedData; } Uint8List _unpadData(Uint8List data){ final padLength = data[data.length - 1]; return Uint8List.sublistView(data, 0, data.length - padLength); } String generateKey(){ final random = math.Random.secure(); //Generate random key final keyBytes = Uint8List(16); for(int i = 0; i < keyBytes.length; i++){ keyBytes[i] = random.nextInt(256); } return base64Encode(keyBytes); } String generateIV(){ final random = math.Random.secure(); //Generate random key final ivBytes = Uint8List(16); for(int i = 0; i < ivBytes.length; i++){ ivBytes[i] = random.nextInt(256); } return base64Encode(ivBytes); }
(忽略生成密钥和IV的函数重复这一点),错误出现在_unpadData函数的return Uint8List.sublistView(data, 0, data.length - padLength);行,错误信息为:
Exception has occurred.
RangeError (RangeError (end): Invalid value: Not in inclusive range 0..16: -89)
原因分析
这个错误的核心是解密后的字节数据已损坏,导致读取到的padLength是异常值(比如超出1-16的合法范围),计算data.length - padLength时得到负数,超出了sublistView的合法索引范围。具体可能的诱因:
- Cipher实例化错误:直接用
BlockCipher('AES/CBC')字符串构造器可能没有正确初始化CBC模式,导致加解密的块处理逻辑不一致,解密出乱码数据。 - 密钥/IV不匹配:解密时使用的key或iv和加密时不一致,包括base64解码后的字节长度(AES密钥必须是16/24/32字节对应AES-128/192/256,IV必须是16字节)。
- 填充校验缺失:没有验证填充字节的合法性,当解密数据损坏时,直接使用异常的
padLength计算索引。
解决方案
修正Cipher实例化方式
改用PointyCastle的标准构造方式初始化CBC模式的AES加密器,避免字符串构造器的潜在问题:// 加密时 final cipher = CbcBlockCipher(AesEngine())..init(true, params); // 解密时 final cipher = CbcBlockCipher(AesEngine())..init(false, params);验证密钥和IV的一致性与合法性
确保加解密使用完全相同的key和iv,且解码后的字节长度符合要求:// 加密和解密时都可以添加校验 assert(keyBytes.length == 16 || keyBytes.length == 24 || keyBytes.length == 32, "Invalid AES key length"); assert(ivbytes.length == 16, "Invalid IV length");添加填充合法性校验
在_unpadData中先校验填充长度和填充字节的合法性,避免异常值导致的索引错误:Uint8List _unpadData(Uint8List data){ final padLength = data[data.length - 1]; // 校验填充长度是否在合法范围(1-16,AES块大小为16) if (padLength < 1 || padLength > 16 || padLength > data.length) { throw ArgumentError('Invalid padding length'); } // 校验所有填充字节是否等于padLength(符合PKCS7标准) for (int i = data.length - padLength; i < data.length; i++) { if (data[i] != padLength) { throw ArgumentError('Invalid padding bytes'); } } return Uint8List.sublistView(data, 0, data.length - padLength); }确认填充逻辑正确性
你的_padData实现是标准的PKCS7填充,当原数据长度为块大小整数倍时,会填充整个块的字节(16个0x10),这符合规范,无需修改。
内容的提问来源于stack exchange,提问作者Nick Haimovich
相关产品推荐
相关产品推荐

