AES加密填充的思考

924 阅读3分钟

AES加密填充的思考

在使用AES的一个项目中,遇到了加密填充的问题,前端使用的AES库为crypto-js,后端使用的则是javax.crypto.Cipher。前端默认使用的填充模式为Pkcs7,但在JAVA并没有实现Pkcs7填充。于是便面临了一个填充模式的问题。使用不同的填充模式带来的后果便是解密出的数据会带有乱码。这便引发了我对加密填充的思考。

为什么需要填充?

在AES算法中,对明文的加密要求按照AES的KEY的长度对齐,叫做块大小即 BlockSize,以AES-128为例,它的秘钥长度就是128bit(16个字节),也就是说明文会分割成一个又一个的128bit的块,之后AES再对这些块分别进行加密。这样就面临了一个问题,若一个明文或者一个明文分割后的块大小小于块的大小,那么便无法进行加密,因此我们需要对明文进行填充。

我们填充到了哪里?

正如上节所说,明文被分割成了一个又一个的128bit的块,假设我们对明文"加密我"进行加密,那么在UTF-8的情况下,一个汉字占用了三个字节,即占用了24个bit,那么我们的明文就占用了3*24=72bit,还剩下128-72=56bit的大小没有内容,这56个bit便是我们需要进行填充的位置。

几种填充的模式:

AES/CBC/NoPadding

NoPadding 名如其填充,它并没有对明文进行填充,也就是说没有对明文进行任何的修改,因此在明文长度和块大小长度不一致时便会报错。

AES/CBC/ZeroPadding

ZeroPadding填充,对需要填充的内容全部填充为0,在上述例子中表现为剩下的56个bit全部为0。

AES/CBC/Pkcs7Padding

Pkcs7Padding 填充,每个字符填充的值是需要填充的字节数,在上述例子中,56/8=7字节,因此若选用Pkcs7Padding填充,则剩下七个字节全部填充为7。

AES/CBC/Pkcs5Padding

Pkcs5Padding 填充和Pkcs7Padding填充的运算方式相同,差异点在于Pkcs5Padding只能用来填充8字节的块大小。但这并不意味着二者直接进行互换没有问题

Pkcs5Padding和Pkcs7Padding填充的是什么?

一个字节便是16个bit,因此在UTF-8中,Pkcs7Padding只能填充第0-15位,而Pkcs5Padding因为只能处理8字节的块所以能填充的内容则是0-7位,这些都是未定义的,在后端可以用trim()去掉

为什么Pkcs5和Pkcs7互换会有问题?

在Pkcs7Padding中填充的块大小并不限于8字节,但Pkcs5Padding只限于8字节,因此使用相同的明文加密可能会导致最后块的填充部分值不同,导致解密错误。

但这并不意味着完全不能使用,因为Pkcs7是包含Pkvs5的。

因此Pkcs5可以转换为Pkcs7

但是Pkcs7不一定可以准换到Pkcs5,用Pkcs7解密出来的Pkcs5可能会是错误的。

参考资料:

加密演算法要注意的那些毛

Padding WIKI

AES在线加密工具

AES在线加密工具

UTF-8字符查询