TLS 详解握手流程

22,046 阅读10分钟

字节面试官:TLS 三个版本的握手方式,你都了解吗?

TLS 握手的过程比较复杂,就如上面的问题,我之前的理解很是零碎,面试时回答地并不好。现在闲下来,写下此文加深理解。

具体梳理如下:

  1. TLS 的历史。
  2. 各版本的握手细节与总结。
  3. 扩展。

SSL/TLS 历史

我们知道,HTTP 是明文传输的协议,可能受到第三方的攻击,非常不安全。因此才诞生 “HTTPS”。

这个 “S” 表示 SSL/TLS 协议,用公式说明:HTTPS = HTTP + SSL(TLS)

其中 SSL 即安全套接层(Secure Sockets Layer),处于 OSI 七层模型中的会话层。

带有 TLS 安全协议的网站,如 掘金 在 Chrome 浏览器地址栏上会将 https/www 等自动隐藏,以 🔐 代之:

以下是权威文档摘取的部分历史:

  • 1996 年,SSL3.0 问世,得到大规模应用(已于 2015 年弃用)。

  • 1999 年,SSL3.1 被标准化,更新为 TLS1.0(传输层安全,Transport Layer Security)。所以有,TLS1.0 = SSL3.1

  • 现在主流的版本是 TLS1.2(2008 年发布)。

  • 未来,可能是 TLS1.3 的时代(2018 年发布)。

TLS 握手

要了解具体的握手流程,首先我们要了解何为握手?

握手 (Handshake):一般是通信的前置动作,即达成某种约定,比如 TCP 握手是要确定双端的接收、发送能力等;而 TLS 握手则是为了验证身份、交换信息从而生成秘钥,为后续加密通信做准备。

通过 Wireshark 抓包我们不难发现:不论客户端和服务端的连接走 HTTP 还是 TLS 协议,所有连接最初都要经过 TCP 三次握手,而 TLS 四次握手是在 TCP 建立连接之后进行的。

因此,HTTPS 首次通信需要 7 次握手!

详细的 TCP 学习资料可以查阅这里:TCP灵魂之问,不是本节的重点。

本节将会解析 TLS 主要的两种握手方式,分别为:RSA 握手DH 握手。再以 DH 握手 为基础继续演进优化,推出更安全、性能更佳的 TLS1.3 版本握手方式。不过在此之前,还需要了解一些基础概念 (若已了解可跳过)。

基础概念

① 对称加密

加、解密使用的同一串密钥,常见的对称加密算法:DES,AES 等。

② 非对称加密

加、解密使用不同的密钥,一把作为公开的公钥,另一把作为保密的私钥。公钥加密的信息,只有私钥才能解密。反之,私钥加密的信息,只有公钥才能解密。

常见的非对称加密算法:RSA,ECC 等。

RSA 算法:该算法的命名以三位科学家的姓氏缩写组合得来,在计算机网络世界,一直是最广为使用的 “非对称加密算法”。

ECC 是非对称加密里的 “后起之秀”,它基于 “椭圆曲线离散对数” 的数学难题,使用特定的曲线方程和基点生成公钥和私钥,子算法 ECDHE 用于密钥交换,ECDSA 用于数字签名。

③ 混合加密

对称加密 算法中只要持有密钥就可以解密。如果你和网站约定的密钥在传递途中被黑客窃取,那他就可以在之后随意解密收发的数据,通信过程也就没有机密性可言了。

非对称加密 算法中,需要应用到复杂的数学运算,虽然保证了安全,但速度很慢,比对称加密算法差了好几个数量级。

所以,TLS 里使用了 “混合加密” 的方式博采众长:在通信刚开始的时候使用 非对称加密 算法,解决密钥交换的问题。后续全都使用 对称加密 进行通信。

加密套件列表

加密套件列表一般由客户端发送,供服务器选择。用 openssl 查看列表:

