模块七 · 网络安全
报文完整性与数字签名
接收方怎样知道内容是否被替换、消息是否来自可信持钥者?先区分普通散列、消息认证码和数字签名,再讨论实现与公钥信任。
本章要解决的问题:接收方怎样知道内容是否被替换、消息是否来自可信持钥者?先区分普通散列、消息认证码和数字签名,再讨论实现与公钥信任。
本章的证据来源:教材§8.3,纸书409–417页,及HMAC、RSA、EdDSA等规范。本轮在原始笔记工程阅读了mac-attack-lab脚本和已有输出,核对了它演示的范围;本次未重新运行,不把脚本打印说明当作实际业务攻击证明。
先从一个例子理解概念
你收到一条“转账100元”的消息。问题有两个:内容是否被改成1000元?验证依据能否确认它来自你信任的发送方?只看到消息附着一个散列值,还不能回答第二个问题。
机密性、完整性与认证
机密性防止未授权读取;完整性验证帮助发现未授权修改;消息认证验证消息与可信持钥者的关系。这些概念可以由组合机制一起实现。
单纯加密未必提供完整性;认证加密(AEAD)则同时提供机密性与认证保护。不能把所有加密都描述成“完全不能验证修改”。认证也不能阻止攻击者丢弃数据或让通信中断。
散列、MAC与签名
普通散列由公开算法计算摘要;MAC(消息认证码)结合共享秘密;数字签名由私钥生成、用相应公钥验证。局域网里的MAC缩写另指介质访问控制,需按语境区分。
为什么消息加普通散列不够
发送(m, H(m))时,如果攻击者能替换消息和摘要,就能改成(m′, H(m′))。接收方核对散列仍通过。
散列可用来与可信取得的摘要比较,但不能让一个任意来源的摘要自动变可信。这也是上一模块CRC不等同于安全认证的原因。
HMAC:采用明确的带密钥构造
HMAC用共享密钥认证消息。验证方若信任密钥分发、算法与实现,验证通过可支持“来自某个持有该密钥的主体”的判断;不能仅靠MAC区分多个共享同一密钥的人。
以SHA-256为底层散列,示意公式为:
HMAC(K,m) = H((K0 xor opad) || H((K0 xor ipad) || m))||是拼接,xor是按位异或。K0是按散列分组长度处理过的密钥:过长时先散列,再按规范补零到分组长度;ipad和opad是规范规定的固定填充。
不能只把任意原始密钥直接代入公式,也不能把“双重散列”当作任意自制MAC都安全的证明。实现应使用经过明确规范定义的构造。
长度扩展攻击的条件
简单的SHA256(K || m)前缀构造可能遭受长度扩展:已知摘要并知道或猜中总长度后,可以构造带填充的扩展消息及对应摘要,而不必得知K。
能伪造扩展消息的校验,不意味着每个业务系统都接受其中的填充字节或按攻击者意图解释参数。原脚本只是比较散列值后打印“转账改向攻击者”,并没有解析和执行转账的服务;所以只能讨论构造的认证弱点,不能断言真实业务被修改。
HMAC有特定的内外层带密钥结构,不能用上述前缀长度扩展直接伪造一个扩展消息的有效HMAC。一次攻击失败也不是该实现全部安全的证明。
比较结果也需要注意实现
逐字节比较遇到不同时立即返回,会使运行过程依赖匹配前缀长度;攻击者在适当测量条件下可能利用耗时差异。
Python的hmac.compare_digest旨在减少比较内容相关的计时泄漏,官方文档仍提醒类型或长度差异等边界。不要用两次计时就宣布一个实现“恒定时间已证明”。
原脚本每轮调用time.sleep(2e-6),是在请求额外暂停,不是测得比较一个字节耗时2微秒。真实暂停受调度影响;其“恒定时间”演示也加入sleep,不等于实际库实现。少量总耗时比较不证明无泄漏,本章不用这些数值推断真实远程攻击能力。
数字签名不是把公钥加密反过来
签名算法使用私钥产生签名;验证算法使用可信取得的公钥、消息和签名判断是否有效。签名不隐藏消息,机密性需要另行提供。
发送方:Sign(私钥, 消息) → 签名
接收方:Verify(公钥, 消息, 签名) → 通过或失败这是通用接口。RSA签名使用其规定的编码与数学运算,EdDSA等又有不同算法;不能把所有签名都说成“私钥加密摘要、公钥解密”。
许多签名方案包含散列,但处理方式由方案规定,有些接受完整消息,有些另设预散列模式。RSA-OAEP的加密长度限制不是签名消息长度的通用上限,也不能据此要求使用者在调用任何签名接口前都先自行散列。
MAC与签名能提供怎样的证据
| 机制 | 谁能验证 | 注意条件 |
|---|---|---|
| MAC | 持有共享密钥者 | 验证者通常也能生成同类MAC,无法单独向第三方区分发送者 |
| 数字签名 | 可靠取得公钥者 | 可由第三方验证,但身份与证据效力仍依公钥绑定、私钥控制及上下文 |
密码学签名可以支持来源证明,但不能凭一次验证就自动证明某个人亲自操作、某个时间确实发生,或不存在私钥泄露。不可否认性的讨论要保留这些前提。
公钥属于谁,需要另一条信任依据
攻击者可以用自己的私钥对假消息签名,再提供自己的公钥。签名验证能通过,并不证明它来自你原本想联系的人。
证书和CA是一种绑定身份与公钥的方式。受信任渠道分发、预配置公钥、独立核对指纹等也可建立相应信任;数字签名并非必须依赖CA证书。TLS采用怎样的认证模式,在后续章节再具体理解。
签名与加密的组合顺序也依完整协议、安全目标和验证方式而定,不能笼统推荐“先加密再签名总是更安全”。
本章小结
| 结论 | 前提 |
|---|---|
| 普通散列不能单独认证任意来源消息 | 摘要必须有可信取得方式 |
| MAC依赖共享秘密,HMAC是特定构造 | 密钥处理与算法实现需符合规范 |
| 长度扩展可威胁某些前缀构造 | 总长度、填充与消息处理条件重要 |
| 比较过程可能产生侧信道 | 两次计时不能证明实现安全 |
| 签名是Sign/Verify算法,不是通用“反向加密” | 依具体签名方案 |
| 公钥验证与身份信任不同 | CA证书是一种途径,不是唯一途径 |
| 单纯加密未必认证,AEAD可组合保护 | 要看具体机制 |
自测题
7 题 · 先自己答,再展开对照
单纯加密和认证加密在完整性保护上有什么区别?
单纯加密未必检测未授权修改;AEAD同时提供机密性与认证保护。是否提供完整性要看具体机制,不能把所有加密一概归类。为什么
(m, H(m))不能单独认证任意来源的消息?攻击者可同时换成m′与H(m′)。普通散列只有在摘要等验证依据可信时才能支持完整性检查,不能自行建立来源信任。HMAC公式中的K0是什么?为什么不能随意用
SHA256(K||m)替代?K0是按底层散列分组长度规范化的密钥。HMAC有特定内外层结构,简单SHA-256前缀构造可受长度扩展威胁,且实际攻击还需总长度、填充及业务解析等条件。少数计时样本能证明恒定时间,或证明能在真实网络完成攻击吗?
不能,需考虑噪声、重复样本、输入条件、实现和攻击能力。少数运行时间既不能证明无泄漏,也不能证明真实环境一定可利用。MAC与数字签名对第三方验证有什么区别?
MAC验证方也持有共享密钥,可以生成MAC;签名可供可靠取得公钥的第三方验证。身份与不可否认性的进一步判断仍依密钥控制、信任和上下文。所有数字签名都是“私钥加密摘要”吗?RSA-OAEP加密上限能限制所有签名消息吗?
不是,签名由具体Sign/Verify算法定义。RSA签名和EdDSA等不同,RSA-OAEP是加密方案,其长度上限不能当成签名消息的通用上限。验证签名通过就确认了目标身份吗?数字签名是否必须有CA证书?
不一定,还要确认公钥确实绑定目标身份。CA证书是一种方式,可信分发、预配置或独立核对指纹等也可提供信任依据。
参考资料与适用范围
- 《计算机网络:自顶向下方法(原书第8版)》§8.3,纸书409–417页(PDF428–436页):散列、MAC、数字签名与公钥认证。书中的RSA说明不能推广成所有签名算法。
- RFC 2104 §2:HMAC构造与密钥处理
- RFC 8017 §7–8:RSA加密方案与签名方案分开定义
- RFC 8032 §3:EdDSA签名与验证
- RFC 5116 §2:认证加密接口
- Python官方:hmac与compare_digest
- Python官方:sleep暂停时间可能受调度延长
修订核对日期:2026-10-13。原脚本和输出已静态核对,旧稿结果仍是未重新运行的历史观察,不证明实际业务可被利用,脚本未随网站打包。教材中的旧算法示例和简化RSA图景不能替代具体方案与当前实现要求。