首页/安全/加密与证书速查/PKCS#7 填充规则
加密算法基础95 条

PKCS#7 填充规则

分组模式对不足一整块的数据补齐规则:填充字节的值等于填充长度,数据正好对齐时追加一整块,TLS 1.0-1.2 的 CBC 套件使用该规则。

加密与证书速查安全更新于 2026-10-13

PKCS#7 填充是什么

PKCS#7 填充是分组密码在工作模式中把数据补齐到整块的规则:缺几个字节就填几个「值等于缺失字节数」的字节;数据本来就对齐时追加一整个块,保证解密端一定能识别填充。TLS 1.0-1.2 的 CBC 套件与大量应用层的 AES-CBC 用法都采用这套规则,解密后必须校验填充是否合法。

基本信息

适用场景分组模式的数据对齐(AES-CBC、AES-ECB、SM4-CBC)
填充值0x01 到 0x10,数值等于填充字节数
块长AES/SM4 为 16 字节,DES/3DES 为 8 字节
整块情况追加 16 个 0x10
校验要求解密后逐字节校验填充值一致

典型使用场景

  • AES-CBC 加解密:调用库时区分 PKCS7 填充与无填充两种模式
  • 对接第三方接口:密文长度不是块长整数倍说明使用了填充
  • 协议实现:TLS 1.2 的 CBC 套件中填充长度参与 MAC 计算
  • 数据迁移:从带填充的旧格式迁到 AEAD 时需显式去掉填充
  • 排查解密报错:填充非法是「解密失败」类报错最常见的原因之一

库中的填充与去除

库中的填充与去除
from cryptography.hazmat.primitives import padding   # 提供 PKCS7 填充器
padder = padding.PKCS7(128).padder()                  # 128 位 = 16 字节块
padded = padder.update(data) + padder.finalize()
unpadder = padding.PKCS7(128).unpadder()
plain = unpadder.update(padded) + unpadder.finalize()  # 填充非法会抛异常

填充示例(块长 16 字节)

原始长度填充字节结果长度
1303 03 0316
150116
1610 共 16 个32
010 共 16 个16
PKCS#5 在规范上只定义 8 字节块(DES)的填充,AES 场景实际使用的是 PKCS#7 的推广形式;很多库与文档把二者名称混用,行为一致,Java 的 PKCS5Padding 在 AES 下也按 16 字节块处理。
填充是否合法只能通过校验判断,不能只按长度截断:填充预言攻击正是利用解密端对填充合法性的差异化反馈,因此错误信息与响应耗时都应保持一致。

常见实现差异

  • Python cryptography 的 PKCS7 需要显式指定块位数,128 位对应 16 字节
  • Java 的 AES/CBC/PKCS5Padding 实际按 16 字节块填充
  • Go 标准库不提供填充,需自行实现或引入成熟库
  • Node.js 的 createCipheriv 要求调用方自行处理填充与去填充
填充长度字段的取值介于 1 与块长之间;解密端校验时只要发现某个字节的值超过块长或前后不一致,就应判定整块非法并丢弃,不能返回部分明文。

易错点

  • 解密后不校验填充直接按长度截断:攻击者可篡改密文诱导解密端返回不同错误,形成填充预言攻击
  • 忽略整块时也要填充:长度恰为块长整数倍时不追加填充,解密端会把最后一个数据块误判为填充
  • 跨语言对接按名称假设行为:Java 的 PKCS5Padding、Python 的 PKCS7 与部分库的自动填充实际块长不同
  • 把填充当认证手段:填充不提供完整性保护,篡改密文仍可能解密成功,需 MAC 或 AEAD 兜底
  • 在流式场景反复对小块单独填充:每块都引入额外字节且破坏语义,长数据应改用 CTR 或 GCM 这类流式模式

数据来源与参考

  • RFC 5652 §6.3(PKCS#7 填充说明)
  • RFC 8018 §6.1.1(PKCS#5 定义)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