015: TLS1.2 握手的过程是怎样的?
之前谈到了 HTTP 是明文传输的协议,传输保文对外完全透明,非常不安全,那如何进一步保证安全性呢?
由此产生了 HTTPS
,其实它并不是一个新的协议,而是在 HTTP 下面增加了一层 SSL/TLS 协议,简单的讲,HTTPS = HTTP + SSL/TLS。
那什么是 SSL/TLS 呢?
SSL 即安全套接层(Secure Sockets Layer),在 OSI 七层模型中处于会话层(第 5 层)。之前 SSL 出过三个大版本,当它发展到第三个大版本的时候才被标准化,成为 TLS(传输层安全,Transport Layer Security),并被当做 TLS1.0 的版本,准确地说,TLS1.0 = SSL3.1。
现在主流的版本是 TLS/1.2, 之前的 TLS1.0、TLS1.1 都被认为是不安全的,在不久的将来会被完全淘汰。因此我们接下来主要讨论的是 TLS1.2, 当然在 2018 年推出了更加优秀的 TLS1.3,大大优化了 TLS 握手过程,这个我们放在下一节再去说。
TLS 握手的过程比较复杂,写文章之前我查阅了大量的资料,发现对 TLS 初学者非常不友好,也有很多知识点说的含糊不清,可以说这个整理的过程是相当痛苦了。希望我下面的拆解能够帮你理解得更顺畅些吧 : )
传统 RSA 握手
先来说说传统的 TLS 握手,也是大家在网上经常看到的。我之前也写过这样的文章,(传统RSA版本)HTTPS为什么让数据传输更安全,其中也介绍到了对称加密
和非对称加密
的概念,建议大家去读一读,不再赘述。之所以称它为 RSA 版本,是因为它在加解密pre_random
的时候采用的是 RSA 算法。
TLS 1.2 握手过程
现在我们来讲讲主流的 TLS 1.2 版本所采用的方式。
刚开始你可能会比较懵,先别着急,过一遍下面的流程再来看会豁然开朗。
step 1: Client Hello
首先,浏览器发送 client_random、TLS版本、加密套件列表。
client_random 是什么?用来最终 secret 的一个参数。
加密套件列表是什么?我举个例子,加密套件列表一般张这样:
TLS_ECDHE_WITH_AES_128_GCM_SHA256
意思是TLS
握手过程中,使用ECDHE
算法生成pre_random
(这个数后面会介绍),128位的AES
算法进行对称加密,在对称加密的过程中使用主流的GCM
分组模式,因为对称加密中很重要的一个问题就是如何分组。最后一个是哈希摘要算法,采用SHA256
算法。
其中值得解释一下的是这个哈希摘要算法,试想一个这样的场景,服务端现在给客户端发消息来了,客户端并不知道此时的消息到底是服务端发的,还是中间人伪造的消息呢?现在引入这个哈希摘要算法,将服务端的证书信息通过这个算法生成一个摘要(可以理解为比较短的字符串
),用来标识这个服务端的身份,用私钥加密后把加密后的标识和自己的公钥传给客户端。客户端拿到这个公钥来解密,生成另外一份摘要。两个摘要进行对比,如果相同则能确认服务端的身份。这也就是所谓数字签名的原理。其中除了哈希算法,最重要的过程是私钥加密,公钥解密。
step 2: Server Hello
可以看到服务器一口气给客户端回复了非常多的内容。
server_random
也是最后生成secret
的一个参数, 同时确认 TLS 版本、需要使用的加密套件和自己的证书,这都不难理解。那剩下的server_params
是干嘛的呢?
我们先埋个伏笔,现在你只需要知道,server_random
到达了客户端。
step 3: Client 验证证书,生成secret
客户端验证服务端传来的证书
和签名
是否通过,如果验证通过,则传递client_params
这个参数给服务器。
接着客户端通过ECDHE
算法计算出pre_random
,其中传入两个参数:server_params和client_params。现在你应该清楚这个两个参数的作用了吧,由于ECDHE
基于椭圆曲线离散对数
,这两个参数也称作椭圆曲线的公钥
。
客户端现在拥有了client_random
、server_random
和pre_random
,接下来将这三个数通过一个伪随机数函数来计算出最终的secret
。
step4: Server 生成 secret
刚刚客户端不是传了client_params
过来了吗?
现在服务端开始用ECDHE
算法生成pre_random
,接着用和客户端同样的伪随机数函数生成最后的secret
。
注意事项
TLS的过程基本上讲完了,但还有两点需要注意。
第一、实际上 TLS 握手是一个双向认证的过程,从 step1 中可以看到,客户端有能力验证服务器的身份,那服务器能不能验证客户端的身份呢?
当然是可以的。具体来说,在 step3
中,客户端传送client_params
,实际上给服务器传一个验证消息,让服务器将相同的验证流程(哈希摘要 + 私钥加密 + 公钥解密)走一遍,确认客户端的身份。
第二、当客户端生成secret
后,会给服务端发送一个收尾的消息,告诉服务器之后的都用对称加密,对称加密的算法就用第一次约定的。服务器生成完secret
也会向客户端发送一个收尾的消息,告诉客户端以后就直接用对称加密来通信。
这个收尾的消息包括两部分,一部分是Change Cipher Spec
,意味着后面加密传输了,另一个是Finished
消息,这个消息是对之前所有发送的数据做的摘要,对摘要进行加密,让对方验证一下。
当双方都验证通过之后,握手才正式结束。后面的 HTTP 正式开始传输加密报文。
RSA 和 ECDHE 握手过程的区别
ECDHE 握手,也就是主流的 TLS1.2 握手中,使用
ECDHE
实现pre_random
的加密解密,没有用到 RSA。使用 ECDHE 还有一个特点,就是客户端发送完收尾消息后可以提前
抢跑
,直接发送 HTTP 报文,节省了一个 RTT,不必等到收尾消息到达服务器,然后等服务器返回收尾消息给自己,直接开始发请求。这也叫TLS False Start
。