密码学 06:对称加密为什么不能单独认证消息
给订单 JSON 加密后,旁观者读不出数量。是否可以省去校验身份与完整性的步骤,让接收端解密后直接创建订单?不能。“看不懂密文”只涉及某种保密目标,并不自动保证来源可信、内容没被有意改变;同一个 AES 算法配上不同的模式,观察到的泄露和验证行为也不同。本章先看一个不能用于一般订单的加密模式,再用独立的玩具模型观察改动密文的可能性,最后明确为何 07 要转向认证加密。
谁持有密钥,接收端检查什么
对称加密的典型输入是一个共享密钥、明文字节,以及所选模式要求的初始化输入;输出是密文字节。服务端拿到解密密钥后可以恢复明文。如果双方共享同一密钥,不看额外凭据,服务端通常也有能力制造看似由发送端生成的密文;这不是“只有浏览器才能签名”的公开可验证授权。
1 | |
如果订单本来在经过身份验证的 TLS 连接上,TLS 记录保护还包括认证加密,不是“只有 AES 加密裸奔”;不能把下面的 ECB 例子倒推为 TLS 1.3 的漏洞。这里的问题是:只加密一串应用字节而不验证它来自谁、是否被改过,能否完成订单安全要求?答案要具体到模式与校验逻辑,不能从一个“ciphertext”字符串得出。
两块相同订单字节怎样露出形状
AES 是固定 16 字节分组上的密码算法。ECB 逐块独立处理;同一密钥下两个完全相同的明文块产生两个完全相同的密文块。如果把同一段订单模板 BOOKBOOKBOOKBOOK 连续放两次,虽然攻击者未必能还原模板,仍能看见两段密文完全相同。这泄露了输入的重复结构,特别不适合直接加密普通结构化消息。
1 | |
这是 ECB 模式的真实算法输出,不是一般 AES 的普遍性质。模式的任务是定义分组怎样用于长消息;IV 是一些模式要求的初始化输入,但“用了 IV”仍不足以推出认证或防重放。不能把 ECB 作为订单或文件加密的部署选项。比如有人换成 CTR 等流式模式,重复块的问题可能变了,但若没有认证步骤,改动风险仍需逐项核对,不能凭“已加密”宣称完整性。
一个只说明“可改”的玩具模型
脚本还演示了一个明说不是 AES 的字节关系:密文 = 明文 XOR 掩码。若接收者仍用同样掩码解密、却不校验任何 tag,攻击者把数字 1 对应的密文字节翻转 ord("1") ^ ord("9"),接收方得到的就是 quantity=9。
1 | |
这是没有认证的 XOR 流模型的结果。它不能证明能对 OpenSSL AES-ECB 密文做同样定向修改,更不能证明现实 TLS 流量可以被这样改。真实系统中的流密码或 CTR 类模式还涉及密钥、计数器、nonce 的具体约束;本篇不自行实现任何生产流密码。模型的价值是让“密文并非天然拒绝改动”变成可检查的字节关系,再到成熟库里看认证加密怎样拒绝它。
实际命令与已知向量
examples/cryptography/06_modes.py 调用安装的 OpenSSL 3.0.13 的 enc -aes-128-ecb -nopad -nosalt 执行真实 AES-128 的不安全模式演示。公知测试向量使用 000102030405060708090a0b0c0d0e0f 密钥、00112233445566778899aabbccddeeff 明文,输出 69c4e0d86a7b0430d8cdb78070b4c55a;这里的“密钥”是完全公开的测试数据,绝不能复用为服务秘密。第二个输入是两个相同的 16 字节块。
1 | |
2026-10-06 UTC,在 Python 3.12.3 与 OpenSSL 3.0.13 上执行,两条命令退出码均为 0,四项测试通过。重复块的密文相等布尔值为 true,玩具改动后的明文为 quantity=9。脚本主动拒绝不满 16 字节的 ECB 输入:无填充模式要求整块,报错比悄悄补零可检验。若去掉拒绝检查,相关单元测试就会失败;如果误把两块之一改成不同明文,原先的重复块断言也应失败。
这里没有实现接收端解密业务消息,也没有运行 TLS 或真实 AEAD。ECB 重复块实验属于成熟工具执行的真实算法原语,XOR 翻转属于教学模型;两份证据不能合并为“成功篡改一笔真实 AES 加密订单”。
什么要交给下一步认证加密
接收者需要的是:错误密钥、错误密文或错误业务上下文时拒绝输出有效订单,而不是“解得出某些字节就算成功”。认证加密(AEAD)把密文和认证标签关联到密钥、nonce、明文及可见但要保护的附加数据;07 将分别改动密文、标签与上下文,观察真实库拒绝,并单独说明 nonce 重用风险。即便 AEAD 都通过,业务唯一性、服务名和交易授权仍由其他层负责。
如果把加密层去掉,明文订单会暴露给能读通道的人;如果只留加密却删掉认证步骤,至少不能再保证恶意改动一定被拒绝;若把验证上下文省掉,原本发给一个用途的受保护字节也可能被错误地解释为另一种用途。每删一步,先描述谁控制哪些输入,再问接收者凭什么拒绝。
两道带答案的练习
推导题。 在不透露 AES 密钥的前提下,攻击者看见两块相同 ECB 密文能推断什么,不能推断什么?若改用同密钥但不相同的明文块,是否仍能仅凭这条规则推断两块密文相同?
可核对答案: 在这个固定分组/密钥前提下,相等密文块表明对应明文块相等,可泄露重复结构;不能由此直接得知块内文字或订单业务含义。不同明文块在可逆的固定分组密码下不会映射成相同密文块,但不能把“没有发现重复”说成没有其他泄露或已获认证。
变更题。 在 06_modes.py 的 block * 2 中让第二块变成另一组 16 字节,运行脚本与测试。失败在哪一处?能否将玩具 XOR 的数字修改实验称为同一次 OpenSSL AES-ECB 实验?
可核对答案: 脚本检查 identical_plaintext_blocks_produce_identical_ciphertext_blocks 为真时应断言失败;如果只改脚本不改测试中固定的输入,测试仍可能独立通过,所以需核对实际运行输入。XOR 是另一个模型,不是对该次 OpenSSL AES-ECB 密文的篡改或安全结论。
资料与导航
- NIST FIPS 197,AES、NIST SP 800-38A,分组模式:标准向量与模式边界。
- RFC 5116,认证加密接口:后续 07 的真实 AEAD 接口入口;本章未执行 AEAD。
- 05 随机数、salt 与 nonce · 04 HMAC 为什么需要秘密;07 正文存在后再加入。






