分组密码要求明文长度为分组大小的整数倍,如AES分组大小为16字节。若不满足该要求,则需要将最后一个明文分组进行填充。以下为几种主流填充方法。
pkcs7
PKCS7是几乎各个加密算法都遵循的填充算法,比如OpenSSL就使用它作为默认填充算法。
文档:RFC 2315: PKCS #7: Cryptographic Message Syntax Version 1.5 (rfc-editor.org)
比如AES(分组长度16字节)加密"Unaligned",该字符串长9字节,还需要7字节,于是在后面追加7字节0x07。
from Crypto.Util.Padding import pad, unpad
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from binascii import b2a_hex
data = b'Unaligned' # 9 bytes
key = get_random_bytes(32)
iv = get_random_bytes(16)
cipher1 = AES.new(key, AES.MODE_CBC, iv)
pad_data = pad(data, 16)
print(b2a_hex(pad_data))
# b'556e616c69676e656407070707070707'
ct = cipher1.encrypt(pad_data)
cipher2 = AES.new(key, AES.MODE_CBC, iv)
pt = unpad(cipher2.decrypt(ct), 16)
print(b2a_hex(pt))
# b'556e616c69676e6564'
再举两个例子:
from Crypto.Util.Padding import pad, unpad
from binascii import b2a_hex
data = b'Unaligned0'
style = "pkcs7"
pad_data = pad(data, 16, style)
print(b2a_hex(pad_data))
# b'556e616c69676e656430060606060606'
print(b2a_hex(unpad(pad_data, 16)))
# b'556e616c69676e656430'
data = b'0123456789012345'
pad_data = pad(data, 16, style)
print(b2a_hex(pad_data))
# b'3031323334353637383930313233343510101010101010101010101010101010'
print(b2a_hex(unpad(pad_data, 16, style)))
# b'30313233343536373839303132333435'
可以看出,填充后的最后一个字节一定是填充量。
逆过程unpad则需要注意进行各种检查:
pdata_len = len(padded_data)
if pdata_len == 0:
raise ValueError("Zero-length input cannot be unpadded")
if pdata_len % block_size:
raise ValueError("Input data is not padded")
padding_len = bord(padded_data[-1])
if padding_len<1 or padding_len>min(block_size, pdata_len):
raise ValueError("Padding is incorrect.")
return padded_data[:-padding_len]
还有一种填充方法叫做PKCS5,其实就是分组长度固定为8字节,而PKCS7的分组长度为1-255字节。
PKCS #5文档:RFC 2898: PKCS #5: Password-Based Cryptography Specification Version 2.0 (rfc-editor.org)
iso7816
填充值第一个字节为 0x80,其他字节填 0。
from Crypto.Util.Padding import pad, unpad
from binascii import b2a_hex
data = b'Unaligned'
style = "iso7816"
pad_data = pad(data, 16, style)
print(b2a_hex(pad_data))
# b'556e616c69676e656480000000000000'
print(b2a_hex(unpad(pad_data, 16, style)))
# b'556e616c69676e6564'
data = b'\x00\x01\x02\x03\x04\x05\x06\x07' \
b'\x00\x01\x02\x03\x04\x05\x06\x80'
style = "iso7816"
pad_data = pad(data, 16, style)
print(b2a_hex(pad_data))
# b'0001020304050607000102030405068080000000000000000000000000000000'
print(b2a_hex(unpad(pad_data, 16, style)))
# b'00010203040506070001020304050680'
unpad:
pdata_len = len(padded_data)
if pdata_len == 0:
raise ValueError("Zero-length input cannot be unpadded")
if pdata_len % block_size:
raise ValueError("Input data is not padded")
padding_len = pdata_len - padded_data.rfind(bchr(128))
if padding_len<1 or padding_len>min(block_size, pdata_len):
raise ValueError("Padding is incorrect.")
if padding_len>1 and padded_data[1-padding_len:]!=bchr(0)*(padding_len-1):
raise ValueError("ISO 7816-4 padding is incorrect.")
return padded_data[:-padding_len]
x923
填充值最后一个字节为填充的数量数,其他字节填 0。
from Crypto.Util.Padding import pad, unpad
from binascii import b2a_hex
data = b'Unaligned'
style = "x923"
pad_data = pad(data, 16, style)
print(b2a_hex(pad_data))
# b'556e616c69676e656400000000000007'
print(b2a_hex(unpad(pad_data, 16, style)))
# b'556e616c69676e6564'
data = b'0123456789012345'
pad_data = pad(data, 16, style)
print(b2a_hex(pad_data))
# b'3031323334353637383930313233343500000000000000000000000000000010'
print(b2a_hex(unpad(pad_data, 16, style)))
# b'30313233343536373839303132333435'
unpad:
pdata_len = len(padded_data)
if pdata_len == 0:
raise ValueError("Zero-length input cannot be unpadded")
if pdata_len % block_size:
raise ValueError("Input data is not padded")
padding_len = bord(padded_data[-1])
if padding_len<1 or padding_len>min(block_size, pdata_len):
raise ValueError("Padding is incorrect.")
if padded_data[-padding_len:-1]!=bchr(0)*(padding_len-1):
raise ValueError("ANSI X.923 padding is incorrect.")
return padded_data[:-padding_len]
其它填充方式
ISO10126,填充值最后一个字节为填充的字节数,其他字节随机处理。
zero-padding,填充值全0。
TBC(Trailing-Bit-Compliment),明文最后一位为1时填充 0x00,最后一位为0时填充“0xFF”。