Dart Java Swift AES CBC PKCS7Padding

使用Flutter开发,加密必不可少。

可选方案两种:1、Dart实现;2、原生实现。

既然要跨平台,那当然尽量用Dart实现,不然将来如果需要支持桌面、web还需要新的原生实现,麻烦。

试了pub上的几个高分的库,加密的结果和java都不一样。而且相关参数也不明所以。所以又不得不研究一下AES。

AES加密算法浅析

https://mp.weixin.qq.com/s/Q99jGZOUGFiM-ZTnkWWYew

这篇文章比较易懂,读完大致了解了AES算法,对几个要素也明确了。

秘钥

秘钥长度决定了加密的轮数,分为128bit,192bit,256bit,128性能最好,256安全性最高。

暂时选定的128bit。

填充(Padding)

AES是分组加密,每一组(块)是128bit,16字节,所以明文必须是16字节的整数倍。如果不是,则需要Padding。

大致有三种Padding:NoPadding、PKCS7Padding、ISO10126Padding。

这几种Padding好像是业界标准,不止可以用在AES,别的加密算法也可以使用。

NoPadding就是不用Padding,但是需要保证加密明文的长度是16字节的整数倍。

PKCS7Padding、ISO10126Padding是按照某种规则填充到16字节的整数倍。

暂时选定PKCS7Padding。

模式(mode of operation)

主要关注ECB、CBC这两种模式。

ECB:每个明文块加密独立完成,互不干涉。优点是各个块可以并行计算,缺点是安全性差。

CBC:引入初始向量(IV),初始向量会参与第一个明文块的异或,后续的每个明文块会和前一个明文块的秘闻块异或。由于不是互不干涉,所以无法并行计算,优点是更安全。

所以可以看出,IV的长度和明文块的长度是一样的,16字节(128bit)

关于模式,可以参看wiki

https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation

选定CBC。

Java实现

Java作为非常成熟的语言,库应有尽有。但是还是卡壳了。

使用时发现,Java没有PKCS7Padding,只有PKCS5Padding。

经过一番查找资料,发现资料非常少,有限的解决方案里发现了可以使用bouncycastle的库来实现。

https://blog.csdn.net/ngh8897/article/details/46008467

结果使用时发现,首次初始化的时候非常非常慢,大概1秒多。

于是继续在网上查找。

https://stackoverflow.com/questions/29232705/encrypt-text-to-aes-cbc-pkcs7padding

https://crypto.stackexchange.com/questions/9043/what-is-the-difference-between-pkcs5-padding-and-pkcs7-padding

总结起来就是,AES根本没有PKCS5Padding

PKCS5Padding、PKCS7Padding的区别就是,block size。PKCS5Padding定义的是8byte的block size,PKCS7Padding可以是1-255bytes。而AES的block是16byte,所以PKCS5Padding根本不能用在AES上。

Java官方文档似乎也没给出确定的文字。从网上的有限的资料来看,Java中PKCS5Padding就是PKCS7Padding,历史遗留,没有改名字。所以也不用引入三方库了,参数使用AES/CBC/PKCS5Padding即可。

Dart实现

在pub.dev上搜AES,试遍了几个高分库,加密结果都和Java不一致,而且和几个在线AES加密网站的加密结果也不一致,不知道什么原因。

几个原生交互的库倒是加密结果一致的,但是想的是尽量用纯dart的。就在要放弃,考虑选择别的加密方式时,看到一篇文章,他们使用的是pointycastle。我在pub.dev上看了一下,此库只有64分。抱着试试看的态度,试了一下,加密结果一致。于是选定了pointycastle来AES加密。

参考:

http://guoyz.top/2019/05/03/Flutter%E5%8A%A0%E8%A7%A3%E5%AF%86%E4%B9%8B%E6%AE%87/

经大神指出,pub上的高分AES是正确的,是我没用对。

库名称为encrypt。

我没算对是因为mode没赋值,默认的是sic。感谢大神。

    final key = Encrypt.Key.fromBase64(keyString);
    final iv = Encrypt.IV.fromBase64(ivString);


    final encrypter = Encrypt.Encrypter(Encrypt.AES(key, mode: Encrypt.AESMode.cbc));

    final encrypted = encrypter.encrypt(plainText, iv: iv);
    final decrypted = encrypter.decrypt(encrypted, iv: iv);

Swift实现

待续..

发表评论

电子邮件地址不会被公开。 必填项已用*标注