import 'dart:convert'; import 'dart:typed_data'; import 'package:pointycastle/export.dart'; class AesDecrypt { static const String _cn = 'i0.a'; static const List _obf = [ 0xED, 0x7A, 0x7F, 0x3F, 0x9E, 0x6E, 0xE7, 0xF9, 0x06, 0xA0, 0xA8, 0xE4, 0x16, 0xE5, 0x69, 0x70, ]; /// Java String.hashCode() 等价实现(32-bit signed) static int javaStringHashCode(String str) { int h = 0; for (int i = 0; i < str.length; i++) { h = _toSigned32(h * 31 + str.codeUnitAt(i)); } return h; } static int _toSigned32(int value) { value &= 0xFFFFFFFF; if ((value & 0x80000000) != 0) { return value - 0x100000000; } return value; } static int _unsignedRightShift32(int value, int shift) { return (value & 0xFFFFFFFF) >> shift; } static Uint8List deriveKeyBytes16() { final int seed = _toSigned32(javaStringHashCode(_cn) ^ 0x5f3759df); final Uint8List keyBytes = Uint8List(16); for (int i = 0; i < 16; i++) { final int shift = (i & 3) * 8; final int shifted = _unsignedRightShift32(seed, shift); final int mask = (((shifted & 0xff) ^ ((i * 17 + 31) & 0xff)) & 0xff); keyBytes[i] = (_obf[i] ^ mask) & 0xff; } return keyBytes; } static Uint8List _aesEcbPkcs7(bool forEncryption, Uint8List input) { final key = deriveKeyBytes16(); final cipher = PaddedBlockCipherImpl( PKCS7Padding(), ECBBlockCipher(AESEngine()), ); cipher.init( forEncryption, PaddedBlockCipherParameters( KeyParameter(key), null, ), ); return cipher.process(input); } /// 对应 JS / Java encrypt(String) -> Base64.NO_WRAP static String encrypt(String plainText) { try { final Uint8List input = Uint8List.fromList(utf8.encode(plainText)); final Uint8List encrypted = _aesEcbPkcs7(true, input); return base64.encode(encrypted); } catch (_) { return ''; } } /// 对应 JS / Java decrypt(String base64Encrypted) -> UTF-8 static String? decrypt(String base64Encrypted) { try { final Uint8List encrypted = Uint8List.fromList( base64.decode(base64Encrypted), ); final Uint8List decrypted = _aesEcbPkcs7(false, encrypted); final String text = utf8.decode(decrypted, allowMalformed: false); return text.isNotEmpty ? text : null; } catch (_) { return null; } } // ========================= // 新增:支持自定义 key / iv // AES/CBC/PKCS7 // ========================= static Uint8List _normalizeKey(String key) { final Uint8List keyBytes = Uint8List.fromList(utf8.encode(key)); if (keyBytes.length != 16 && keyBytes.length != 24 && keyBytes.length != 32) { throw ArgumentError('AES key length must be 16/24/32 bytes.'); } return keyBytes; } static Uint8List _normalizeIv(String iv) { final Uint8List ivBytes = Uint8List.fromList(utf8.encode(iv)); if (ivBytes.length != 16) { throw ArgumentError('AES CBC iv length must be 16 bytes.'); } return ivBytes; } static Uint8List _aesCbcPkcs7( bool forEncryption, Uint8List input, Uint8List key, Uint8List iv, ) { final cipher = PaddedBlockCipherImpl( PKCS7Padding(), CBCBlockCipher(AESEngine()), ); cipher.init( forEncryption, PaddedBlockCipherParameters( ParametersWithIV(KeyParameter(key), iv), null, ), ); return cipher.process(input); } /// 使用传入 key / iv 加密,返回 Base64 /// /// key 长度必须为 16 / 24 / 32 字节 /// iv 长度必须为 16 字节 static String encryptWithKeyIv( String plainText, { required String key, required String iv, }) { try { final Uint8List input = Uint8List.fromList(utf8.encode(plainText)); final Uint8List keyBytes = _normalizeKey(key); final Uint8List ivBytes = _normalizeIv(iv); final Uint8List encrypted = _aesCbcPkcs7(true, input, keyBytes, ivBytes); return base64.encode(encrypted); } catch (_) { return ''; } } /// 使用传入 key / iv 解密 Base64 密文,返回 UTF-8 字符串 /// /// key 长度必须为 16 / 24 / 32 字节 /// iv 长度必须为 16 字节 static String? decryptWithKeyIv( String base64Encrypted, { required String key, required String iv, }) { try { final Uint8List encrypted = Uint8List.fromList( base64.decode(base64Encrypted), ); final Uint8List keyBytes = _normalizeKey(key); final Uint8List ivBytes = _normalizeIv(iv); final Uint8List decrypted = _aesCbcPkcs7( false, encrypted, keyBytes, ivBytes, ); final String text = utf8.decode(decrypted, allowMalformed: false); return text.isNotEmpty ? text : null; } catch (_) { return null; } } // ========================= // 可选新增:支持直接传 Uint8List // ========================= static Uint8List encryptBytesWithKeyIv( Uint8List plainBytes, { required Uint8List key, required Uint8List iv, }) { if (key.length != 16 && key.length != 24 && key.length != 32) { throw ArgumentError('AES key length must be 16/24/32 bytes.'); } if (iv.length != 16) { throw ArgumentError('AES CBC iv length must be 16 bytes.'); } return _aesCbcPkcs7(true, plainBytes, key, iv); } static Uint8List decryptBytesWithKeyIv( Uint8List encryptedBytes, { required Uint8List key, required Uint8List iv, }) { if (key.length != 16 && key.length != 24 && key.length != 32) { throw ArgumentError('AES key length must be 16/24/32 bytes.'); } if (iv.length != 16) { throw ArgumentError('AES CBC iv length must be 16 bytes.'); } return _aesCbcPkcs7(false, encryptedBytes, key, iv); } }