如 AES,DES 一类的分组对称加密算法要求明文数据的字节长度必须是其块大小的倍数,因此在加密明文数据之前我们必须对明文数据进行填充。
概述
在分组对称加密算法中,我们通常在加密之前在明文数据的末尾添加对应的填充数据使数据达到算法块大小的倍数;并且在解密数据之后将对应的填充内容去掉。
填充的方法具体来说分为基于二进制位 bit 的和基于字节 byte 的。其中基于字节的填充分为:ANSI X9.23,ISO 10126,PKCS#5 与 PKCS#7,零填充。本文中涉及到的字节数据均使用 16 进制表示。
本篇文章资料来源于 Padding (cryptography)。
二进制位填充(Bit Padding)
二进制位填充可以用于填充任意大小的明文数据。二进制位填充的原理是在数据的末尾的第一个二进制位填充 1
,然后在后面填充 0
,0
的数量由业务需要的填充位数决定。
二进制位填充可以填充以二进制位为单位的数据,也可以填充以字节为单位的数据。当它以字节为单位进行填充时,又称为 ISO/IEC 7816-4 填充,也就是填充内容类似 0x80 0x00
这样的填充。
下面是一个基于二进制位的填充案例,假设我们有一个 23 bit 的明文数据,需要填充 9 bit 到 32 bit,那么我们需要填充 1 bit 的 1
和 8 bit 的 0
:
1
2
3
4
# 明文数据
...... | 1011 1001 1101 0100 0010 011
# 填充后的数据
...... | 1011 1001 1101 0100 0010 011 1 0000 0000 |
对于字节填充来说,假设我们需要将数据填充 4 个 byte 达到 8 byte 的块大小,我们需要在数据末尾的第一个字节填充 0x80
,后面三个字节填充 0x00
:
1
2
3
4
# 明文数据
...... | DD DD DD DD DD DD DD DD | DD DD DD DD
# 填充后的数据
...... | DD DD DD DD DD DD DD DD | DD DD DD DD 80 00 00 00 |
ANSI X9.23
ANSI X9.23 是针对块大小为 8 的算法设计的填充模式,需要注意的是该标准已经被撤销。
在 ANSI X9.23 中,使用 0x01
到 0x08
对数据进行填充。对于填充内容可以使用任意随机数(一般来说固定使用 0x00
),并且将填充内容的最后一个字节设置为填充内容的长度。事实上我们可以很容易的将该标准应用于填充 0x01
到 0xFF
范围的数据块。
下面是块大小为 8 个字节,需要填充 4 个字节的内容:
1
2
3
4
# 明文数据
...... | DD DD DD DD DD DD DD DD | DD DD DD DD
# 填充后的数据
...... | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 04 |
注意:ANSI X9.23 对于刚好是块大小倍数的明文数据不进行填充。
ISO 10126
ISO 10126 目前也是已经撤销的标准,它在设计上和 ANSI X9.23 标准有些类似,均是通过随机数作为填充内容并在填充内容的最后一个字节指定填充内容的长度。
下面是块大小为 8 个字节,需要填充 4 个字节的内容:
1
2
3
4
# 明文数据
...... | DD DD DD DD DD DD DD DD | DD DD DD DD
# 填充后的数据
...... | DD DD DD DD DD DD DD DD | DD DD DD DD 81 A6 23 04 |
注意:ISO 10126 对于刚好是块大小倍数的明文数据也会进行填充1:
下面是块大小为 8 个字节,需要填充 8 个字节的内容:
1
2
3
4
# 明文数据
...... | DD DD DD DD DD DD DD DD |
# 填充后的数据
...... | DD DD DD DD DD DD DD DD | 98 EF 35 27 81 A6 23 08 |
PKCS#5 与 PKCS#7
PKCS#7 实际上是为了支持 AES 算法而对 PKCS#5 做的扩展,因为 PKCS#5 设计上只是针对 8 字节大小的块做填充的。也就是说,在实践中 PKCS#5 和 PKCS#7 是等同的。
PKCS#7 的 RFC 规范见 RFC 5652 - Section-6.3。
PKCS#7 的填充内容其实就是要填充的数据长度,如果要填充 N 个字节,那么每个字节都是 N 。需要添加的字节数取决于块的大小,当且仅当 N 小于 256 时,该填充方法是有效的。
填充的内容可能是下面的某一种:
1
2
3
4
5
6
7
01
02 02
03 03 03
04 04 04 04
05 05 05 05 05
06 06 06 06 06 06 06
......
下面是块大小为 8 个字节,需要填充 4 个字节内容;和块大小为 8 个字节,需要填充 8 个字节内容的案例:
1
2
3
4
5
6
7
8
9
# 明文数据
...... | DD DD DD DD DD DD DD DD | DD DD DD DD
# 填充后的数据
...... | DD DD DD DD DD DD DD DD | DD DD DD DD 04 04 04 04 |
# 明文数据
...... | DD DD DD DD DD DD DD DD |
# 填充后的数据
...... | DD DD DD DD DD DD DD DD | 08 08 08 08 08 08 08 08 08 |
这里如果数据长度已经是块大小的倍数了,还是需要填充块大小长度的填充内容。只有这样在解密的时候,算法才能确定的认为解密出来的数据的最后一位是填充的内容,并且填充的数据长度就是该字节的大小。否则,算法无法判断最后一个字节是填充值还是明文值,也就无法进行去掉填充内容的操作。
零填充
零填充是指将需要填充的所有字节都填充为零,该方案尚未被标准化。零填充也被叫做空填充或零字节填充。
下面是块大小为 8 个字节,需要填充 4 个字节内容的案例:
1
...... | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 00 |
注意:如果填充的明文数据本来就以一个或多个零字节接收,那么零填充在解密后可能无法被去填充,原因是解密算法无法区分零字节是明文数据的还是填充内容。一般而言,比较适用于填充二进制编码的字符串,在字符串中空字符 0x00
被看做是字符串结束的字节,是可以被剥离的。
如果数据长度已经是块大小的倍数了,视情况可以填充块大小长度的填充内容,这视具体的实现而定。
补充说明
-
此处存疑,仅个人理解 ↩