Flutter AES 加密对接

Blade 已结 2 2650
dp9212
dp9212 2021-04-06 19:11

一、该问题的重现步骤是什么?

1. Flutter 数据加密后提交到接口解密后出现解密不完整,使用AesUtil.encryptToBase64(....)加密数据后用Flutter提交接口正常解密。

2. 测试了各种数据长度,框架加密后的数据长度是44、88成倍增长,Flutter用的组件加密后数据长度会出现24、44、64、88的,猜测是IV(偏移向量)或者补码方式的问题,没学过密码学,整不明白

3. Flutter用的组件:https://pub.dev/packages/encrypt,加密代码是AES部分示例代码


二、你期待的结果是什么?实际看到的又是什么?

    怎么实现Flutter的AES加密对接呢?


三、你正在使用的是什么产品,什么版本?在什么操作系统上?

    bladex 2.8.0 ,windows 10


四、请提供详细的错误堆栈信息,这很重要。

(1)正确的数据是  {"auPhone":"10008376857","auPassword":"123456"},加解密的KEY确认没有写错。

(2)密钥:eELB8HbcgKWkndH0g9qRcBMZG5zgPkMc

(3)框架加密:BLo1Q73NNBWMxpqBF66sKrI+PYfikhD5YU2O0A+tkBThF1W3TC3zKFCIg/34cnckkEBbjp3+ZsCKsz3zziWFFA==

(4)Flutter组件加密:0yCTQv/5A7cWbPTZjXFWZWaWLmkvkpHpeZlxnCBWixmvBpbXSQ3gWg1i4s6l8/TW

(5)如果数据再多一位,FIutter组件加密出来就是88位的,但是和框架加密的不一样...

)DKJZ8D4_@@X$5{J@T4HF1K.jpg


五、若有更多详细信息,请在下面提供。


2条回答
  •  如梦技术
    2021-04-07 10:51

    AesUtil 使用得是 AES/CBC/NoPadding,而 dart 中默认是 PKCS7Padding,


    如果可以的话,将 java 的 AesUtil 的改成 AES/CBC/PKCS5Padding,详见:

    Java Cipher package only supports PKCS#7 padding with AES/CBC/PKCS5Padding

    This is not a good naming since PKCS#5 padding supports 8-byte block sizes as DES and PKCS#7 supports up to 255 bytes. For Java use this;

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");


    经过测试 java 端的代码需要调整,之前是为了兼容 的 PKCS7Padding。具体调整如下:

    // 去掉的 PKCS7Padding 编码
    public static byte[] encrypt(byte[] content, byte[] aesKey) {
       return aes(content, aesKey, Cipher.ENCRYPT_MODE);
    }
    
    // 去掉的 PKCS7Padding 解码
    public static byte[] decrypt(byte[] encrypted, byte[] aesKey) {
       return aes(encrypted, aesKey, Cipher.DECRYPT_MODE);
    }
    
    // 改为 AES/CBC/PKCS5Padding
    private static byte[] aes(byte[] encrypted, byte[] aesKey, int mode) {
       Assert.isTrue(aesKey.length == 32, "IllegalAesKey, aesKey's length must be 32");
       try {
          Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
          SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
          IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
          cipher.init(mode, keySpec, iv);
          return cipher.doFinal(encrypted);
       } catch (Exception e) {
          throw Exceptions.unchecked(e);
       }
    }

    dart 代码如下:

    void main(List arguments) {
      // 明文数据和密钥
      final plainText = '{"auPhone":"10008376857","auPassword":"123456"}';
      final plainKey = 'eELB8HbcgKWkndH0g9qRcBMZG5zgPkMc';
    
      // 初始化加密工具
      final key = Key.fromUtf8(plainKey);
      final iv = IV.fromUtf8(plainKey.substring(0, 16));
      final encrypter = Encrypter(AES(key, mode: AESMode.cbc));
    
      // dart 端加密
      final encrypted = encrypter.encrypt(plainText, iv: iv);
      print(encrypted.base64);
    
      // 解密 java 端密文
      final javaEncryptedText = 'BLo1Q73NNBWMxpqBF66sKrI+PYfikhD5YU2O0A+tkBRuiy1FkPxSnxUHw5rV2QZK';
      final decrypted = encrypter.decrypt64(javaEncryptedText, iv: iv);
    
      print(decrypted);
    }


    你把 dart 代码改成这样试试,这样输出的是:


    BLo1Q73NNBWMxpqBF66sKrI+PYfikhD5YU2O0A+tkBRuiy1FkPxSnxUHw5rV2QZK
    {"auPhone":"10008376857","auPassword":"123456"}


    这样,加密出来 dart 和 java 是一样的,加解密是都没问题了



    最新研究发现:java 中 AesUtil 只需要将 Pkcs7Encoder 中默认的 BLOCK_SIZE 改为 16 即可。


    dart 中默认为 16,java 的 bcprov-jdk15on 中也是默认的 16

    pc-dart/aes_fast.dart at e5cd4dd87d3a2b2f317b9a59b245978d92e965ee · bcgit/pc-dart (github.com) 


    作者追问:2021-04-07 10:51

    "AesUtil 使用得是 AES/CBC/NoPadding,iv 取的是密钥的前 16 位"

    这样测试过,IV写16个0和16个8也测试过,最好的情况就是解密出一半数据。

    麻烦大佬们有时候看看,项目进度卡这了

    作者追问:2021-04-07 10:51

    那如果 把 Pkcs7Encoder 中默认的 BLOCK_SIZE 改为 16,vue前端框架需要改吗?看了crypto.js里面的代码,没发现设置长度的地方,aes.js代码压缩了看不明白。

    目前已经按照第一种方式改写了工具类,加解密key从依赖包里面的bean获取,正常使用中,工具类有个好处是可以对单个参数加解密。

提交回复