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协议可分为两个阶段:
1、握手(Handshake)阶段,其目的是通信双方约定在数据传输阶段使用的加解密算法及密钥(为效率考虑,在数据传输阶段会使用对称密钥算法);
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
TLS 1.2 Handshake
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
报文,告诉客户端我准备好加密通信了。至此,握手结束。
单向认证和双向认证
握手阶段,如果服务器发送了 CertificateRequest
,就意味着开启“双向认证”。和“单向认证”相比,“双向认证”在握手阶段多了下面3种报文:
- 服务器发送的
CertificateRequest
; - 客户端发送的
Certificate
和CertificateVerify
。
参考:An Introduction to Mutual SSL Authentication
复用TLS协商结果:Session Identifier, Session Ticket
当我们打开一个https网页时往往会发送几十个请求,难道要重复几十次TLS握手协商吗?当然不用, 有两种方案可以复用第一次请求的TLS协商结果:Session Identifier和Session Ticket。
这里不详细介绍它们,可参考:
https://en.wikipedia.org/wiki/Transport_Layer_Security#Resumed_TLS_handshake
https://*.com/questions/19939247/ssl-session-tickets-vs-session-ids
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报文。
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
Server Name Indication (SNI)
在TLS握手时,客户端发送的ClientHello中不包括服务器的域名,这一般不会有问题。但随着HTTP服务器开启虚拟主机支持后,每个服务器通过相同的IP地址可以为很多个网站提供服务。这样服务器在发送Certificate报文时不知道应该向客户端提供哪一个网站的证书。2006年,TLS协议增加了Server Name Indication (SNI) 扩展,允许客户端在ClientHello报文中带上它所请求的域名,这样服务器就知道客户端想访问哪个网站了,从而提供对应的证书。
参考:https://blog.csdn.net/makenothing/article/details/53292335
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发起请求而不必多一次往返了。
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 报文了。
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握手完成仅需“来回一次”
参考
The Transport Layer Security (TLS) Protocol Version 1.2
Traffic Analysis of an SSL/TLS Session
使用wireshark观察SSL/TLS握手过程--双向认证/单向认证