Files
flame_flip/lib/utils/aes_decrypt.dart
2026-06-10 22:28:22 +08:00

236 lines
6.1 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}