Updated on 十二月 26, 2019
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
总结起来就是,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实现
待续..