跳到主要内容
浏览器
导航
内核与技术专题

TLS 1.3 握手流程

对比 TLS 1.2 与 1.3 的握手差异:临时密钥交换与前向保密、HKDF 密钥调度表的派生过程、握手加密的起点、0-RTT 会话恢复及其重放风险,以及证书验证与 SNI 扩展在流程中的作用。

发布于 5 分钟阅读 1663 字

TLS 运行在传输层之上,为上层协议提供加密与身份验证。它的握手阶段要完成三件事:协商算法、验证服务端身份、派生对称密钥。在 TLS 1.3 中,密钥交换的默认方式改为临时 Diffie-Hellman,完整握手被压缩到一次往返,静态 RSA 密钥交换与若干旧算法则被移除。

流程的简化不是单纯删减步骤,而是把「协商」与「密钥派生」两部分重新组织:客户端在起始消息里就给出密钥共享,服务端据此即可计算共享秘密,并开始加密后续消息。

记录层与握手层的分工

TLS 在传输层之上分为记录层与握手层。记录层负责把上层数据切成记录、执行加解密与完整性校验;握手层负责消息交换与状态转换,握手消息本身作为记录层的载荷传输。

在 TLS 1.2 中,握手消息在流程结束前以明文传输,只有其后的应用数据被加密。TLS 1.3 把加密的起点前移:服务端的 ServerHello 之后,其余握手消息都在派生出握手密钥后加密传输,证书与签名因此不再以明文暴露。

往返次数的差异

版本完整握手往返密钥交换默认方式密钥派生
TLS 1.2两次往返可选 (EC)DHE 或静态 RSAPRF,基于协商出的哈希
TLS 1.3一次往返仅 (EC)DHEHKDF,分层密钥调度

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 换来了零往返的启动,但也引入重放风险,需要应用层配合加以约束。

内核与技术专题 · 相关文章