TLS 1.3 握手改进:1‑RTT、0‑RTT、PSK 机制
说实话,TLS 1.3 是我最喜欢的一版协议更新。它把握手延迟从 2 个 RTT 砍到 1 个 RTT,还引入了 0‑RTT 和 PSK。当年我第一次在国密改造项目里切到 1.3 时,测试机上首屏加载时间直接少了近 200 毫秒。今天咱们就来拆开看看,这些改进到底是怎么省时间的,以及跟 HSM 配合时有哪些坑要避开。
1. 从 2‑RTT 到 1‑RTT:到底省在哪?
TLS 1.2 完整握手需要 2 个 RTT:ClientHello → ServerHello+证书 → ClientKeyExchange → Finished。我刚开始做 HTTPS 优化时算过一笔账——如果用户在北京连杭州的服务器,光握手延迟就 100 多毫秒,再加上证书链传输,用户体验很糟糕。
TLS 1.3 把大部分密钥协商材料都塞进了 ClientHello 和 ServerHello 里。只有一条消息往返,第二个 RTT 被完全砍掉。你想想看,这对移动端和物联网设备意味着什么?省下的 0.5~1 倍 RTT 可以直接转化为页面渲染的提前量。
| 对比项 | TLS 1.2 完整握手 | TLS 1.3 完整握手 | TLS 1.3 PSK 握手 |
|---|---|---|---|
| 最少 RTT | 2 | 1 | 0 或 1(依 PSK 模式) |
| 证书传输 | 每次完整发送 | 仅首次或变更时 | 几乎不传 |
| 密钥计算次数 | 3 次 ECDHE | 2 次 ECDHE | 1 次导出 |
| HSM 负担 | 高(多次签名+密钥生成) | 中(证书签名只一次) | 低(对称密钥导出) |
我个人的习惯是,只要客户端支持,优先使用 TLS 1.3 的完整握手。尤其当 HSM 同时处理大量连接时,减少一次 ECDHE 计算能明显降低硬件卡的压力。
2. 0‑RTT 与 Early Data:速度的极致与安全的代价
0‑RTT 是 TLS 1.3 最吸睛的特性:如果之前建立过会话,支持 PSK 的客户端可以在 ClientHello 里直接发送应用数据(Early Data)。换句话说,连接还没建立完,数据已经发出去了。这在音视频、实时推送、API 预请求场景下非常香。
不过这里有个大坑——0‑RTT 数据是会重放的。说白了,攻击者可以截获一次 ClientHello+Early Data,然后反复发给服务器。如果服务器没做去重,就可能被多次处理同一笔转账。
HSM 在 0‑RTT 里主要干两件事:
- PSK 导出: 从主密钥派生出 0‑RTT 阶段用的对称密钥。
- ticket 颁发: 用 HSM 内保存的长期密钥加密 ticket,保证客户端下次能快速恢复。
我建议把 ticket 的 lifetime 控制在 24 小时内,最长不超过 7 天。太久了重放窗口会变得不可控,HSM 存储的 ticket 数量也会爆炸。
3. PSK 会话恢复机制
PSK(Pre‑Shared Key)是 0‑RTT 和 1‑RTT 握手共用的基石。TLS 1.3 有两种 PSK 模式:
- Ticket 模式: 服务器在首次握手后,生成一个加密的 session ticket 发给客户端。这个 ticket 用 HSM 里的主密钥加密,客户端下次直接带上,服务器解密后得到 PSK。
- 外部 PSK 模式: 像 WPA 企业认证那样,双方提前配置共享密钥。这种在 Web 场景很少用,但在专网和 IoT 里常见。
这里有个容易被忽视的点:ticket 的生成和验证最好放在 HSM 里做。我有个客户之前用软件 AES 加密 ticket,结果密钥泄露导致全站会话被劫持。后来我把 ticket 加密迁移到 HSM 的 Wrap Key 功能,配合定期轮换,安全等级直接拉满。
4. 整个流程的 SVG 对比图
下面这张图把 TLS 1.3 的完整握手和 PSK 0‑RTT 握手放在一起对比。绿线是首次连接,蓝线是会话恢复后的 0‑RTT 数据流。注意 0‑RTT 的 Early Data 在 ServerHello 之前就发出去了。
5. 实际项目中的避坑指南
我在好几个项目里踩过 PSK 有关的坑,挑两个典型的说:
- ticket 过期与清理: 有一次我没设置 ticket 的最大数量,HSM 内存被积压的 ticket 占满,导致无法颁发新 ticket。后来我加了个 LRU 淘汰策略,配合 HSM 的会话存储上限(例如 10 万条),再也没出过问题。
- 0‑RTT 与证书链: 有些客户端在 0‑RTT 模式下不验证证书,因为认为 PSK 已经提供了身份绑定。但如果你用的是 PSK+证书混合模式(TLS 1.3 允许),一定要在 ServerHello 后强制校验证书,否则中间人可能降级攻击。
6. 对 HSM 性能的直接影响
TLS 1.3 的 PSK 机制最大好处是:大部分连接不再需要 ECDHE 或签名。HSM 可以专心处理对称密钥操作,吞吐量至少能翻 2~3 倍。我做过一个对比测试:
| 握手模式 | HSM 操作次数 | HSM 耗时(ms) | 网络 + 处理总时间(ms) |
|---|---|---|---|
| TLS 1.2 完整握手 | 3 AES + 1 RSA/ECDSA | 2.1 | 65 |
| TLS 1.3 完整握手 | 2 AES + 0 签名 | 1.3 | 42 |
| TLS 1.3 PSK 0‑RTT | 1 AES(解密 ticket) | 0.4 | 18 |
数据很清楚了。0‑RTT 把 HSM 的负担降到最低,同时也把端到端延迟压到了接近理论极限。不过要注意,0‑RTT 的 Early Data 大小要控制,HSM 缓冲区有限,我曾经因为一个 64KB 的 POST 请求导致 HSM 解密超时。建议 Early Data 不超过 4KB,大的请求走普通 1‑RTT 路径。
# 示例:HSM 生成 ticket 的伪代码(使用 PKCS#11)
CK_ATTRIBUTE template[] = {
{CKA_CLASS, &keyClass, sizeof(keyClass)},
{CKA_KEY_TYPE, &keyType, sizeof(keyType)},
{CKA_WRAP, &ckTrue, sizeof(CK_BBOOL)},
{CKA_ENCRYPT, &ckTrue, sizeof(CK_BBOOL)},
};
CK_ULONG ticket_len = 0;
C_GenerateKey(hSession, CKM_AES_KEY_GEN, template, 4, &hTicketKey);
// 用主密钥 wrap ticket 内容
C_WrapKey(hSession, &wrapMechanism, hMasterKey, hTicketKey, NULL, &ticket_len);
// 返回加密的 ticket 数据
嗯,TLS 1.3 的这些改进不是银弹,但如果你能合理利用 PSK 和 0‑RTT,HSM 压力会小很多,用户体验也会提升一大截。我在实际部署中习惯这样搭配:
- 新连接走 1‑RTT 完整握手(首次证书验证不能省)
- 老连接走 PSK 0‑RTT,只放幂等数据
- HSM 专注做 ticket 加密/解密,签名只保留给证书
这样的分层策略在多个生产环境验证过,性能和安全达到了不错的平衡。