Flutter中如何解密由CryptoJS默认AES加密的字符串(已知密钥与密文)
解决Flutter中解密CryptoJS默认AES加密消息的问题
首先得搞清楚CryptoJS默认AES加密的底层逻辑——这是你卡壳的核心:
当你朋友用CryptoJS.AES.encrypt(明文, 密码)这种默认方式加密时,它并没有直接用你给的密码当密钥,而是悄悄做了这些操作:
- 自动生成一个8字节的随机盐
- 通过EVP_BytesToKey算法(基于MD5,仅迭代1次),用密码+盐生成真正的AES密钥(默认是256位,32字节)和IV(16字节)
- 用AES-CBC模式、PKCS7填充加密明文
- 最终输出的Base64字符串,解码后结构是:
Salted__(8字节固定头) + 盐(8字节) + 加密后的密文(剩余字节)
你的现有代码有几个关键错误:
- 直接把密码转Base64当作密钥,完全忽略了CryptoJS的密钥推导过程
- 错误地用密码当作IV,而IV其实是从密码+盐推导出来的
- 密文转换逻辑错了:你取了字符串的
codeUnits,但密文是Base64编码的,应该先Base64解码得到原始字节
下面是完整的解决方案步骤和可运行代码:
步骤1:解析加密后的密文字节
先把Base64格式的密文解码,拆分出盐和实际需要解密的密文内容:
import 'dart:convert'; import 'package:encrypt/encrypt.dart'; import 'package:crypto/crypto.dart'; // 需要添加crypto依赖到pubspec.yaml void main() { final encryptedStr = "U2FsdGVkX1851cYw0S6LX/xhUwdy0R/1AlNun5L9Ykc="; final password = "myKey111"; // 1. Base64解码密文得到原始字节数组 final encryptedBytes = base64.decode(encryptedStr); // 2. 验证格式(可选,确认是CryptoJS的标准输出) final saltedHeader = encryptedBytes.sublist(0, 8); if (utf8.decode(saltedHeader) != 'Salted__') { throw Exception("这不是CryptoJS默认格式的加密消息"); } // 3. 提取盐(第9-16字节)和实际密文(第17字节开始) final salt = encryptedBytes.sublist(8, 16); final actualCiphertext = encryptedBytes.sublist(16);
步骤2:实现EVP_BytesToKey生成密钥和IV
CryptoJS默认用MD5、1次迭代生成32字节密钥+16字节IV,咱们手动实现这个推导逻辑:
// 实现CryptoJS默认的EVP_BytesToKey算法 List<int> deriveKeyAndIV(String password, List<int> salt) { final passwordBytes = utf8.encode(password); List<int> derived = []; List<int> current = []; // 循环推导直到凑够32字节密钥 + 16字节IV的长度 while (derived.length < 32 + 16) { current = md5.convert(current + passwordBytes + salt).bytes; derived.addAll(current); } return derived; } final derivedData = deriveKeyAndIV(password, salt); final keyBytes = derivedData.sublist(0, 32); // 提取AES-256密钥 final ivBytes = derivedData.sublist(32, 32+16); // 提取IV
步骤3:用encrypt库完成解密
现在用推导好的密钥、IV和实际密文进行解密:
// 构建encrypt库需要的Key和IV对象 final key = Key.fromBytes(keyBytes); final iv = IV.fromBytes(ivBytes); // 构建Encrypted对象(传入实际密文字节,这是你之前搞错的点) final encrypted = Encrypted(actualCiphertext); // 初始化加密器(完全匹配CryptoJS默认配置:CBC模式、PKCS7填充) final encrypter = Encrypter(AES( key, mode: AESMode.cbc, padding: 'PKCS7', )); // 执行解密 final decrypted = encrypter.decrypt(encrypted, iv: iv); print("解密结果:$decrypted"); // 会输出你朋友加密的原始内容 }
额外注意事项
- 确保在
pubspec.yaml中添加依赖:dependencies: encrypt: ^5.0.1 # 建议使用最新稳定版 crypto: ^3.0.3 - CryptoJS默认是AES-256,如果你的朋友手动修改了密钥长度(比如AES-128),需要调整密钥提取长度为16字节而非32字节,但默认都是256位。
- 密文必须用Base64解码,绝对不能用字符串的UTF-8编码替代——这是你之前代码的核心错误之一。
内容的提问来源于stack exchange,提问作者C. Nutchanon
相关产品推荐
相关产品推荐

