AES 128 CFB解密Flutter/dart

问题描述 投票:0回答:1

我有一个 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;
  }
}

任何帮助将不胜感激,非常感谢!

flutter dart encryption aes cryptojs
1个回答
0
投票

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}
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.