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

如何在Flutter中实现与OpenSSL AES-256-CBC格式一致的加解密功能?

实现与OpenSSL aes-256-cbc兼容的Flutter加解密函数

要匹配你给出的OpenSSL命令openssl aes-256-cbc -d -salt -pbkdf2 -iter 10000的加解密逻辑,需要严格遵循OpenSSL的格式规范,以下是具体实现方案:

核心逻辑对应

OpenSSL的这个命令遵循以下规则:

  • 加密时生成8字节随机盐
  • 通过PBKDF2算法(基于SHA-1哈希,10000次迭代),从密码+盐派生48字节数据:前32字节作为AES-256密钥,后16字节作为CBC模式的IV
  • 加密内容采用PKCS7填充
  • 加密文件格式:Salted__(8字节ASCII头) + 8字节盐 + 加密后的原始数据
  • 解密时先读取文件头的盐,再用相同PBKDF2流程派生密钥和IV,最后解密

依赖配置

在pubspec.yaml中添加所需依赖:

dependencies:
  pointycastle: ^3.7.3
  path: ^1.8.3

完整加解密实现

import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';
import 'package:path/path.dart' as path;

// 辅助函数:用PBKDF2派生密钥和IV
Uint8List _deriveKeyAndIV(String password, Uint8List salt) {
  final pbkdf2 = PBKDF2KeyDerivator(HMac(SHA1Digest(), 64));
  final params = Pbkdf2Parameters(salt, 10000, 48); // 48字节:32密钥+16IV
  pbkdf2.init(params);
  return pbkdf2.process(utf8.encode(password));
}

// AES-CBC加密函数,生成OpenSSL兼容格式文件
static Future<File?> aesEncrypt(String password, File file) async {
  // 生成8字节随机盐
  final salt = Uint8List(8);
  final secureRandom = FortunaRandom();
  secureRandom.seed(KeyParameter(secureRandom.nextBytes(32)));
  secureRandom.nextBytes(salt);

  // 派生密钥和IV
  final keyIV = _deriveKeyAndIV(password, salt);
  final key = keyIV.sublist(0, 32);
  final iv = keyIV.sublist(32, 48);

  // 初始化AES-CBC加密器
  final cipher = PaddedBlockCipher('AES/CBC/PKCS7');
  cipher.init(true, PaddedBlockCipherParameters(
    ParametersWithIV(KeyParameter(key), iv),
    null,
  ));

  // 读取文件原始数据并加密
  final fileBytes = await file.readAsBytes();
  final encryptedBytes = cipher.process(fileBytes);

  // 组装OpenSSL格式的加密数据:Salted__ + 盐 + 加密内容
  final header = utf8.encode('Salted__');
  final outputBytes = Uint8List(header.length + salt.length + encryptedBytes.length)
    ..setAll(0, header)
    ..setAll(header.length, salt)
    ..setAll(header.length + salt.length, encryptedBytes);

  // 生成输出文件
  final outputPath = '${file.path}.enc';
  final outputFile = File(outputPath);
  await outputFile.writeAsBytes(outputBytes);

  // 保留你的文件保存逻辑
  await FileServices.saveFile(outputFile.path);
  return outputFile;
}

// AES-CBC解密函数,解析OpenSSL格式文件
static Future<File?> aesDecrypt(String password, File file) async {
  final fileBytes = await file.readAsBytes();

  // 验证文件头并提取盐
  final header = utf8.encode('Salted__');
  if (fileBytes.length < header.length + 8) {
    throw Exception('无效的加密文件格式');
  }
  final fileHeader = fileBytes.sublist(0, header.length);
  if (!const ListEquality().equals(fileHeader, header)) {
    throw Exception('不是OpenSSL格式的加密文件');
  }
  final salt = fileBytes.sublist(header.length, header.length + 8);
  final encryptedData = fileBytes.sublist(header.length + 8);

  // 派生密钥和IV
  final keyIV = _deriveKeyAndIV(password, salt);
  final key = keyIV.sublist(0, 32);
  final iv = keyIV.sublist(32, 48);

  // 初始化AES-CBC解密器
  final cipher = PaddedBlockCipher('AES/CBC/PKCS7');
  cipher.init(false, PaddedBlockCipherParameters(
    ParametersWithIV(KeyParameter(key), iv),
    null,
  ));

  // 解密数据
  final decryptedBytes = cipher.process(encryptedData);

  // 生成输出文件
  final outputPath = path.withoutExtension(file.path);
  final outputFile = File(outputPath);
  await outputFile.writeAsBytes(decryptedBytes);

  // 保留你的文件保存逻辑
  await FileServices.saveFile(outputFile.path, extraName: 'DECRYPTED');
  return outputFile;
}

你之前代码的问题说明

  1. 密钥派生逻辑错误:你使用了自定义的哈希链(PBKDF2->SHA256->MD5),但OpenSSL的-pbkdf2默认用SHA-1哈希直接派生48字节的密钥+IV,逻辑完全不匹配
  2. 未遵循OpenSSL文件格式:没有生成Salted__头和存储盐,导致解密时无法正确获取盐值
  3. IV生成错误:你手动生成随机IV但未与盐关联,而OpenSSL的IV是从同一PBKDF2派生流程中得到的,不是独立随机值

内容的提问来源于stack exchange,提问作者CCP

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 23:10:36