| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- import 'dart:convert';
- import 'dart:typed_data';
- import 'package:encrypt/encrypt.dart' as encrypt;
- import 'package:pointycastle/export.dart';
- import '../utils/hexUtils.dart';
- import 'cryptData.dart';
- // AES CBC 加密结果,存储 IV 和 加密数据
- class CryptCBCData {
- final CryptData iv;
- final CryptData encrypted;
- CryptData? salt;
- CryptCBCData({
- required this.iv,
- required this.encrypted,
- this.salt,
- });
- CryptCBCData.fromBase64({
- required String base64iv,
- required String base64encrypted,
- String? base64salt,
- }) : iv = CryptData.fromBase64(base64iv),
- encrypted = CryptData.fromBase64(base64encrypted),
- salt = base64salt != null ? CryptData.fromBase64(base64salt) : null;
- }
- /// ------------------- AES 编解码工具类 -------------------
- ///
- class AESUtils {
- /// ---------- CBC 模式 ----------
- /// 默认密钥长度(字节)
- static const int _defaultKeyLength = 32; // AES-256
- /// 默认 IV 长度(字节)
- static const int _ivLength = 16;
- /// 生成随机密钥
- /// [keyLength] 密钥长度:16(AES-128), 24(AES-192), 32(AES-256)
- static CryptData generateKey({int keyLength = _defaultKeyLength}) {
- if (![16, 24, 32].contains(keyLength)) {
- throw ArgumentError('密钥长度必须是 16、24 或 32 字节');
- }
- final secureRandom = SecureRandom('Fortuna');
- secureRandom.seed(KeyParameter(Uint8List.fromList(
- List.generate(32, (_) => DateTime.now().microsecond))));
- return CryptData(secureRandom.nextBytes(keyLength));
- }
- /// 生成随机 IV
- static CryptData generateIV() {
- final secureRandom = SecureRandom('Fortuna');
- secureRandom.seed(KeyParameter(Uint8List.fromList(
- List.generate(32, (_) => DateTime.now().microsecond))));
- return CryptData(secureRandom.nextBytes(_ivLength));
- }
- /// AES 加密
- /// [plainText] 明文
- /// [key] 密钥(16/24/32 字节)
- /// [iv] 初始化向量(16 字节),如果为空则自动生成
- /// 返回包含 IV 和密文的 Map,格式为 {'iv': iv_hex, 'encrypted': encrypted_base64}
- static CryptCBCData encryptCBC(String plainText, CryptData key, {CryptData? iv}) {
- try {
- // 验证密钥长度
- if (![16, 24, 32].contains(key.bytes.length)) {
- throw ArgumentError('密钥长度必须是 16、24 或 32 字节');
- }
- // 如果未提供 IV,则生成新的 IV
- final usedIv = iv ?? generateIV();
- // 验证 IV 长度
- if (usedIv.bytes.length != _ivLength) {
- throw ArgumentError('IV 长度必须是 16 字节');
- }
- // 创建加密器
- final encrypter = encrypt.Encrypter(
- encrypt.AES(
- encrypt.Key(key.bytes),
- mode: encrypt.AESMode.cbc,
- padding: 'PKCS7',
- ),
- );
- // 执行加密
- final encrypted = encrypter.encrypt(plainText, iv: encrypt.IV(usedIv.bytes));
- // 返回 IV 和密文(IV用十六进制,密文用base64)
- return CryptCBCData(
- iv: CryptData(usedIv.bytes),
- encrypted: CryptData(encrypted.bytes),
- );
- } catch (e) {
- throw Exception('AES encrypt failed: $e');
- }
- }
- /// AES 解密
- /// [encryptedText] 密文(base64 格式)
- /// [key] 密钥(16/24/32 字节)
- /// [iv] 初始化向量(16 字节)
- /// 返回解密后的明文字符串
- // static String decryptCBC(String encryptedText, Uint8List key, Uint8List iv) {
- static String decryptCBC(CryptCBCData encrypted, CryptData key) {
- try {
- // 验证密钥长度
- if (![16, 24, 32].contains(key.bytes.length)) {
- throw ArgumentError('key must be 16 or 24 or 32 bytes');
- }
- // 验证 IV 长度
- if (encrypted.iv.bytes.length != _ivLength) {
- throw ArgumentError('IV must be 16 bytes');
- }
- // 创建解密器
- final encrypter = encrypt.Encrypter(
- encrypt.AES(
- encrypt.Key(key.bytes),
- mode: encrypt.AESMode.cbc,
- padding: 'PKCS7',
- ),
- );
- // 执行解密
- final decrypted = encrypter.decrypt64(
- encrypted.encrypted.base64,
- iv: encrypt.IV(encrypted.iv.bytes),
- );
- return decrypted;
- } catch (e) {
- throw Exception('AES decrypt failed: $e');
- }
- }
- /// 简化的加密方法 - 自动处理密钥派生
- /// [plainText] 明文
- /// [password] 密码字符串,将用于派生密钥
- /// [salt] 盐值(可选,十六进制格式)
- /// 返回包含 IV、盐和密文的 CryptCBCData
- static CryptCBCData encryptCBCWithPassword(
- String plainText,
- String password,
- {String? salt}
- ) {
- try {
- // 如果没有提供盐值,则生成随机盐值(十六进制格式)
- final usedSalt = salt ?? bytesToHex(generateIV().bytes, include0x: false);
- // 从密码派生密钥(使用 PBKDF2)
- final derivedKey = _deriveKeyFromPassword(password, usedSalt);
- // 执行加密
- final result = encryptCBC(plainText, derivedKey);
- // 将盐值添加到结果中
- result.salt = CryptData.fromHex(usedSalt);
- return result;
- } catch (e) {
- throw Exception('AES CBC encrypt with password failed: $e');
- }
- }
- /// 简化的解密方法 - 使用密码派生密钥
- /// [encrypted] 包含 IV、盐和密文的 CryptCBCData
- /// [password] 密码字符串
- /// 返回解密后的明文字符串
- static String decryptCBCWithPassword(
- CryptCBCData encrypted,
- String password,
- ) {
- try {
- // 验证盐值是否存在
- if (encrypted.salt == null) {
- throw ArgumentError('Salt is required for password-based decryption');
- }
- // 将盐值转换为十六进制字符串
- final saltHex = bytesToHex(encrypted.salt!.bytes, include0x: false);
- // 从密码派生密钥
- final derivedKey = _deriveKeyFromPassword(password, saltHex);
- // 执行解密
- return decryptCBC(encrypted, derivedKey);
- } catch (e) {
- throw Exception('AES CBC decrypt with password failed: $e');
- }
- }
- /// 从密码派生密钥(使用 PBKDF2)
- static CryptData _deriveKeyFromPassword(String password, String salt, {int keyLength = 32}) {
- final pbkdf2 = PBKDF2KeyDerivator(HMac(SHA256Digest(), 64));
- final params = Pbkdf2Parameters(
- hexToBytes(salt),
- 100000, // 迭代次数
- keyLength,
- );
- pbkdf2.init(params);
- final key = Uint8List(keyLength);
- pbkdf2.deriveKey(Uint8List.fromList(utf8.encode(password)), 0, key, 0);
- return CryptData(key);
- }
- /// 验证密钥格式是否正确
- static bool isValidKey(Uint8List key) {
- return [16, 24, 32].contains(key.length);
- }
- /// 验证 IV 格式是否正确
- static bool isValidIV(Uint8List iv) {
- return iv.length == _ivLength;
- }
- /// ---------- ECB 模式 ----------
- /// * ECB 模式不安全,现在服务器使用,提供该方法,但不建议广泛使用
- /// *
- /// ECB 模式加密 - 使用 CryptData 密钥
- /// [plainText] 明文
- /// [key] 密钥(CryptData,16/24/32 字节)
- /// 返回 CryptData 格式的密文
- static CryptData encryptECB(String plainText, CryptData key) {
- try {
- // 验证密钥长度
- if (![16, 24, 32].contains(key.bytes.length)) {
- throw ArgumentError('key must be 16 or 24 or 32 bytes');
- }
- // 创建加密器(ECB 模式不需要 IV)
- final encrypter = encrypt.Encrypter(
- encrypt.AES(
- encrypt.Key(key.bytes),
- mode: encrypt.AESMode.ecb,
- padding: 'PKCS7',
- ),
- );
- // 执行加密并返回 CryptData 格式的密文
- final encrypted = encrypter.encrypt(plainText);
- return CryptData(encrypted.bytes);
- } catch (e) {
- throw Exception('AES ECB encrypt failed: $e');
- }
- }
- /// ECB 模式解密 - 使用 CryptData 密钥
- /// [encrypted] CryptData 格式的密文
- /// [key] 密钥(CryptData,16/24/32 字节)
- /// 返回解密后的明文字符串
- static String decryptECB(CryptData encrypted, CryptData key) {
- try {
- // 验证密钥长度
- if (![16, 24, 32].contains(key.bytes.length)) {
- throw ArgumentError('密钥长度必须是 16、24 或 32 字节(当前 ${key.bytes.length} 字节)');
- }
- // 创建解密器(ECB 模式不需要 IV)
- final encrypter = encrypt.Encrypter(
- encrypt.AES(
- encrypt.Key(key.bytes),
- mode: encrypt.AESMode.ecb,
- padding: 'PKCS7',
- ),
- );
- // 执行解密
- final decrypted = encrypter.decryptBytes(encrypt.Encrypted(encrypted.bytes));
- return utf8.decode(decrypted);
- } catch (e) {
- throw Exception('AES ECB 解密失败: $e');
- }
- }
- }
- // 废弃
- // CryptCBCData encryptAesCbc(String plainText, CryptData password) {
- // final iv = encrypt.IV.fromLength(16); // AES-CBC 必需的16字节IV
- // final key = encrypt.Key(password.byte);
- // final encrypter = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
- //
- // final encrypted = encrypter.encrypt(plainText, iv: iv);
- //
- // return CryptCBCData(iv: CryptData(iv.bytes), encrypted: CryptData(encrypted.bytes));
- // }
- //
- //
- // String decryptAesCbc(CryptCBCData encrypted, CryptData password) {
- // final iv = encrypt.IV(encrypted.iv.byte);
- // final key = encrypt.Key(password.byte);
- // final encrypter = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
- //
- // final decrypted = encrypter.decrypt(encrypt.Encrypted(encrypted.encrypted.byte), iv: iv);
- //
- // return decrypted;
- // }
|