Files
flame_flip/lib/utils/aes_decrypt.dart

236 lines
6.1 KiB
Dart
Raw Normal View History

2026-06-10 22:28:22 +08:00
import 'dart:convert';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';
class AesDecrypt {
static const String _cn = 'i0.a';
static const List<int> _obf = <int>[
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<CipherParameters, CipherParameters>(
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<CipherParameters, CipherParameters>(
ParametersWithIV<KeyParameter>(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);
}
}