➜ openssl ciphers -v
ECDHE-RSA-AES256-GCM-SHA384  	TLSv1.2 Kx=ECDH     Au=RSA    Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 	TLSv1.2 Kx=ECDH     Au=ECDSA  Enc=AESGCM(256) Mac=AEAD
AES256-SHA256                     TLSv1.2 Kx=RSA      Au=RSA    Enc=AES(256)   Mac=SHA256
...
... Cipher Suites(17 suites)

上图数据表明列表附带了多达 17 个加密套件,单个拎出来一般长这样:

意思是:

  • TLS 握手过程中,使用 ECDHE 算法生成 pre_random。
  • 身份验证 (签名) 使用 RSA 算法。
  • 128 位的 AES 算法进行对称加密,在对称加密的过程中使用主流的 GCM 分组模式,可以让算法用固定长度的密钥加密任意长度的明文。
  • 最后是用于数据完整性校验的哈希摘要算法,采用 SHA256 算法。

数字证书

数字证书一般有两个作用:

  1. 服务器向浏览器证明自己的身份,毕竟秘钥、甚至服务器域名都是可以伪造的。
  2. 把公钥传给浏览器。

证书本身是由权威、受信任的证书颁发机构 (CA) 授予的。

RSA 握手

先看图

具体流程如下:

 1.浏览器向服务器发送随机数 client_random,TLS 版本和供筛选的加密套件列表。
 2.服务器接收到,立即返回 server_random,确认好双方都支持的加密套件
 以及数字证书 (证书中附带公钥 Public key certificate)。
 3.浏览器接收,先验证数字证书。若通过,接着使用加密套件的密钥协商算法 RSA 
 算法生成另一个随机数 pre_random,并且用证书里的公钥加密,传给服务器。
 4.服务器用私钥解密这个被加密后的 pre_random,参考 “非对称加密”。

现在浏览器和服务器都拥有三样相同的凭证:client_random、server_random 和 pre_random。两者都用筛好的加密套件中的加密方法混合这三个随机数,生成最终的密钥。

最后,浏览器和服务器使用相同的密钥进行通信,即使用 对称加密

到这里,还有两点需要注意。

第一:握手中的任何消息均未使用秘钥加密,它们都是明文发送的。

第二:TLS 握手其实是一个 双向认证 的过程,客户端和服务器都需要进行摘要认证以及收尾 (发送 Finished 消息,意为后面 HTTP 开始正式加密报文通信了)。

DH 握手

还是看图:

具体流程如下(放出 diff 比较):

 1.浏览器向服务器发送随机数 client_random,TLS 版本和供筛选的加密套件列表。

// RSA
-2.服务器接收到,立即返回 server_random,确认好双方都支持的加密套件
-以及数字证书 (证书中附带公钥)。
// DH
+2.服务器接收到,立即返回 server_random,确认好双方都支持的加密套件
+以及数字证书 (证书中附带公钥)。
+同时服务器利用私钥将 client_random,server_random,server_params 签名,
+生成服务器签名。然后将签名和 server_params 也发送给客户端。 
+这里的 server_params 为 DH 算法所需参数。

// RSA
-3.浏览器接收,先验证数字证书。
-若通过,接着使用加密套件的密钥协商算法 RSA 算法
-生成另一个随机数 pre_random,并且用证书里的公钥加密,传给服务器。
// DH
+3.浏览器接收,先验证数字证书和 _签名_。
+若通过,将 client_params 传递给服务器。
+这里的 client_params 为 DH 算法所需参数。

-4.服务器用私钥解密这个被加密后的 pre_random,参考 “非对称加密”。
+4.现在客户端和服务器都有 client_params、server_params 两个参数,
+因 ECDHE 计算基于 “椭圆曲线离散对数”,通过这两个 DH 参数就能计算出 pre_random。

现在浏览器和服务器都拥有三样相同的凭证:client_random、server_random 和 pre_random,后续步骤与 RSA 握手一致。

DH 握手前向安全性

TLS1.2 握手

有了前面一节的概念后,TLS1.2 握手理解起来就显得毫不费力了。因为主流的 TLS1.2 握手就是上节完整的 DH 握手流程。

TLS1.3 握手

