SSL/TLS
Table of Contents
1. TLS 简介
The Transport Layer Security (TLS) protocol aims primarily to provide secure and data integrity between two communicating computer applications.
TLS 位于 TCP 之上(也有基于 UDP 的,称为DTLS,这里不讨论它),如图 1 所示。
Figure 1: SSL/TLS 协议所在层次
TLS 各个版本提出时间如表 1 所示。
Protocol | Published |
---|---|
SSL 1.0 | Unpublished |
SSL 2.0 | 1995 |
SSL 3.0 | 1996 |
TLS 1.0 | 1999 |
TLS 1.1 | 2006 |
TLS 1.2 | 2008 |
TLS 1.3 | 2018 |
1.1. TLS 两个阶段
TLS 协议可分为两个阶段:
- 握手(Handshake)阶段,其目的是通信双方约定在数据传输阶段使用的加解密算法及密钥(为效率考虑,在数据传输阶段会使用对称密钥算法);
- 数据传输阶段,即发送到网络前加密数据,从网络收到数据后解密数据。
1.2. TLS 报文头
TLS 报文头占 5 字节,第 1 字节是类型(目前有 4 种类型),第 2-3 字节是版本(目前有 4 种版本),第 4-5 字节是长度(不包含报文头本身长度),其格式为:
record type (1 byte) / / version (1 byte major, 1 byte minor) / / / / length (2 bytes) / / / +----+----+----+----+----+ | | | | | | | | | | | | TLS Record header (5 bytes) +----+----+----+----+----+ Record Type Values dec hex ------------------------------------- CHANGE_CIPHER_SPEC 20 0x14 ALERT 21 0x15 HANDSHAKE 22 0x16 APPLICATION_DATA 23 0x17 Version Values dec hex ------------------------------------- SSL 3.0 3,0 0x0300 TLS 1.0 3,1 0x0301 TLS 1.1 3,2 0x0302 TLS 1.2 3,3 0x0303
2. TLS 1.2 Handshake
2.1. Handshake 具体过程
TLS1.2 使用“两个来回”完成握手过程:
TLS1.2 Handshake +-----+ +-----+ | | | | | | ClientHello | | | o----------------------------> | | | | | | | | | | | | ServerHello | | | | [Certificate] | | | | [ServerKeyExchange] | | | | [CertificateRequest] | | | | ServerHelloDone | | | | <----------------------------o | CLIENT | | | | SERVER | | | | | | [Certificate] | | | | ClientKeyExchange | | | | [CertificateVerify] | | | | ** ChangeCipherSpec ** | | | | Finished | | | o----------------------------> | | | | | | | | | | | | ** ChangeCipherSpec ** | | | | Finished | | | | <----------------------------o | | | | | +-----+ +-----+
下面来介绍一下 Handshake 的具体过程。
第 1 步、客户端发送 ClientHello
报文。其主要作用是告诉服务器,本客户端所支持的 TLS 协议版本,以及所支持的加密算法等等。
第 2 步、服务器发送 ServerHello
报文。其主要作用是服务器选择一个它认为安全的,且双方都支持的加密算法;如果服务器认为客户端所有支持的加密算法都不安全,则服务器可以发送一个 ALERT 报文(ALERT 报文是 TLS 顶级报文,参见 1.2 )。
第 3 步、服务器发送 Certificate
报文。其主要作用是服务器发送自己的证书给客户端。
第 4 步、服务器发送 ServerKeyExchange
。其主要作用是提供一些信息,以便双方有足够的信息来约定一个数据传输阶段所使用的对称密钥算法的密钥。这个报文是可选的,如果使用 Diffie-Hellman 方式来约定密钥,则这个是必须的;如果是 RSA 方式来约定密钥,它可以省略,参见后面介绍的 ClientKeyExchange
报文。
第 5 步、服务器发送 CertificateRequest
。其作用是开启“双向认证(Mutual authentication)”模式,即不仅客户端要验证服务器,而且服务器还要验证客户端。这种方式在 https 网站中很少使用,如果对 https 网站进行抓包分析,一般都不会有这个报文。
第 6 步,服务器发送 ServerHelloDone
。其作用是告诉客户端
第 7 步、客户端发送 Certificate
报文(仅当客户端收到了 CertificateRequest
时才发送,即服务器开启了双向认证)。其主要作用是客户端发送自己的证书给服务器。
第 8 步、客户端发送 ClientKeyExchange
报文。其主要作用是提供一些信息,以便双方有足够的信息来约定一个数据传输阶段所使用的对称密钥算法的密钥。如果是 RSA 方式,则客户端生成一个对称密钥算法的密钥后,使用服务器的公钥进行加密后传送给服务器。如果是 Diffie-Hellman 方式,则传送必要信息以便双方可以按约定方式生成同一个密钥。
第 9 步、客户端发送 CertificateVerify
报文(仅当客户端收到了 CertificateRequest
时才发送,即服务器开启了双向认证)。主要作用是客户端发送一段它签名的信息给服务器,这样服务器使用客户端的公钥就可以验证签名,从而验证客户端。
第 10 步、客户端发送 ChangeCipherSpec
报文,告诉服务器你可以使用加密模式了。注: ChangeCipherSpec
报文不属于 Handshake 报文,它是 TLS 顶级报文。
第 11 步、客户端发送 Finished
报文,告诉服务器我准备好加密通信了。
第 12 步、服务器发送 ChangeCipherSpec
报文,告诉客户端你可以使用加密模式了。
第 13 步、服务器发送 Finished
报文,告诉客户端我准备好加密通信了。至此,握手结束。
2.1.1. 单向认证和双向认证
握手阶段,如果服务器发送了 CertificateRequest
,就意味着开启“双向认证”。和“单向认证”相比,“双向认证”在握手阶段多了下面 3 种报文:
- 服务器发送的
CertificateRequest
; - 客户端发送的
Certificate
和CertificateVerify
。
2.1.2. 复用 TLS 协商结果:Session Identifier, Session Ticket
当我们打开一个 https 网页时往往会发送几十个请求,难道要重复几十次 TLS 握手协商吗?当然不用, 有两种方案可以复用第一次请求的 TLS 协商结果:Session Identifier 和 Session Ticket。
这里不详细介绍它们,可参考:
https://en.wikipedia.org/wiki/Transport_Layer_Security#Resumed_TLS_handshake
https://stackoverflow.com/questions/19939247/ssl-session-tickets-vs-session-ids
2.2. Handshake 报文格式
Handshake 报文格式如下所示:
| | | Record Layer | Handshake Layer | | | | | | +----+----+----+----+----+----+----+----+----+------ - -+----+----+----+----+------ - -+ ...... | 22 | | | | | | | | | | | | | | | |0x16| | | | | | | | |message | | | | |message | +----+----+----+----+----+----+----+----+----+------ - -+----+----+----+----+------ - -+ / /--/--/ | \ \----\-----\ | \ \----\-----\ | / / | \ \ \ \ type: 22 / | \ handshake message length \ handshake message length / type type / length: arbitrary (up to 16k) Handshake Type Values dec hex ------------------------------------- HELLO_REQUEST 0 0x00 CLIENT_HELLO 1 0x01 SERVER_HELLO 2 0x02 CERTIFICATE 11 0x0b SERVER_KEY_EXCHANGE 12 0x0c CERTIFICATE_REQUEST 13 0x0d SERVER_HELLO_DONE 14 0x0e CERTIFICATE_VERIFY 15 0x0f CLIENT_KEY_EXCHANGE 16 0x10 FINISHED 20 0x14
注 1:共有 10 种类型的 Handshake 报文,每种类型的具体格式可参考:Traffic Analysis of an SSL/TLS Session
注 2:多个 Handshake 报文可以组合为一个 TLS Record,上面演示中就有两个 Handshake 报文。
2.2.1. ClientHello
ClientHello 报文如下所示:
| | | | Handshake Layer | | - ---+----+----+----+----+----+----+------+----+----------+--------+-----------+----------+ | 1 | | | | | |32-bit| |max 32-bit| Cipher |Compression|Extensions| |0x01| | | | 3 | 1 |random| |session Id| Suites | methods | | - ---+----+----+----+----+----+----+------+----+----------+--------+-----------+----------+ / | \ \---------\ \----\ \ \ / \ \ \ \ SessionId record \ length SSL/TLS \ length \ version SessionId type: 1 (TLS 1.0 here) length CipherSuites +----+----+----+----+----+----+ | | | | | | | | | | | | | | +----+----+----+----+----+----+ \-----\ \-----\ \----\ \ \ \ length cipher Id cipherId Compression methods (no practical implementation uses compression) +----+----+----+ | | | | | 0 | 1 | 0 | +----+----+----+ \-----\ \ \ \ length: 1 cmp Id: 0 Extensions +----+----+----+----+----+----+----- - - | | | | | | | | | | | | | |...extension data +----+----+----+----+----+----+----- - - \-----\ \-----\ \----\ \ \ \ length Extension Extension data Id length
3. Server Name Indication (SNI)
在 TLS 握手时,客户端发送的 ClientHello 中不包括服务器的域名,这一般不会有问题。但随着 HTTP 服务器开启虚拟主机支持后,每个服务器通过相同的 IP 地址可以为很多个网站提供服务。这样服务器在发送 Certificate 报文时不知道应该向客户端提供哪一个网站的证书。2006 年,TLS 协议增加了 Server Name Indication (SNI) 扩展,允许客户端在 ClientHello 报文中带上它所请求的域名,这样服务器就知道客户端想访问哪个网站了,从而提供对应的证书。
参考:https://blog.csdn.net/makenothing/article/details/53292335
3.1. Encrypted SNI (ESNI)
SNI 是没有加密的,“中间人”可以知道这个请求是发往哪个域名的。在 TLS 1.3 中可以使用 Encrypted SNI (ESNI) 对 SNI 进行加密。
4. Application Layer Protocol Negotiation (ALPN)
Application Layer Protocol Negotiation (ALPN, RFC 7301),旨在将原来应用层协议中的协商,提前合并到 TLS 握手时完成,以减少一次往返时间(即不再需要应用层协议中的协商了)。
ALPN 最初是为 Google SPDY 协议(现已标准化为 HTTP/2)而提出的。我们以 HTTP/2 为实例来说明 ALPN。假设 TLS 握手完成了,接下来客户端和服务端就应该开始进行正常的 HTTP 通信了,但是客户端认为 HTTP 协议比较慢,在请求内容之前先向服务端提出“我们能不能使用 HTTP/2 协议”。服务端会根据自己支持 HTTP/2 的情况给出回答,之后客户端会根据服务端的答复,使用 HTTP 或 HTTP/2 向服务端请求内容。可以看到这个过程中又多了一次协商升级协议的往返,导致客户端拿到返回结果的延迟更大了。
为了优化这一点,在 HTTP 使用 TLS 加密的时候,会使用 ALPN 扩展,把升级 HTTP 协议的请求放在 ClientHello 的 ALPN Extension 字段中,服务端在 ServerHello 的 ALPN Extension 字段中回复说“我支持 HTTP/2”。这样 TLS 握手之后,客户端就可以直接按 HTTP/2 发起请求而不必多一次往返了。
4.1. ALPN 检测服务器是否支持 HTTP 2
前面说过,在 TLS 握手阶段,客户端可通过 ALPN 来检测服务器是否支持 HTTP 2。下面以浏览器访问 “https://tmail.com” (它已经支持了 HTTP 2)为例,使用 Wireshark 进行抓包验证。
浏览器第一次发送 ClientHello 报文时,在 ALPN 中携带了浏览器支持的版本,h2 代表浏览器支持 HTTP 2 协议,如图 2 所示。
Figure 2: ClientHello
服务器在返回 ServerHello 报文时,在 ALPN 中返回了 h2,表示服务器支持 HTTP 2 协议,如图 3 所示。
Figure 3: ServerHello
这样,建立 TLS 连接后,浏览器就知道了服务器已经支持 HTTP 2,下一步,可以直接发送“GET / HTTP/2”这样的 HTTP 报文了。
5. TLS 1.3
2018 年 8 月 10 日,IETF 发布了 TLS 1.3(RFC 8446)。这次是 TLS 协议的第一次重大改革,带来了重大的安全性和性能改进。
TLS 1.3 只需要一个 RTT(来回传输一次的时间)就能完成握手,相比 TLS 1.2 省去了一个 RTT(如图 4 和 5 所示,图片摘自:https://www.cloudflare.com/learning-resources/tls-1-3/ )。并且 TLS 1.3 支持“0-RTT”模式,在该模式下客户端可以在握手的同时发送数据,极大地加快了 https 网页的加载速度。
Figure 4: TLS 1.2 握手完成需要“来回两次”
Figure 5: TLS 1.3 握手完成仅需“来回一次”