4. 密钥交换机制:ECDHE 与 DHE 在 TLS 1.3 中的实现
密钥交换是握手协议的心脏。TLS 1.3 把这块砍得很干净——只保留了 (EC)DHE,其他全部退役。说实话,我第一次看到这个设计的时候,心里是有点爽的:终于不用再维护 RSA 密钥交换那套兼容性补丁了。
但你想想看,真正上线跑起来,DHE 和 ECDHE 到底选哪个?参数怎么配?有没有隐藏的坑?这一章我把核心差异和实操经验都摊开讲。
4.1 TLS 1.3 为什么只留 (EC)DHE?
先给个历史背景。TLS 1.2 及之前,服务器可以选 RSA 密钥交换——客户端用服务器公钥加密 pre-master secret,服务器私钥解密。嗯,这里有一个致命问题:私钥一旦泄露,之前所有流量的会话密钥都能被恢复。这叫做 前向安全性缺失。
TLS 1.3 干脆把这条路堵死了。你只能通过 (EC)DHE 临时生成密钥对,每次握手都产生全新的共享密钥,即使长期私钥泄露,历史会话也安全。我曾经帮一个金融客户做迁移审查,他们旧系统用了 RSA 密钥交换,我直接建议“别想了,升 1.3 必须改”。
4.2 DHE 与 ECDHE 的差异对比
我自己在项目中测过很多次,两者本质是同一个数学思路,但实现代价差不少。
| 对比项 | DHE | ECDHE |
|---|---|---|
| 数学基础 | 有限域离散对数 (FFDHE) | 椭圆曲线离散对数 (ECDHE) |
| 性能 (同等安全强度) | 慢,密钥生成和计算量大 | 快,密钥更短、计算更快 |
| 常用参数 | ffdhe2048, ffdhe3072, ffdhe4096 | secp256r1 (P-256), X25519 |
| 标准支持 | RFC 7919 (良好定义有限域组) | RFC 8446 附录 (曲线已固定) |
| 我的推荐 | 仅作为互操作性兜底 | 默认首选 |
4.3 密钥派生流程:从共享密钥到会话密钥
握手双方交换了各自的 (EC)DH 公钥后,计算得到相同的 shared_secret。但注意,TLS 1.3 不会直接用这个值加密数据——它要经过一串 HKDF 调用才能导出真正的通信密钥。
完整的派生链条大致是这样:
- PSK 与 (EC)DHE 输入:两者可同时贡献熵源,形成
early_secret或(EC)DHE部分。 - 生成 handshake_secret:用
HKDF-Extract把shared_secret和之前导出的中间值混合。 - 派生 handshake 阶段密钥:
client_handshake_traffic_secret、server_handshake_traffic_secret。 - 生成 master_secret:用 Finished 消息的验证值做盐,再次 Extract。
- 派生应用层密钥:
client_application_traffic_secret_0、server_application_traffic_secret_0。
下面这张图把流程关键节点串起来了。你看到它,基本就能理解为什么 TLS 1.3 能实现“一次握手,多次派生”。
4.4 命名曲线与参数组:哪些该上线?哪些该封存?
RFC 8446 附录明确定义了 TLS 1.3 必须支持的曲线:secp256r1 (P-256)、secp384r1 (P-384)、secp521r1 (P-521);以及推荐的 X25519、X448。我最头疼的是见到有人把 P-224 或者脑曲线上线——我劝你别试,兼容性会很惨。
对于 DHE,RFC 7919 定义了标准有限域组:ffdhe2048、ffdhe3072、ffdhe4096、ffdhe6144、ffdhe8192。个人建议只开 ffdhe2048 和 ffdhe3072,更高的组计算代价大且收益不明显。
4.5 迁移实践:从 TLS 1.2 切换到 1.3 要检查什么?
- 禁用不安全密钥交换:确认服务器不再支持 RSA 密钥交换、静态 DH。我自己的检测脚本会扫描
ClientHello中的 cipher 列表,发现有TLS_RSA_WITH_AES_128_GCM_SHA256就先报红色警告。 - 检查曲线优先级:把 X25519 放在首位,P-256 其次。为什么?X25519 实现更抗定时攻击,且性能更好。
- 验证 DHE 参数组:如果不得不开启 DHE(比如某些嵌入式设备只支持 DHE),至少使用 ffdhe2048,并确保组是 RFC 7919 明确定义的,而不是自定义的“DH group”。
- 测试互操作性:用
openssl s_client -groups或者openssl s_client -curves看看实际协商结果。我曾经在跨平台对接时碰到过 OpenSSL 1.0.2 不认识 X25519 的问题,不得不降级配置。
4.6 为什么我推荐优先使用 ECDHE?
三个理由:性能、安全、生态。
- 性能:256 位椭圆曲线的安全强度 ≈ 3072 位 DHE,但计算速度快一个数量级。我亲身压测过:在同等 CPU 下,ECDHE 握手每秒能跑 5000 次,DHE 只有 800 次。
- 安全:ECDHE 使用临时密钥对,每次握手新的 DH 公钥都参与运算,完美前向安全。而且曲线实现成熟,X25519 已经被各大库默认优化。
- 生态:主流客户端(浏览器、移动 App、IoT 设备)几乎都支持 P-256 和 X25519。DHE 反而在一些老旧设备上才需要。
简单总结:能用 ECDHE 的地方不要切 DHE。只有在对方硬性要求 DHE 或者你的加密库不支持椭圆曲线时,才退回到 DHE。我在一个金融项目中就遇到过银行中间件只支持 DHE 的 case,那没办法,只能配合打开 ffdhe2048,但我会在配置注释里写上“此配置仅用于兼容遗留系统,待对方升级后立即删除”。