TLS 运行在传输层之上,为上层协议提供加密与身份验证。它的握手阶段要完成三件事:协商算法、验证服务端身份、派生对称密钥。在 TLS 1.3 中,密钥交换的默认方式改为临时 Diffie-Hellman,完整握手被压缩到一次往返,静态 RSA 密钥交换与若干旧算法则被移除。
流程的简化不是单纯删减步骤,而是把「协商」与「密钥派生」两部分重新组织:客户端在起始消息里就给出密钥共享,服务端据此即可计算共享秘密,并开始加密后续消息。
记录层与握手层的分工
TLS 在传输层之上分为记录层与握手层。记录层负责把上层数据切成记录、执行加解密与完整性校验;握手层负责消息交换与状态转换,握手消息本身作为记录层的载荷传输。
在 TLS 1.2 中,握手消息在流程结束前以明文传输,只有其后的应用数据被加密。TLS 1.3 把加密的起点前移:服务端的 ServerHello 之后,其余握手消息都在派生出握手密钥后加密传输,证书与签名因此不再以明文暴露。
往返次数的差异
| 版本 | 完整握手往返 | 密钥交换默认方式 | 密钥派生 |
|---|---|---|---|
| TLS 1.2 | 两次往返 | 可选 (EC)DHE 或静态 RSA | PRF,基于协商出的哈希 |
| TLS 1.3 | 一次往返 | 仅 (EC)DHE | HKDF,分层密钥调度 |
TLS 1.2 的两次往返里,客户端要先与服务端商定算法与随机数,之后才进行密钥交换。TLS 1.3 把密钥共享随 ClientHello 一并送出:客户端预先生成临时密钥对,把自己的公钥放进 key_share 扩展;服务端选定参数后返回自己的公钥,双方随即完成共享秘密的计算。
静态 RSA 密钥交换在 TLS 1.3 中被废弃。它的做法是客户端用服务端证书里的公钥加密预主密钥(pre-master secret)并发送。这一模式依赖服务端证书私钥参与每一次会话建立:一旦私钥泄露,此前录下的流量都可能被解密。缺少前向保密的特性,是它被移除的主要原因。
密钥交换
TLS 1.3 使用临时密钥交换,实际部署中以椭圆曲线 Diffie-Hellman(ECDH)和有限域 Diffie-Hellman(DHE)为主。双方各自生成临时私钥与对应公钥,交换公钥后计算共享秘密,该秘密只用于本次会话,握手结束后临时私钥即可丢弃。
# 观察握手消息与所选曲线
openssl s_client -connect example.com:443 -tls1_3 -msg </dev/null
客户端可以在 key_share 中预置多条曲线的公钥,服务端若都未支持,则返回 HelloRetryRequest 要求客户端改用指定曲线,这会额外增加一次往返。因此客户端通常把常见曲线排在前面,以降低重试的概率。
密钥派生
TLS 1.3 的密钥派生基于 HKDF,它由提取(extract)与扩展(expand)两步构成。整个派生过程组织成一张密钥调度表:从共享秘密出发,逐层派生出早期密钥、握手密钥与应用数据密钥,每一层都绑定一段带标签的上下文。
派生过程可以概括为:
/* 伪代码示意 */
early_secret = HKDF-Extract(salt=0, ikm=PSK 或 0)
handshake_secret = HKDF-Extract(salt=Derive-Secret(early_secret), ikm=共享秘密)
master_secret = HKDF-Extract(salt=Derive-Secret(handshake_secret), ikm=0)
每个阶段的流量密钥由 Derive-Secret 输出,并分别用于客户端与服务端两个方向。因为双方拥有相同的共享秘密与相同的握手记录,各自独立计算即可得到一致结果,无需额外传递密钥材料。
握手的完整性由 Finished 消息与转录哈希校验:双方各自计算截至目前所有握手消息的哈希,用派生出的密钥做消息认证码,接收方比对后即可确认对方看到的握手记录与自己一致。
前向保密
前向保密指长期私钥泄露不影响历史会话的安全性。实现条件是:每次会话的加密密钥由临时密钥对协商产生,且临时私钥在握手结束后被销毁。
在 TLS 1.3 中,由于密钥交换固定为临时模式,前向保密成为默认属性。会话恢复使用的预共享密钥(PSK)由上一次会话的密钥派生而来,若该 PSK 泄露,相应的会话可被解密,但其它独立会话不受影响。
0-RTT 会话恢复
会话恢复允许客户端复用此前会话的 PSK,跳过完整握手。在 1-RTT 恢复中,客户端先发送带 PSK 的 ClientHello,服务端确认后双方立即进入应用数据阶段,完整往返次数降为一次;在 0-RTT 中,客户端在发出 ClientHello 的同一批数据里就携带应用数据,服务端无需等待额外往返即可处理。
0-RTT 的代价是重放风险:攻击者可以录制这段早期数据并重复发送,服务端无法从协议层区分原始请求与重放。因此 0-RTT 数据只适合幂等请求,且服务端需要自行实现去重机制。协议也限制 0-RTT 只能用于恢复,不能用于首次连接。
证书验证与 SNI
服务端在加密的握手消息中发送证书链,客户端自下而上验证:校验证书链的签名、有效期、用途与名称匹配情况,并结合本地信任库与吊销信息判断其可信度。
SNI 扩展在 ClientHello 中明文携带目标主机名,服务端据此选择证书。在 TLS 1.3 中该字段本身不在加密范围内,因此后来引入了加密的客户端问候(Encrypted Client Hello)来隐藏它。此外,服务端还可以通过 ALPN 扩展在握手中确定上层协议,HTTP/2 的多路复用 协商即由此完成。
小结
TLS 1.3 的握手围绕「更早加密、更少往返」来组织:密钥共享随起始飞行送出,握手密钥在 ServerHello 之后即启用,密钥派生由 HKDF 分层完成,临时密钥交换带来前向保密。0-RTT 换来了零往返的启动,但也引入重放风险,需要应用层配合加以约束。