AES和SM4
本来也不懂加密算法的,前段时间项目里需要用SM4来加密二维码的一部分。把学到的东西总结如下:
一、AES(Advanced Encryption Standard)
AES是一种对称加密算法,也就是加密和解密用同样的密钥, 密钥长度128位(16字节);同时它采用分组方式,分组长度也是128位(16字节)。AES支持5种模式:1)ECB无需指定偏移量iv;2)CBC需要指定偏移量,安全性优于ECB;3)CFB采用流模式,需要指定偏移量;4)OFB采用流模式,需要指定偏移量;5)CTR:需要指定偏移量。
二、SM4
SM4也是对称加密算法,是AES的国标版本,密钥长度也是128位(16字节);采用分组加密方式(block cipher),把明文按照确定的长度分为若干组,每组长度128位(16字节),然后分别进行加密,生成的密文与明文一样长。当最后一个分组长度不够16字节时,采用指定的填充方式进行填充,常用的填充方式为pcs7padding,即根据需要填充的位数确定填充内容,如相比16字节差3个字节,则填充3个3。另外编码后的结果需要指定编码方式,常用编码方式是base64。
2.1 SM4算法的基础部件
SM4的处理单位是字节和字(32位),速度和实现都优于位bit。
1)S盒:相当于密码表,是一个16*16的表格;对于输入68(两位16进制数字),则查找S盒中6行8列的数据,得到对应的密文。此时输入为1字节,每个16进制数字用4位表示,输出也是1个字节,因此长度不变。对于字,一个字包含4字节,并行进行S盒置换;S盒变换用t表示。
2)线性变换L:对于S盒,如果我改了明文中的一个字节,那么密文也是只对应修改一个字节,但我希望一个小的改动能使整个密文发生大的变化,以增加破解的难度,这里就用到L变换。C=L(B)=B@(B<<<2)@(B<<<10)@(B<<<18)@(B<<<24);B是S盒变换后的结果;@是异或,<<<代表循环左移,后面的数字的左移的位数。
2.2 SM4算法进阶
1)T变换:先进行t变换即S盒变换,再进行线性L变换。
2)轮函数F变换:输入是明文和四字节的轮密钥;首先对明文分组,每组16字节(四个字);然后各组分别和轮密钥进行轮函数变换。轮变化以字为单位,如明文分组后第一组包含4个字X0,X1,X2,X3;具体步骤如下:1)对X1、X2、X3、轮密钥进行异或操作得到A1;2)A1通过S盒变换得到B1;3)B1进行L变换得到C1;4)C1和X0异或得到结果。
因此,SM4的整体加密算法就是对一组密文X0,X1,X2,X3进行F变换,结果存储到X4;对X1,X2,X3,X4进行F变换,结果存储到X5;进行32次轮变换,生成X32,X33,X34,X35。生成的密文就是X35,X34,X33,X32。每次轮变换的密钥是不同的。
这里的轮密钥,通常由用户选择主密钥,根据SM4算法生成轮密钥。
三、SM4算法的实现与验证
我的项目中使用的是Java语言,要在代码中实现SM4加密,并没有现成的library可以引用,我的做法是把网上的SM4代码示例拷贝到项目的代码库,并编写测试验证加密结果。我拷贝的是百度百科中附录的SM4的代码实现,没有什么问题,可以放心用,只是测试通过之后把代码中的冗余稍微优化了一下。
另外,这个SM4的线上编解码器也能帮助我们验证编码结果的正确性:https://aks.jd.com/tools/sec/。不过这个工具有一个小bug,就是选择对称加解码之后如果直接使用SM4编解码功能,编码结果是有问题的,需要先点击一下SM4旁边的AES,然后再点击回到SM4,才能正确的使用SM4的编解码功能,猜测是指示当前编解码模式的变量初始值设置的有点问题吧。瑕不掩瑜,这个工具还是挺好用的。