互联网的世界飞速发展,随着时间的推移,人们早已不满足于现有的 TLS1.2。于是,更快、更安全的 “船新版本” TLS1.3 发布了。

TLS1.3 废除了原有的部分不安全的加密算法,其中甚至包括 RSA 算法。

RSA 算法的废除不仅因为已经有大能将其破解,同时还缺少 前向安全性。何为前向安全?即能够保护过去进行的通讯不受密码或密钥在未来暴露的威胁。

现在我们来看看 TLS1.3 握手具体流程,先放图:

流程梳理:

// 原 DH 握手
-1.浏览器向服务器发送 client_random,TLS 版本和供筛选的加密套件列表。
// TLS1.3 优化
+1.浏览器向服务器发送 client_params,client_random,TLS 版本和供筛选的加密套件列表。

// 原 DH 握手
-2...
// TLS1.3 优化
+2.服务器返回:server_random、server_params、TLS 版本、确定的加密套件方法以及证书。
+浏览器接收,先验证数字证书和签名。
+现在双方都有 client_params、server_params,可以根据 ECDHE 计算出 pre_random 了。

最后,集齐三个参数,生成最终秘钥。

如你所见,TLS1.3 客户端和服务器之间只需要一次往返就完成 (TLS1.2 需要两次往返来完成握手),即 1-RTT 握手。当然,如果利用 PSK 我们甚至能优化到 0-RTT (这并不好,安全受到质疑~)。

小结

以上,我们梳理了三个版本的 TLS 握手流程,它们其实属于两类:一种基于 RSA,另一种基于 Diffie-Hellman (ECDHE)。这两类握手的区别仅在于如何实现秘钥建立和身份验证:

秘钥交换 身份验证
RSA 握手 RSA RSA
DH 握手 ECDHE RSA/DSA

我们看到,在版本演进中,最新的 TLS1.3 抛弃了 RSA 算法是为了更安全,减少 RTT 是为了更快。可知,互联网技术革新的特点不外如是。

扩展

前文提到,握手是一个 双向认证 的过程,当然这并不绝对。

在握手的第三步,客户端验证证书的时候,如果服务端的证书在系统默认信任证书列表中则会直接通过,如果不在默认列表中,浏览器可能会弹窗让用户选择是否信任该证书,也有可能会直接关闭连接,提示证书不可信。

而在 App 内,如果想要信任未在系统信任列表中的证书,则需要在应用内提前置入服务端证书。此外,关于认证方式,大多数也都是采用的单向认证,也就是说仅仅认证服务端的证书,而像银行等机构则多使用双向认证的方式。

当然,握手的细节还有很多,由于篇幅原因,这里不再细讲,让我们回过头来,讲讲协议的核心之一:加密算法

大数据时代,需要收集大量的个人信息用于统计。

一方面它给我们带来了便利,与财富捆绑,如利用 哈希 算法的 区块链

另一方面一些个人信息数据在无意间被泄露,产生不良后果,就如某年的一起舆论自杀事故,被告人的个人数据在网络上爆发,再加上群众的盲目导向,最终导致被告人不堪精神压力酿成悲剧,而起因只是一件本可以轻易协商解决的事件。

这样的例子多不胜数,数据泄露的后果无法估量。

为了保护客户的隐私数据,不论是 HTTP 还是 HTTPS,都建议密文传输信息。当然数据加解密带来的性能消耗,也需要各位开发者各自衡量。

网络世界,越了解,便越敬畏。

最后

文章产出不易,还望各位小伙伴们支持一波!如有误,望指正。

往期精选:

「中高级前端面试」手写代码合集

#fragment 本文锚点跳转测试

参考资料

wiki/HTTPS

HTTP 灵魂之问 —— 神三元

HTTPS 权威指南

Keyless SSL: The Nitty Gritty Technical Details

The First Few Milliseconds of an TLS 1.2 Connection

TLS 1.3 Handshake: Taking a Closer Look

直观感受 TLS 握手流程

RFC5246

本文使用 mdnice 排版