ProtocolHTTPS

HTTPS 温故知新(三) —— 直观感受 TLS 握手流程(上)

在 HTTPS 开篇的文章中,笔者分析了 HTTPS 之所以安全的原因是因为 TLS 协议的存在。TLS 能保证信息安全和完整性的协议是记录层协议。(记录层协议在上一篇文章中详细分析了)。看完上篇文章的读者可能会感到疑惑,TLS 协议层加密的密钥是哪里来的呢?客户端和服务端究竟是如何协商 Security Parameters 加密参数的?这篇文章就来详细的分析一下 TLS 1.2 和 TLS 1.3 在 TLS 握手层上的异同点。

TLS 1.3 在 TLS 1.2 的基础上,针对 TLS 握手协议最大的改进在于提升速度和安全性。本篇文章会重点分析这两块。

一. TLS 对网络请求速度的影响

由于部署了 HTTPS,传输层增加了 TLS,对一个完成的请求耗时又会多增加一些。具体会增加几个 RTT 呢?

先来看看一个请求从零开始,完整的需要多少个 RTT。假设访问一个 HTTPS 网站,用户从 HTTP 开始访问,到收到第一个 HTTPS 的 Response,大概需要经历一下几个步骤(以目前最主流的 TLS 1.2 为例):

流程 消耗时间 总计
1. DNS 解析网站域名 1-RTT
2. 访问 HTTP 网页 TCP 握手 1-RTT
3. HTTPS 重定向 302 1-RTT
4. 访问 HTTPS 网页 TCP 握手 1-RTT
5. TLS 握手第一阶段 Say Hello 1-RTT
6. 【证书校验】CA 站点的 DNS 解析 1-RTT
7. 【证书校验】CA 站点的 TCP 握手 1-RTT
8. 【证书校验】请求 OCSP 验证 1-RTT
9. TLS 握手第二阶段 加密 1-RTT
10. 第一个 HTTPS 请求 1-RTT
10-RTT

在上面这些步骤中,1、10 肯定无法省去,6、7、8 如果浏览器本地有缓存,是可选的。将剩下的画在流程图上,见下图:

针对上面的步骤进行一些说明:

用户第一次访问网页需要解析 DNS,DNS 解析以后会被浏览器缓存下来,只要是 TTL 没有过期,期间的所有访问都不需要再耗 DNS 的解析时间了。另外如果有 HTTPDNS,也会缓存解析之后的结果。所以第一步并非每次都要花费 1-RTT。

如果网站做了 HSTS (HTTP Strict Transport Security),那么上面的第 3 步就不存在,因为浏览器会直接替换掉 HTTP 的请求,变成 HTTPS 的,防止重定向的中间人攻击。

如果浏览器有主流 CA 的域名解析缓存,也不需要进行上面的第 6 步,直接访问即可。

如果浏览器关闭掉了 OCSP 或者是有本地缓存,那么也不需要进行上面的第 7 和第 8 步。

上面这 10 步是最最完整的流程,一般有各种缓存不会经历上面每一步。如果有各种缓存,并且有 HSTS 策略,所以用户每次访问网页都必须要经历的流程如下:

流程 消耗时间 总计
1. 访问 HTTPS 网页 TCP 握手 1-RTT
2. TLS 握手第一阶段 Say Hello 1-RTT
3. TLS 握手第二阶段 加密 1-RTT
4. 第一个 HTTPS 请求 1-RTT
4-RTT

除去 4 是无论如何都无法省掉的以外,剩下的就是 TCP 和 TLS 握手了。 TCP 想要减至 0-RTT,目前来看有点难。那 TLS 呢?目前 TLS 1.2 完整一次握手需要 2-RTT,能再减少一点么?答案是可以的。

二. TLS/SSL 协议概述

TLS 握手协议运行在 TLS 记录层之上,目的是为了让服务端和客户端就协议版本达成一致, 选择加密算法, 选择性的彼此相互验证对方, 使用公钥加密技术生成共享密钥——即协商出 TLS 记录层加密和完整性保护需要用到的 Security Parameters 加密参数。协商的过程中还必须要保证网络中传输的信息不能被篡改,伪造。由于协商需要在网络上来来回回花费几个来回,所以 TLS 的网络耗时基本上很大一部分花费在网络 RTT 上了。

和加密参数关系最大的是密码套件。客户端和服务端在协商过程中需要匹配双方的密码套件。然后双方握手成功以后,基于密码套件协商出所有的加密参数,加密参数中最重要的就是主密钥(master secret)。

