如何在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; }
你之前代码的问题说明
- 密钥派生逻辑错误:你使用了自定义的哈希链(PBKDF2->SHA256->MD5),但OpenSSL的
-pbkdf2默认用SHA-1哈希直接派生48字节的密钥+IV,逻辑完全不匹配 - 未遵循OpenSSL文件格式:没有生成
Salted__头和存储盐,导致解密时无法正确获取盐值 - IV生成错误:你手动生成随机IV但未与盐关联,而OpenSSL的IV是从同一PBKDF2派生流程中得到的,不是独立随机值
内容的提问来源于stack exchange,提问作者CCP
相关产品推荐
相关产品推荐

