You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中如何解密由CryptoJS默认AES加密的字符串(已知密钥与密文)

解决Flutter中解密CryptoJS默认AES加密消息的问题

首先得搞清楚CryptoJS默认AES加密的底层逻辑——这是你卡壳的核心:
当你朋友用CryptoJS.AES.encrypt(明文, 密码)这种默认方式加密时,它并没有直接用你给的密码当密钥,而是悄悄做了这些操作:

  1. 自动生成一个8字节的随机盐
  2. 通过EVP_BytesToKey算法(基于MD5,仅迭代1次),用密码+盐生成真正的AES密钥(默认是256位,32字节)和IV(16字节)
  3. 用AES-CBC模式、PKCS7填充加密明文
  4. 最终输出的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 09:29:05