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; // }