我有一个 AES 128 CFB 加密字符串,需要解密它。
加密字符串:NpevHdSNWoXzXXzndH4WgRnM6QChqPwI8wguZ3iCOzGo4sG1RtHRiGGMkPcR2EaHOqJ22LQUcTM2BLKe6rQAdHJ58E/E/OpCY5wV45AYqJIgB2Yx4GbfeNeo0Do0AGhfVdbFFTybSElpJoLV dX5a4KWQVifKrw
密钥:bKxh4vz1WpDnMlK7
我有javascript函数,可以使用crypto-js库成功解密
const decryptURL = src => {
var key = 'bKxh4vz1WpDnMlK7';
var base64data = CryptoJS.enc.Base64.parse(src);
var encrypted = new CryptoJS.lib.WordArray.init(
base64data.words.slice(4),
base64data.sigBytes - 16,
);
var iv = new CryptoJS.lib.WordArray.init(base64data.words.slice(0, 4));
var cipher = CryptoJS.lib.CipherParams.create({ciphertext: encrypted});
var decrypted = CryptoJS.AES.decrypt(cipher, CryptoJS.enc.Utf8.parse(key), {
iv: iv,
mode: CryptoJS.mode.CFB,
padding: CryptoJS.pad.NoPadding,
});
return decrypted.toString(CryptoJS.enc.Utf8);
};
我尝试了很多解决方案,但仍然没有成功解密,下面是我的解密函数
extension StringEx on String {
Map<dynamic, dynamic> get decryptData {
final key = encrypt.Key.fromUtf8('bKxh4vz1WpDnMlK7');
final base64data = base64.decode(base64.normalize(this));
final encrypted = base64data.sublist(4, base64data.length - 16); // Assuming 16 bytes are the IV in CryptoJS
final iv = base64data.sublist(0, 4); // Assuming 16 bytes are the IV in CryptoJS
final encrypter = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cfb64, padding: null));
final decryptedBytes = encrypter.decrypt(encrypt.Encrypted(encrypted), iv: encrypt.IV(iv));
final dataMap = jsonDecode(decryptedBytes);
return dataMap;
}
}
任何帮助将不胜感激,非常感谢!
CryptoJS 和 Dart 代码不兼容,因为 1. 使用的段大小不同,2. Dart 代码与 CryptoJS 代码不同,只能处理长度为段大小整数倍的明文。
关于 1:对于 CFB 模式,始终存在关联的段大小。段大小是每个加密步骤加密的位数,并且通常附加到标识符,例如CFB-128 指定段大小为 128 位的 CFB。
CryptoJS 代码使用 128 位的段大小 (CFB-128),而 Dart 代码使用 64 位的段大小 (CFB-64)。两个库中的段大小都无法更改,因此在 Dart 端,encrypt 库无法用于解密 CryptoJS 代码的密文。
因此,在 Dart 方面,需要另一个也支持 CFB-128 的库。一种可能性是 PointyCastle(请注意,encrypt 库是 PointyCastle 子功能的包装器,因此实际上使用相同的库,只是直接使用)。
关于2:CFB模式是流密码模式,因此可以加密任意长度的明文,无需填充。
然而,PointyCastle(以及因此的encrypt库)的 CFB 实现只能加密长度为段大小整数倍的明文,即对于 CFB-128 为块大小的整数倍。
这通常需要额外的填充,这是低效的并且实际上不应该是必要的。
相反,由于这个限制,要使用PointyCastle解密CryptoJS代码的密文,必须在解密之前将密文填充到块大小的整数倍。
为此,可以使用任何填充,例如零填充(后者就足够了,因为有关填充字节的长度信息由密文的原始长度给出,因此不需要包含在填充中)。
解密后,明文必须被取消填充(即缩短为密文的原始长度)。
允许解密 CryptoJS 密文的 PointyCastle 的可能实现是:
import 'dart:convert';
import 'dart:typed_data';
import"package:pointycastle/export.dart";
...
final dataB64 = "NpevHdSNWoXzXXzndH4WgRnM6QChqPwI8wguZ3iCOzGo4sG1RtHRiGGMkPcR2EaHOqJ22LQUcTM2BLKe6rQAdHJ58E/E/OpCY5wV45AYqJIgB2Yx4GbfeNeo0Do0AGhfVdbFFTybSElpJoLVdX5a4KWQVifKrw";
print(dataB64.decryptData);
extension StringEx on String {
Map<dynamic, dynamic> get decryptData {
// Get key
final key = utf8.encode('bKxh4vz1WpDnMlK7');
// Separate IV and ciphertext
final base64data = base64.decode(base64.normalize(this));
final encrypted = base64data.sublist(16); // Assuming 16 bytes are the IV in CryptoJS
final iv = base64data.sublist(0, 16); // Assuming 16 bytes are the IV in CryptoJS
// Zero pad ciphertext
const aesBlockSize = 16;
final padLength = (aesBlockSize - (encrypted.length % aesBlockSize)) % aesBlockSize;
final cipherBytesPadded = Uint8List(encrypted.length + padLength)..setAll(0, encrypted);
// Decrypt
final params = ParametersWithIV(KeyParameter(key), iv);
final cipher = BlockCipher("AES/CFB-128")..init(false, params);
final plainBytesPadded = Uint8List(cipherBytesPadded.length);
for (var offset = 0; offset < cipherBytesPadded.length;) {
final len = cipher.processBlock(cipherBytesPadded, offset, plainBytesPadded, offset);
offset += len;
}
// Truncate plaintext to ciphertext size
final plainBytes = plainBytesPadded.sublist(0, encrypted.length);
return jsonDecode(utf8.decode(plainBytes)); // {error: Signature invalid, hash: 6c0ccf462bee20812224664ad0a8cc27, str: null10131696322798salt}
}
}