四、TLS加密套件与密钥交换归纳总结
加密套件这东西,我刚开始接触时真被那些又长又乱的字符串搞晕了。像 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,一眼看去全是缩写。后来我发现,只要拆成四个部分来记,就清楚多了。
4.1 加密套件拆解:四个部分各司其职
一个加密套件本质上就是一个“协议配方”。它规定了:用什么做密钥交换、用什么做身份认证、用什么做对称加密、用什么做完整性校验。我整理了一个表格,方便你对照理解。
| 组成部分 | 常见算法 | 作用 |
|---|---|---|
| 密钥交换 | ECDHE、DHE、RSA、PSK | 协商出一个只有通信双方知道的共享密钥 |
| 身份认证 | RSA、ECDSA、Ed25519 | 验证服务器(或客户端)证书的真实性 |
| 对称加密 | AES_128_GCM、ChaCha20_Poly1305 | 对应用数据批量加密,保证机密性 |
| 完整性校验 | SHA256、SHA384 | 用HMAC或AEAD的认证标签防篡改 |
小技巧: 看加密套件字符串时,从后往前读更容易——先看哈希,再看加密,最后看密钥交换。比如 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,SHA256 是完整性,ChaCha20 是对称加密,ECDHE+ECDSA 是密钥交换和认证。
为什么会把密钥交换单独拎出来?因为它在握手阶段最核心,也最容易被忽视。我曾经在项目里遇到过:客户要求全部禁用 RSA 密钥交换,但服务端配置漏了一条,结果中间人大摇大摆地降级到 RSA 套件。嗯,那件事之后,我习惯每次改完配置都跑一遍 sslyze 做全面扫描。
4.2 密钥交换算法对比:该选哪个?
我个人习惯把常用的密钥交换算法按安全性和性能分成三档。下面这张表帮你快速决策。
| 算法 | 是否前向安全 | 性能 | 适用场景 |
|---|---|---|---|
| RSA | 否 | 中等 | 遗留系统、兼容旧设备 |
| DHE | 是 | 慢(大整数运算) | 可接受延迟的场景 |
| ECDHE | 是 | 快(椭圆曲线) | 现代推荐,几乎所有业务 |
| PSK | 取决于模式 | 极快 | IoT、内部网络预共享密钥 |
避坑: 我曾经在某个金融项目里遇到过,为了兼容旧版客户端,不得不保留 RSA 密钥交换。但攻击者很容易保存会话流量,等以后有能力解出私钥后再解密历史数据。前向安全的 ECDHE 才是必须的,别再抱着 RSA 不放了。
说到 ECDHE,我记得有一次排查 TLS 握手超时,发现原因是客户端不支持服务端推荐的 P-521 曲线。当时两边都用了 ECDHE,但曲线不匹配,协商失败。后来我建议把曲线列表优先放 P-256 和 X25519,兼容性和性能都最好。
4.3 TLS 1.3 中的加密套件:简化到极致
TLS 1.3 彻底改写了加密套件的定义。你想想看,TLS 1.2 里有两百多个套件,实际在用的不过十几个。TLS 1.3 直接砍到 5 个,而且只定义了 AEAD 算法和哈希的组合。
TLS_AES_128_GCM_SHA256– 默认首选TLS_AES_256_GCM_SHA384– 高安全需求TLS_CHACHA20_POLY1305_SHA256– 移动端、没有 AES 硬加速的设备TLS_AES_128_CCM_SHA256– IoT 低功耗场景TLS_AES_128_CCM_8_SHA256– 分组长度更小的变体
其实,TLS 1.3 里密钥交换完全固定为 (EC)DHE,不再支持 RSA、PSK 只作为预共享密钥模式保留。这样就剔除了 RSA 密钥交换这个历史包袱。
重要: TLS 1.3 的加密套件字符串不再包含密钥交换和认证算法部分,因为它在握手阶段固化了两者。如果想了解具体协商了哪个曲线,得看 ServerHello 里的 supported_groups 扩展。
我个人觉得,这种设计才符合“不要让使用者猜”的原则。你在配置 TLS 1.3 时只需要关心对称加密算法,其他交给协议自动选择最安全的。
4.4 加密套件与密钥交换对水印字段的影响
回到咱们课程的核心——数字水印。水印字段被植入应用数据后,整个数据包会经过对称加密和完整性保护。但有两个容易被忽略的交叉点:
- 密钥交换阶段对水印字段的“偷看”风险 – 如果用了 RSA 密钥交换,中间人可以记录密文,未来解密私钥后就能还原整个会话。水印字段也会被还原。ECDHE 因为每次会话用临时密钥,即使私钥泄露也无法回溯解密,水印字段在传输过程中更安全。
- AEAD 认证标签对水印字段的覆盖 – TLS 1.2 和 1.3 都推荐使用 AEAD(GCM、ChaCha20-Poly1305)。认证标签(authentication tag)会保护整个加密载荷和附加数据。水印字段如果被埋在 Application Data 里,任何篡改都会导致解密失败。我遇到过有人想在中间修改水印字段,结果被 TLS 完整性检查拦下来了,这就是 AEAD 在帮你。
来看一段抓包示例,展示 AEAD 如何保护水印字段。
// 假设水印字段为 "WATERMARK:2025-04-08",在 TLS 1.3 中
// 非加密的内容:
// type: 23 (Application Data)
// plaintext: "GET /resource HTTP/1.1\r\nX-Watermark: WATERMARK:2025-04-08\r\n"
//
// 经过 TLS 1.3 保护后, wire 上看到的是:
// Content-Type: 23
// Encrypted Record: [nonce + ciphertext + auth_tag]
// 其中 ciphertext 完全不可读,auth_tag 为 16 字节
//
// 任何中间节点修改了 ciphertext,接收方计算 tag 不匹配,直接断开连接。
个人建议: 如果你的业务依赖水印字段在网络中间节点被识别(比如 CDN 缓存),那这些节点必须是 TLS 解密代理(如中间盒),否则水印在密文里完全不可见。我曾经有一个客户把水印放在 HTTP 头部,却在 Cloudflare 之前被加密了,导致 CDN 无法根据水印分流。最后他们不得不改用 ECH(Encrypted Client Hello)里的扩展字段来带水印。
最终,加密套件的选择对水印字段的影响可以归纳为:
- 如果只要求机密性,任意现代 AEAD 套件都可以,注意前向安全要选 ECDHE。
- 如果水印字段需要在中间节点可见,必须设计提前解密或使用外带通道。
- TLS 1.3 的 0-RTT 模式下,水印字段可能被重放攻击利用,要谨慎使用。
注意: 千万别以为加密了就能隐藏水印字段。如果你在水印字段里插入了唯一客户端标识,并且攻击者能观测到连接建立后的加密流量大小和时间,他仍然可以通过流量分析关联到特定用户。加密套件只保护内容,不保护元数据。
上面这张图把加密套件的四个组成部分和 TLS 1.3 的简化关系串了起来。重点看左下角的密钥交换分支,以及底部的“水印字段受对称加密层完整保护”——这是咱们课程一直在强调的:只要选择了 AEAD + ECDHE 的组合,水印字段在传输中就像锁在保险柜里,任何人想动一下都会被立刻发现。
最后提一句我在实际渗透测试中的观察:很多系统配置了 TLS 1.2 但保留了 TLS_RSA_WITH_AES_128_CBC_SHA 这类套件作为降级备选。攻击者会故意发送不支持 ECDHE 的 ClientHello,迫使服务器回退到 RSA。这样一来,水印字段虽然被加密了,但一旦服务器私钥泄露(比如通过 Heartbleed 漏洞),历史会话全部暴露。所以我维护的推荐策略是:只保留 ECDHE + AEAD 套件,禁用一切 RSA 密钥交换和 CBC 模式。