握手协议主要负责协商一个会话,这个会话由以下元素组成:

  • session identifier:
    由服务端选取的一个任意字节的序列用于辨识一个活动的或可恢复的连接状态。

  • peer certificate:
    对端的 X509v3 [PKIX]证书。这个字段可以为空。

  • compression method:
    加密之前的压缩算法。这个字段在 TLS 1.2 中用的不多。在 TLS 1.3 中这个字段被删除。

  • cipher spec:
    指定用于产生密钥数据的伪随机函数(PRF),块加密算法(如:空,AES 等),和 MAC 算法(如:HMAC-SHA1)。它也定义了密码学属性如 mac_length。这个字段在 TLS 1.3 标准规范中已经删除,但是为了兼容老的 TLS 1.2 之前的协议,实际使用中还可能存在。在 TLS 1.3 中,密钥导出用的是 HKDF 算法。具体 PRF 和 HKDF 的区别会在之后的一篇文章中详细分析。

  • master secret:
    client 和 server 之间共享的 48 字节密钥。

  • is resumable:
    一个用于标识会话是否能被用于初始化新连接的标签。

上面这些字段随后会被用于产生安全参数并由记录层在保护应用数据时使用。利用TLS握手协议的恢复特性,使用相同的会话可以实例化许多连接。

TLS 握手协议包含如下几步:

  • 交换 Hello 消息, 交换随机数和支持的密码套件列表, 以协商出密码套件和对应的算法。检查会话是否可恢复
  • 交换必要的密码参数以允许 client 和 server 协商预备主密钥 premaster secret
  • 交换证书和密码信息以允许 client 和 server 进行身份认证
  • 从预备主密钥 premaster secret 和交换的随机数中生成主密钥 master secret
  • 为 TLS 记录层提供安全参数(主要是密码块)
  • 允许 client 和 server 验证它们的对端已经计算出了相同的安全参数, 而且握手过程不被攻击者篡改

下面行文思路会按照 TLS 首次握手,会话恢复的顺序,依次对比 TLS 1.2 和 TLS 1.3 在握手上的不同,并且结合 Wireshark 抓取实际的网络包进行分析讲解。最后分析一下 TLS 1.3 新出的 0-RTT 是怎么回事。

三. TLS 1.2 首次握手流程

TLS 1.2 握手协议主要流程如下:

Client 发送一个 ClientHello 消息,Server 必须回应一个 ServerHello 消息或产生一个验证的错误并且使连接失败。ClientHello 和 ServerHello 用于在 Client 和 Server 之间建立安全性增强的能力。ClientHello 和 ServerHello 建立了如下的属性: 协议版本,会话 ID,密码套件,压缩算法。此外,产生并交换两个随机数: ClientHello.random 和 ServerHello.random。

密钥交换中使用的最多 4 个消息: Server Certificate, ServerKeyExchange, Client Certificate 和 ClientKeyExchange。新的密钥交换方法可以通过这些方法产生:为这些消息指定一个格式, 并定义这些消息的用法以允许 Client 和 Server 就一个共享密钥达成一致。这个密钥必须很长;当前定义的密钥交换方法交换的密钥大于 46 字节。

在 hello 消息之后, Server 会在 Certificate 消息中发送它自己的证书,如果它即将被认证。此外,如果需要的话,一个 ServerKeyExchange 消息会被发送(例如, 如果 Server 没有证书, 或者它的证书只用于签名,RSA 密码套件就不会出现 ServerKeyExchange 消息)。如果 Server 被认证过了,如果对于已选择的密码套件来说是合适的话,它可能会要求 Client 发送证书。接下来,Server 会发送 ServerHelloDone 消息,至此意味着握手的 hello 消息阶段完成。Server 将会等待 Client 的响应。如果 Server 发送了一个 CertificateRequest 消息,Client 必须发送 Certificate 消息。现在 ClientKeyExchange 消息需要发送, 这个消息的内容取决于 ClientHello 和 ServerHello 之间选择的公钥算法。如果 Client 发送了一个带签名能力的证书, 则需要发送以一个数字签名的 CertificateVerify 消息,以显式验证证书中私钥的所有权。

这时,Client 发送一个 ChangeCipherSpec 消息,并且复制 pending 的 Cipher Spec 到当前的 Cipher Spec 中. 然后 Client 在新算法, 密钥确定后立即发送 Finished 消息。作为回应,Server 会发送它自己的 ChangeCipherSpec 消息, 将 pending 的 Cipher Spec 转换为当前的 Cipher Spec,在新的 Cipher Spec 下发送 Finished 消息。这时,握手完成,Client 和 Server 可以开始交换应用层数据。应用数据一定不能在第一个握手完成前(在一个非TLS_NULL_WITH_NULL_NULL 类型的密码套件建立之前)发送。

用经典的图表示一次完成的握手就是下图:

      Client