摘要
- RFC 3218 把错误文字、响应方式与处理时间都视为密码协议的外部输出。只要攻击者能区分“RSAES-PKCS1-v1_5 块格式错误”和“格式正确但内容密钥错误”,这种区别就可能被反复利用。
- “随机填充”在解包异常时生成一把符合预期长度的新 CEK,并继续完成内容解密与完整性检查。这把钥匙刻意不是正确钥匙;它的职责是让早期失败看起来像普通的后期失败。
最危险的泄露并不是明文,而是接收者对明文形状作出的一个判断。
1998 年,Daniel Bleichenbacher 证明,PKCS #1 v1.5 的这种一比特判断足以支撑自适应选择密文攻击。攻击者先取得一段 RSA 密文,再对它作数学变换,把新密文交给持有私钥的服务,并观察解密结果是否符合规定格式。每一次回答都会缩小隐藏明文可能落入的数值区间。私钥没有离开机器,但机器的一连串分类替攻击者完成了求解。
RFC 3218 把这一问题放进 Cryptographic Message Syntax。它在 2002 年 1 月以 Informational RFC 发布,并没有发明新的身份凭证,也不是泛称所有 RSA 用法都已经失效。它要处理的是一个现实过渡:CMS 已经大量使用 RSA PKCS #1 v1.5 来传送保护正文的对称内容加密密钥,旧格式不可能一夜之间退出互操作环境。
v1.5 编码块具有严格结构:先是 00 02,接着至少八个非零随机字节,再放一个 00 分隔符,最后才是被传送的值。在 CMS 里,这个值就是 content-encryption key,也就是 CEK。于是,RSA 私钥运算完成,只能说明产生了一串字节,不能说明这串字节构成了合法传输块。
这里至少有六种不同事实。RSA 运算是否完成;PKCS #1 结构是否有效;CEK 是否符合算法与长度;这把 CEK 是否真是发送者使用的密钥;内容解密是否得到字节;完整性与应用是否接受这些字节。把它们在内部混成一件事,系统就难以审计;把它们逐项暴露给外部,系统又可能成为 oracle。
所谓 Million Message Attack 描述的是操作规模。攻击者从捕获的密文 C 出发,选择乘数 S,发送相关密文 C' = C * S^e mod n。RFC 3218 估计,大约每 2^16 次变换会有一次得到以 00 02 开头的明文;在其讨论的 CMS 条件下,完整过程可能需要约 2^20 次消息与响应。
“一百万”并不是适用于所有密钥和实现的精确预算。它说明自动化如何改变威胁。一百万次人工邮件往返既昂贵又醒目;自动邮件列表代理、网关或无人值守服务却可以在没有人逐项查看的情况下持续解密、分类与回应。攻击者提供流量,接收端提供耐心。
一条攻击消息进入系统后,内部可能出现多种结果。PKCS #1 块可能格式错误;也可能格式完全正确,却包含伪造 CEK;错误 CEK 可能导致完整性检查失败;若内容缺少认证完整性,随机明文甚至可能偶然出现像样的 CBC 填充。变换后的密文恰巧恢复真实 CEK,则极不可能。
攻击者无须区分全部结果。它只需要把第一类——格式错误——和后面那些“格式正确但钥匙错误”的失败分开。不同错误文字可以提供这一个比特;有回复还是沉默、连接关闭、退信、签名告警乃至稳定的时间差,也都可以。
RFC 3218 的核心对策叫作 random filling。PKCS #1 解码发现错误时,接收者不在这里立即退出,而是使用密码学安全随机源,生成一把符合内容加密算法所需长度的 CEK,然后假装 RSA 解包已经返回了它。
接收者继续用这把虚构密钥解密正文,并照常执行填充、MAC、签名或应用级检查。随机 CEK 几乎必然在后面导致普通失败。对外错误和抵达错误所需的时间,应当接近“编码合法但 CEK 不对”的正常路径。
因此,随机 CEK 绝不是恢复密钥。它没有找回发送者的秘密,没有修复正文,也没有让消息取得授权。它是一段一次性的错误材料,用于让程序继续走到一个不暴露早期分支的位置。它之所以正确,恰恰因为它不可预测,而且几乎必然是错的。
固定回退 CEK 会把防护彻底颠倒。开发者可能偏爱一个常量:测试可重复,也不必在异常路径调用随机数生成器。但攻击者若知道或猜到这个常量,就可以先用它加密 CMS 正文,再配上一段故意破坏的 RSA 密钥传输值。接收者若进入回退分支,正文会被正确解开并通过应用检查;若未进入,就会失败。所谓防护反而制造了更清楚的 oracle。
替代值必须每次重新生成、具备密码学随机性,并符合预期长度。复用会让本应消失的失败分支获得稳定身份,供特制正文识别。
随机数生成的时机也会说话。如果生成器很慢,而且只在检测到错误后才运行,那么延迟本身就标记了错误路径。RFC 3218 因此提出另一种做法:每条消息都先生成一个随机候选值;若 RSA 解包有效,再把它丢弃。成功与失败两条路径都承担相近的随机生成成本。
这并不证明整个程序已经恒定时间。内存访问、解析器、日志、任务队列、邮件响应与应用调用仍可能不同。它只说明,统一错误文案远远不够;明显的计算分支也必须一并消除。
CEK 长度又构成一层边界。通用 RSA/PKCS #1 库可能知道编码语法合法,却不知道上层内容算法到底需要 8、16、24 还是 32 字节。如果底层返回数值,而 CMS 层用一种可观察的特殊异常拒绝错误长度,oracle 只是向上移动,并未消失。
知道内容算法的那一层,也必须把错误长度改换为随机 CEK。安全责任跨越软件层级:底层密码原语没有算法上下文,就无法独自识别所有必须隐藏的异常。
RFC 3218 还建议更严格地检查填充字节、CEK 长度,以及适用时 DES 系列的奇偶校验位。这些条件会降低随机变换通过检查的概率,从而提高攻击成本。
但稀有 oracle 仍然是 oracle。如果接收者继续告诉攻击者究竟哪一项失败,对方仍可用更多请求收集同一个比特。检查越严,不等于检查结果可以越公开。
未认证的 CBC 内容还揭示了“解密成功”有多薄弱。随机字节可能偶然以看似合法的填充结束。如果实现只读取最后一个字节并据此截断,RFC 3218 估算表面合法的概率约为 1/32;若核验全部填充字节,概率会降到接近 1/255。
这些概率从未让随机明文获得意义。它们只是说明,解密函数返回一个带填充缓冲区,并不构成接受证据。MAC 或签名能提供更可靠的后期拒绝点;即便如此,接收者仍不能把初始 PKCS #1 结论单独暴露出去。
OAEP 提供了更干净的密码迁移方向。PKCS #1 v2.0 已经规定 RSAES-OAEP,RFC 3218 认为本文讨论的攻击不适用于它。但 OAEP 与 v1.5 的线格式不兼容。发送者、接收者、证书、算法标识以及已部署 CMS 软件都必须对新编码取得一致。
说出更好的原语名称,不会让旧生态瞬间消失。random filling 是共存期措施:只要遗留格式仍在接收流量,就先限制它能够向外透露的区别。
TLS 处理 RSA 加密的 premaster secret 时也面对同一家族的风险。其规范要求服务器在格式或版本异常时继续使用随机值,而不是把具体原因交给对端。两者共享思路,却不共享所有可观察行为:TLS 握手、CMS 消息与邮件代理拥有不同状态机与后续结果。
真正的一般规则位于密码原语之上:内部解析器不能回答外部协议无权安全回答的问题。外部协议必须决定还要执行多少工作、最终公开何种失败,以及哪些旁路信号仍会泄露。
RFC 3218 没有宣称 random filling 能认证发送者、堵住所有计时通道、修复泄露的私钥,或证明某个实现安全。它也没有给出部署普查。它的贡献更窄,也更耐久:只要内部差别可以被连续询问,错误处理就是密码系统的一部分。
会员简报
档案背景详情
使用相应会员等级登录,即可解锁完整简报与来源注释。
仅限 Strategic Circle
Strategic Circle
所有读者均可浏览。加入并登录后可解锁档案简报。
加入 Strategic Circle仅限 Leadership Alliance
Leadership Alliance
符合条件的 IP 资产所有者和管理层可登录查看 Leadership Alliance 简报。
加入 Leadership Alliance
