https
一般情况下,不管 TLS 握手次数如何,都得先经过 TCP 三次握手后才能进行,因为 HTTPS 都是基于 TCP 传输协议实现的,得先建立完可靠的 TCP 连接才能做 TLS 握手的事情。
tls1.2 需要四次握手(客户-服务,服务-客户,客户-服务,服务-客户), 2RTT tls1.3 需要两次(客户-服务,服务-客户),1RTT tls1.3 还有个更厉害到地方在于会话恢复机制,在重连 TLvS1.3 只需要 0-RTT (TCP 握手时完成 https 连接)
「HTTPS 中的 TLS 握手过程可以同时进行三次握手」,这个场景是可能存在到,但是在没有说任何前提条件,而说这句话就等于耍流氓。需要下面这两个条件同时满足才可以:
- 客户端和服务端都开启了 TCP Fast Open 功能,且 TLS 版本是 1.3;
- 客户端和服务端已经完成过一次通信;
RSA 密钥交换算法(四次握手)
以最简单的 RSA 密钥交换算法,来看看它的 TLS 握⼿过程
传统的 TLS 握⼿基本都是使⽤ RSA 算法来实现密钥交换的,在将 TLS 证书部署服务端时,证书⽂件中包含⼀对公私钥,其中公钥会在 TLS 握⼿阶段传递给客户端,私钥则⼀直留在服务 端,⼀定要确保私钥不能被窃取。
在 RSA 密钥协商算法中,客户端会⽣成随机密钥,并使⽤服务端的公钥加密后再传给服务端。根据⾮对称加密算法,公钥加密的消息仅能通过私钥解密,这样服务端解密后,双⽅就得到了相同的密钥,再⽤它加密应⽤消息
TLS 第⼀次握⼿
客户端⾸先会发⼀个「Client Hello」消息,字⾯意思我们也能理解到,这是跟服务器「打招呼」。 消息⾥⾯有客户端使⽤的 TLS 版本号、⽀持的密码套件列表,以及⽣成的随机数(Client Random),这个随机数会被服务端保留,它是⽣成对称加密密钥的材料之⼀。
TLS 第⼆次握⼿
第二次握手服务端会发送三个消息
当服务端收到客户端的「Client Hello」消息后,会确认 TLS 版本号是否⽀持,和从密码套件列表中选择⼀个密码 套件,以及⽣成随机数(Server Random)。
接着,返回「Server Hello」消息,消息⾥⾯有服务器确认的 TLS 版本号,也给出了随机数(Server Random), 然后从客户端的密码套件列表选择了⼀个合适的密码套件。
密码套件基本的形式是「密钥交换算法 + 签名算法 + 对称加密算法 + 摘要算法」, 握⼿时密钥交换算法是使⽤ RSA; 握⼿后的通信使⽤ AES 对称算法,密钥⻓度 128 位,分组模式是 GCM; 摘要算法 SHA384 ⽤于消息认证和产⽣随机数; 然后,服务端为了证明⾃⼰的身份,会发送「Server Certificate」给客户端,这个消息⾥含有数字证书 随后,服务端发了「Sever Hello Done」消息,⽬的是告诉客户端,我已经把该给你的东⻄都给你了,本次打招呼完毕。
TLS 第三次握⼿
客户端验证发来的 CA 和本地 CA 是否一致
客户端验证完证书后,认为可信则继续往下⾛。接着,客户端就会⽣成⼀个新的随机数 (pre-master),⽤服务器 的 RSA 公钥加密该随机数,通过「Change Cipher Key Exchange」消息传给服务端。
服务端收到后,⽤ RSA 私钥解密,得到客户端发来的随机数 (pre-master)。
⾄此,客户端和服务端双⽅都共享了三个随机数,分别是 Client Random、Server Random、pre-master。 于是,双⽅根据已经得到的三个随机数,⽣成会话密钥(Master Secret),它是对称密钥,⽤于对后续的 HTTP 请求/响应的数据加解密。 ⽣成完会话密钥后,然后客户端发⼀个「Change Cipher Spec」,告诉服务端开始使⽤加密⽅式发送消息。
然后,客户端再发⼀个「Encrypted Handshake Message(Finishd)」消息,把之前所有发送的数据做个摘要,再⽤会话密钥(master secret)加密⼀下,让服务器做个验证,验证加密通信是否可⽤和之前握⼿信息是否有 中途篡改过
只有第三个随机数是经过加密的
TLS 第四次握⼿
服务器也是同样的操作,发「Change Cipher Spec」和「Encrypted Handshake Message」消息,如果双⽅都 验证加密和解密没问题,那么握⼿正式完成。
CA
⾸先 CA 会把持有者的公钥、⽤途、颁发者、有效时间等信息打成⼀个包,然后对这些信息进⾏ Hash 计算,得到⼀个 Hash 值;(数字签名) 然后 CA 会使⽤⾃⼰的私钥将该 Hash 值加密,⽣成 Certificate Signature,也就是 CA 对证书做了签名; 最后将 Certificate Signature 添加在⽂件证书上,形成数字证书; 客户端校验服务端的数字证书的过程 ⾸先客户端会使⽤同样的 Hash 算法获取该证书的 Hash 值 H1; 通常浏览器和操作系统中集成了 CA 的公钥信息,浏览器收到证书后可以使⽤ CA 的公钥解密 Certificate Signature 内容,得到⼀个 Hash 值 H2 ;
最后⽐较 H1 和 H2,如果值相同,则为可信赖的证书,否则则认为证书不可信。
证书信任链
证书的验证过程中还存在⼀个证书信任链的问题,因为我们向 CA 申请的证书⼀般不是根证书签发的, ⽽是由中间证书签发的,⽐如百度的证书,从下图你可以看到,证书的层级有三级 最开始客户端只信任根证书 GlobalSign Root CA 证书的,然后 “GlobalSign Root CA” 证书信任 “GlobalSign Organization Validation CA - SHA256 - G2” 证书,⽽ “GlobalSign Organization Validation CA - SHA256 - G2” 证书⼜信任 baidu.com 证书,于是客户端也信任 baidu.com 证书。 总括来说,由于⽤户信任 GlobalSign,所以由 GlobalSign 所担保的 baidu.com 可以被信任,另外由于⽤户信任操作系统或浏览器的软件商,所以由软件商预载了根证书的 GlobalSign 都可被信任
缺点
使⽤ RSA 密钥协商算法的最⼤问题是不⽀持前向保密。因为客户端传递随机数(⽤于⽣成对称加密密钥的条件之 ⼀)给服务端时使⽤的是公钥加密的,服务端收到后,会⽤私钥解密得到随机数。所以⼀旦服务端的私钥泄漏 了,过去被第三⽅截获的所有 TLS 通讯密⽂都会被破解。
RSA 密钥协商算法「不⽀持」前向保密,ECDHE 密钥协商算法「⽀持」前向保密;
ECDHE
使⽤了 ECDHE,在 TLS 第四次握⼿前,客户端就已经发送了加密的 HTTP 数据,⽽对于 RSA 握⼿过程,必须要完成 TLS 四次握⼿,才能传输应⽤数据。
所以,ECDHE 相⽐ RSA 握⼿过程省去了⼀个消息往返的时间,这个有点「抢跑」的意思,它被称为是「TLS False Start」,跟「TCP Fast Open」有点像,都是在还没连接完全建⽴前,就发送了应⽤数据,这样便提⾼了传输的效率。
1RTT 握手
TLS 1.3 的握⼿过程,可以发现 TLS 1.3 把 Hello 和公钥交换这两个消息合并成了⼀个消息, 于是这样就减少到只需 1 RTT 就能完成 TLS 握⼿。 怎么合并的呢?具体的做法是,客户端在 Client Hello 消息⾥带上了⽀持的椭圆曲线,以及这些椭圆曲线对应的公钥
0RTT 握手
TLS 握⼿的 ⽬的就是为了协商出会话密钥,也就是对称加密密钥,那我们如果我们把⾸次 TLS 握⼿协商的对称加密密钥缓存起来,待下次需要建⽴ HTTPS 连接时,直接「复⽤」这个密钥,不就减少 TLS 握⼿的性能损耗了吗? 这种⽅式就是会话复⽤(TLS session resumption),会话复⽤分两种: 第⼀种叫 Session ID; 第⼆种叫 Session Ticket; 服务器必须保持每⼀个客户端的会话密钥,随着客户端的增多,服务器的内存压⼒也会越⼤。
现在⽹站服务⼀般是由多台服务器通过负载均衡提供服务的,客户端再次连接不⼀定会命中上次访问过的服 务器,于是还要⾛完整的 TLS 握⼿过程;
为了解决 Session ID 的问题,就出现了 Session Ticket,服务器不再缓存每个客户端的会话密钥,⽽是把缓存的⼯作交给了客户端,类似于 HTTP 的 Cookie。 客户端与服务器⾸次建⽴连接时,服务器会加密「会话密钥」作为 Ticket 发给客户端,交给客户端缓存该 Ticket。 客户端再次连接服务器时,客户端会发送 Ticket,服务器解密后就可以获取上⼀次的会话密钥,然后验证有效期, 如果没问题,就可以恢复会话了,开始加密通信
对于集群服务器的话,要确保每台服务器加密 「会话密钥」的密钥是⼀致的,这样客户端携带 Ticket 访问任意⼀ 台服务器时,都能恢复会话。 Session ID 和 Session Ticket 都不具备前向安全性,因为⼀旦加密「会话密钥」的密钥被破解或者服务器泄漏「会 话密钥」,前⾯劫持的通信密⽂都会被破解