TLS握手
本篇详细从理论与实践角度,解析TLS 1.2和TLS 1.3的握手流程。
1、TLS概况
TLS的主要目标是为通信的双方提供一个安全的通道。对下层传输的唯一要求是一个可靠的有序的数据流。
认证: 通道的Server端应该总是被认证的;Client端是可选的被认证。认证可以通过非对称算法完成,或通过一个对称的预共享密钥(PSK)。
机密性:在建立完成的通道上发送的数据只能对终端是可见的。TLS协议并不能隐藏它传输的数据的长度,但是终端能够通过填充TLS记录来隐藏长度,以此来提升针对流量分析技术的防护。
完整性:在建立完成的通道上面发送数据,不可能存在数据被篡改还没有发现的情况。即数据一旦被修改,对端会立即发现这个篡改。
不同版本的TLS由不同RFC文档定义,其关系如下:
TLS 1.0 → RFC 2246
TLS 1.1 → RFC 4346
TLS 1.2 → RFC 5246
TLS 1.3 → RFC 8446
占比情况如Cloudflare Radar统计所示。
2、TLS 1.2握手流程
根据RFC 5246,TLS 1.2握手分为两种,一种为完整的握手,另一种为简短的握手。TLS 1.0和1.1版本的握手流程与TLS 1.2相同,所以仅介绍TLS 1.2的握手流程。

这是一个完整的TLS 1.2握手流程,包含了许多带*的可选项。此外,后续文档在此基础上添加了一些扩展,如RFC 5077定义了New Session Ticket包,这里不做讨论。
然而,当客户端和服务器决定恢复之前的会话或复用已有会话,而不是协商新的安全参数时,就会用到简短握手,其流程如下:

TLS 1.2的简短握手,将原本的2-RTT提升到了1-RTT。
3、TLS 1.3握手流程
TLS 1.3相较于之前的TLS,TLS 1.3不支持RSA,也不支持易受攻击的其他密码套件和参数。它还缩短了TLS握手,改变了密钥协商算法。其同样分为完整的握手和简短的握手。

从握手流程能看出,完整的TLS 1.3握手是1-RTT,其中CCS包在RFC文档中已经被移除,但是为了兼容老设备,实际上的TLS 1.3握手,还是能看到CCS的身影,只不过其不包含实质性数据。
相较于TLS 1.2之前,通过“拼凑”的方式协商密钥,TLS 1.3使用(EC)DHE协商,且客户端是猜服务端支持的算法,如果猜错了,则会回退至2-RTT。有关密钥协商的部分可查看RFC 8446文档。
对应于TLS 1.2简单握手的1-RTT,TLS 1.3则是0-RTT,流程如下:

0-RTT存在“新旧交替”的两个密钥,其中()包裹的是用client_early_traffic_secret旧密钥加密的,[]包裹的则是handshake_traffic_keys新密钥加密的。0-RTT存在重放攻击的危险。
4、TLS握手包解释
| Name | Content Type | Handshake Type |
|---|---|---|
| Client Hello | 0x16 | 0x01 |
| Server Hello | 0x16 | 0x02 |
| Certificate | 0x16 | 0x0B |
| Server Key Exchange | 0x16 | 0x0C |
| Certificate Request | 0x16 | 0x0D |
| Server Hello Done | 0x16 | 0x0E |
| Client Key Exchange | 0x16 | 0x10 |
| Certificate Verify | 0x16 | 0x0F |
| Change Cipher Spec | 0x14 | — |
| Finished | 0x16 | 0x14 |
| New Session Ticket | 0x16 | 0x04 |
这里之所以Change Cipher Spec与其他的包Content Type不同,是因为要避免阻塞,详细请参考RFC 5246。
Client Hello:该包由客户端发往服务端,包含支持的TLS版本、加密套件、扩展等信息,同时包含第1随机数。JA3和JA4指纹就是根据该包计算而来,通常能够标识客户端的身份。
Server Hello:服务端回应客户端,说明选择了哪个TLS版本、加密套件、扩展等,同时包含第2随机数。JA4S指纹就是根据该包计算而来的。
Certificate:发送证书链,以用来验证自己的身份,可以是服务端,也可以是客户端发送。其中包含公钥、持有者信息、证书颁发机构签名等。接收方收到证书后,通过CA公钥进行本地验证,通常这些公钥由操作系统预装或者用户自行安装。
Server Key Exchange:对于某些加密算法,如DHE/ECDHE,服务器证书本身不足以生成预主密钥(Pre-Master Secret),需要额外的参数,如素数p、生成元g等,从而帮助客户端生成预主密钥。RSA场景下,无需此包。
Certificate Request:请求客户端提供证书以进行双向认证,通常用于网银等场景。
Server Hello Done:标志性消息,不携带密钥信息或证书,而是起到通知客户端服务器端握手消息发送完毕的作用。
Client Key Exchange:将客户端生成的预主密钥共享给服务端。对于RSA,客户端用服务端公钥加密预主密钥后传给服务端;对于DHE/ECDHE,客户端发送公钥参数,不用加密,双方通过密钥协商算法计算得到预主密钥。预主密钥通常放在内存里,抓流量不会抓到。RSA加密的TLS不满足向前保密(Perfect Forward Secrecy,PFS),因为服务器私钥泄露后,以往的所有流量都能够解密。而DHE/ECDHE则是各自生成新的临时私钥,然后进行密钥协商,服务器的私钥只用作身份认证。
Certificate Verify:该包是客户端用来证明自己拥有证书私钥的消息,通过签名验证身份。
Change Cipher Spec:该包用于通知对方“从现在起启用协商好的加密参数”的消息,它本身不加密也不签名,仅作为握手流程的信号,标志后续通信使用新的对称密钥和加密算法。
Finished:用于验证握手过程的完整性和双方的身份。用会话密钥加密之前握手消息的摘要,进行加密正确性的验证。在Wireshark中,由于加密过,常显示Encrypted Handshake Message。
New Session Ticket:于RFC 5077中新增,允许客户端保存会话信息,下次连接时可以直接恢复,而无需完整握手。
Encrypted Extensions:是服务器在ServerHello后立即发送的首个加密消息,用于安全地承载与密钥协商无关且不属于证书范畴的所有辅助配置信息,并强制客户端进行合规性检查。
EndOfEarlyData:客户端在0-RTT模式下发送的边界标记,用于向服务器声明“旧钥匙”加密的早期数据已发完,后续将切换回“新密钥”加密。
5、流量分析
TLS 1.2示例:
该流量由curl https://cip.cc产生,其中TLS握手中穿插了TCP的ACK数据,该数据不包含应用层数据,为传输层控制的数据,在分析应用层流量时,可以将其忽略。
此外,实际场景中的TLS握手会将多个握手包合并为一个数据包,这提高了传输效率,但是基本TLS握手规则没变。
TLS 1.3示例: