TLS1.2握手流程分析(RSA,ECDHE),和TLS1.3区别
一、加密算法简介
HTTPS常用的密钥交换算法有两种,分别是RSA和ECDHE算法。
其中,RSA是比较传统的密钥交换算法,它不具备前向安全(指的是长期使用的主密钥泄漏不会导致过去的会话密钥泄漏)的性质,因此现在很少服务器使用它。而ECDHE算法具有前向安全,所以被广泛使用。
1.2 RSA简要流程
- a生成公钥pk跟私钥sk,公钥公开任何人都可以获得,私钥保密
- b获取a的公钥,然后用a的公钥进行信息加密
- a得到加密后的信息,用自己的私钥解密
公钥加密的信息只有私钥解得开,那么只要私钥不泄漏,通信就是安全的
总结:双方交换公钥,即可对传输的数据进行加解密
1.1 ECDHE简要流程
算法演变:DH算法 -- > DHE算法 -- > ECDHE算法(相对于DH做了效率的优化)
总体的思想为
对于公式
A = G ^ a % P
B = G ^ b % P
其中G,P为公开的常数。当已知a时,可以推算出A;反之,当已知A时,几乎无法推算出a。
映射到加密算法中,a为私钥,A为公钥
并且以下两个表达式的计算结果相等
B ^ a % P = A ^ b % P
流程为:
- 双方确定好算法的公共参数G,P
- 服务端生成随机私钥a,算出公钥A (G ^ a % P)。客户端生成随机私钥b,算出公钥B (G ^ b % P)
- 双方交换公钥,服务端计算B ^ a % P,客户端计算A ^ b % P,由上述结论可知二者相等,实现了共享密钥
此过程双方的私钥都是随机、临时生成的,不公开的
总结:双方约定公共参数,然后各自生成临时的公私钥对,双方交换公钥。于是可以用各自的私钥和对方的公钥,算出共享密钥
二、TLS1.2 握手消息简介
总体流程如下图所示:
1、客户端发送
- ClientHello
用于初始化会话消息,该消息主要包含如下信息:
-
- Version Number: 客户端发送它所支持的最高 SSL/TLS 版本
- Randomly Generated Data:一个 32 字节的客户端随机数,该随机数被服务端生成通信用的对称密钥(master secret)
- Session Identification :session ID 被客户 端用于恢复之前的会话
- Cipher Suite: 客户端发送它所支持的加密套件列表
2、服务端发送
- ServerHello
该消息主要包含如下信息:
-
- Version Number:服务端发送双发所支持的最高的 SSL/TLS 版本
- Randomly Generated Data:一个 32 字节的服务端随机数,被客户端用于生成通信用的对称密钥(master secret)
- Session Identification:用于会话恢复
- Cipher Suite: 服务端发送双发支持的最安全的加密套件
- ServerCertificate:服务端下发SSL证书,客户端用该证书验证服务端的身份
- ServerKeyExchange:这个消息是可选的,该消息主要用来传递双方协商密钥的参数
- ClientCertificateRequest:这个消息也是可选的,只有当服务端也需要验证客户端身份会用到
- ServerHelloDone:告知客户端服务端这边握手相关的消息发送完毕,等待客户端响应
3、客户端发送
- ClientCertificate:如果服务端发送了ClientCertificateRequest消息,那么客户端会发送该消息给服务端,包含自己的证书信息,供服务端进行客户端身份认证
- ClientKeyExchange:根据协商的密钥算法不同,该消息的内容会不同,该消息主要包含密钥协商的参数
- CertificateVerify:该消息只有在ClientCertificate消息发送时才发送。客户端通过自己的私钥签名从开始到现在的所有发送过的消息,然后服务端会用客户端的公钥验证这个签名
- ChangeCipherSpec:通知对方此消息以后会以之前协商的密钥加密发送数据
- Finished:客户端计算生成对称密钥,然后使用该对称密钥加密之前所有收发握手消息的 Hash 值,发送给服务器,服务器将用相同的会话密钥(使用相同方法生成)解密此消息,校验其中的Hash 值。该消息是 SSL 握手协议记录层加密的第一条消息
4、服务端发送
- ChangeCipherSpec:通知对方此消息以后会以之前协商的密钥加密发送数据
- Finished:服务器使用对称密钥加密(生成方式与客户端相同)之前所发送的所有握手消息的hash值,发送给客户端去校验
三、TLS1.2 RSA流程
- 客户端向服务器发送随机数client random,TLS版本,支持的加密套件列表
- 服务器响应随机数server random,确认好双方都支持的加密套件。同时下发服务器证书
- 客户端证书验证通过后,生成另一个随机数premaster secret,通过服务器证书公钥加密,传给服务器
- 服务器用私钥解密,获取到premaster secret
至此,客户端和服务器都有3个相同的凭证:client_random,server_random,premaster_secret,两者使用约定好的算法,通过这3个随机数生成最终的密钥,之后数据传输就通过该密钥加密
四、TLS1.2 ECDHE流程
- 客户端向服务器发送随机数client random,TLS版本,支持的加密套件列表
- 服务器响应随机数server random,确认好双方都支持的加密套件,同时下发服务器证书。同时会生成随机数作为私钥,保留在本地。公开DH算法的参数,根据DH算法参数和临时生成的私钥,算出公钥,下发给客户端。为了保证不被篡改,同时会生成签名下发给客户端
- 客户端验证通过后,生成自己私钥,然后根据服务器公开的DH算法参数,算出公钥,发送给服务器
- 服务器得到客户端的临时公钥
至此,对方的公钥,自己的私钥,DH算法的公共参数都已经得到,即可算出共享密钥。最终的加密密钥,通过client_random,server_random,共享密钥,3者一起确定
离散对数
ECDHE 密钥协商算法是 DH 算法演进过来的,所以我们先从 DH 算法说起。
DH 算法是非对称加密算法, 因此它可以用于密钥交换,该算法的核心数学思想是离散对数。
是不是听到这个数学概念就怂了?不怕,这次不会说离散对数推导的过程,只简单提一下它的数学公式。
离散对数是「离散 + 对数」的两个数学概念的组合,所以我们先来复习一遍对数。
要说起对数,必然要说指数,因为它们是互为反函数,指数就是幂运算,对数是指数的逆运算。
举个栗子,如果以 2 作为底数,那么指数和对数运算公式,如下图所示:
那么对于底数为 2 的时候, 32 的对数是 5,64 的对数是 6,计算过程如下:
对数运算的取值是可以连续的,而离散对数的取值是不能连续的,因此也以「离散」得名,
离散对数是在对数运算的基础上加了「模运算」,也就说取余数,对应编程语言的操作符是「%」,也可以用 mod 表示。离散对数的概念如下图:
上图的,底数 a 和模数 p 是离散对数的公共参数,也就说是公开的,b 是真数,i 是对数。知道了对数,就可以用上面的公式计算出真数。但反过来,知道真数却很难推算出对数。
特别是当模数 p 是一个很大的质数,即使知道底数 a 和真数 b ,在现有的计算机的计算水平是几乎无法算出离散对数的,这就是 DH 算法的数学基础。
#DH 算法
认识了离散对数,我们来看看 DH 算法是如何密钥交换的。
现假设小红和小明约定使用 DH 算法来交换密钥,那么基于离散对数,小红和小明需要先确定模数和底数作为算法的参数,这两个参数是公开的,用 P 和 G 来代称。
然后小红和小明各自生成一个随机整数作为私钥,双方的私钥要各自严格保管,不能泄漏,小红的私钥用 a 代称,小明的私钥用 b 代称。
现在小红和小明双方都有了 P 和 G 以及各自的私钥,于是就可以计算出公钥:
- 小红的公钥记作 A,A = G ^ a ( mod P );
- 小明的公钥记作 B,B = G ^ b ( mod P );
A 和 B 也是公开的,因为根据离散对数的原理,从真数(A 和 B)反向计算对数 a 和 b 是非常困难的,至少在现有计算机的计算能力是无法破解的,如果量子计算机出来了,那就有可能被破解,当然如果量子计算机真的出来了,那么密钥协商算法就要做大的升级了。
双方交换各自 DH 公钥后,小红手上共有 5 个数:P、G、a、A、B,小明手上也同样共有 5 个数:P、G、b、B、A。
然后小红执行运算: B ^ a ( mod P ),其结果为 K,因为离散对数的幂运算有交换律,所以小明执行运算: A ^ b ( mod P ),得到的结果也是 K。
这个 K 就是小红和小明之间用的对称加密密钥,可以作为会话密钥使用。
可以看到,整个密钥协商过程中,小红和小明公开了 4 个信息:P、G、A、B,其中 P、G 是算法的参数,A 和 B 是公钥,而 a、b 是双方各自保管的私钥,黑客无法获取这 2 个私钥,因此黑客只能从公开的 P、G、A、B 入手,计算出离散对数(私钥)。
前面也多次强调, 根据离散对数的原理,如果 P 是一个大数,在现有的计算机的计算能力是很难破解出 私钥 a、b 的,破解不出私钥,也就无法计算出会话密钥,因此 DH 密钥交换是安全的。
#DHE 算法
根据私钥生成的方式,DH 算法分为两种实现:
- static DH 算法,这个是已经被废弃了;
- DHE 算法,现在常用的;
static DH 算法里有一方的私钥是静态的,也就说每次密钥协商的时候有一方的私钥都是一样的,一般是服务器方固定,即 a 不变,客户端的私钥则是随机生成的。
于是,DH 交换密钥时就只有客户端的公钥是变化,而服务端公钥是不变的,那么随着时间延长,黑客就会截获海量的密钥协商过程的数据,因为密钥协商的过程有些数据是公开的,黑客就可以依据这些数据暴力破解出服务器的私钥,然后就可以计算出会话密钥了,于是之前截获的加密数据会被破解,所以 static DH 算法不具备前向安全性。
既然固定一方的私钥有被破解的风险,那么干脆就让双方的私钥在每次密钥交换通信时,都是随机生成的、临时的,这个方式也就是 DHE 算法,E 全称是 ephemeral(临时性的)。
所以,即使有个牛逼的黑客破解了某一次通信过程的私钥,其他通信过程的私钥仍然是安全的,因为每个通信过程的私钥都是没有任何关系的,都是独立的,这样就保证了「前向安全」。
#ECDHE 算法
DHE 算法由于计算性能不佳,因为需要做大量的乘法,为了提升 DHE 算法的性能,所以就出现了现在广泛用于密钥交换算法 —— ECDHE 算法。
ECDHE 算法是在 DHE 算法的基础上利用了 ECC 椭圆曲线特性,可以用更少的计算量计算出公钥,以及最终的会话密钥。
小红和小明使用 ECDHE 密钥交换算法的过程:
- 双方事先确定好使用哪种椭圆曲线,和曲线上的基点 G,这两个参数都是公开的;
- 双方各自随机生成一个随机数作为私钥d,并与基点 G相乘得到公钥Q(Q = dG),此时小红的公私钥为 Q1 和 d1,小明的公私钥为 Q2 和 d2;
- 双方交换各自的公钥,最后小红计算点(x1,y1) = d1Q2,小明计算点(x2,y2) = d2Q1,由于椭圆曲线上是可以满足乘法交换和结合律,所以 d1Q2 = d1d2G = d2d1G = d2Q1 ,因此双方的 x 坐标是一样的,所以它是共享密钥,也就是会话密钥。
这个过程中,双方的私钥都是随机、临时生成的,都是不公开的,即使根据公开的信息(椭圆曲线、公钥、基点 G)也是很难计算出椭圆曲线上的离散对数(私钥)。
#ECDHE 握手过程
知道了 ECDHE 算法基本原理后,我们就结合实际的情况来看看。
我用 Wireshark 工具抓了用 ECDHE 密钥协商算法的 TSL 握手过程,可以看到是四次握手:
细心的小伙伴应该发现了,使用了 ECDHE,在 TLS 第四次握手前,客户端就已经发送了加密的 HTTP 数据,而对于 RSA 握手过程,必须要完成 TLS 四次握手,才能传输应用数据。
所以,ECDHE 相比 RSA 握手过程省去了一个消息往返的时间,这个有点「抢跑」的意思,它被称为是「TLS False Start」,跟「TCP Fast Open」有点像,都是在还没连接完全建立前,就发送了应用数据,这样便提高了传输的效率。
接下来,分析每一个 ECDHE 握手过程。
#TLS 第一次握手
客户端首先会发一个「Client Hello」消息,消息里面有客户端使用的 TLS 版本号、支持的密码套件列表,以及生成的随机数(Client Random)。
#TLS 第二次握手
服务端收到客户端的「打招呼」,同样也要回礼,会返回「Server Hello」消息,消息面有服务器确认的 TLS 版本号,也给出了一个随机数(Server Random),然后从客户端的密码套件列表选择了一个合适的密码套件。
不过,这次选择的密码套件就和 RSA 不一样了,我们来分析一下这次的密码套件的意思。
「 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384」
- 密钥协商算法使用 ECDHE;
- 签名算法使用 RSA;
- 握手后的通信使用 AES 对称算法,密钥长度 256 位,分组模式是 GCM;
- 摘要算法使用 SHA384;
接着,服务端为了证明自己的身份,发送「Certificate」消息,会把证书也发给客户端。
这一步就和 RSA 握手过程有很大的区别了,因为服务端选择了 ECDHE 密钥协商算法,所以会在发送完证书后,发送「Server Key Exchange」消息。
这个过程服务器做了三件事:
- 选择了名为 x25519 的椭圆曲线,选好了椭圆曲线相当于椭圆曲线基点 G 也定好了,这些都会公开给客户端;
- 生成随机数作为服务端椭圆曲线的私钥,保留到本地;
- 根据基点 G 和私钥计算出服务端的椭圆曲线公钥,这个会公开给客户端。
为了保证这个椭圆曲线的公钥不被第三方篡改,服务端会用 RSA 签名算法给服务端的椭圆曲线公钥做个签名。
随后,就是「Server Hello Done」消息,服务端跟客户端表明:“这些就是我提供的信息,打招呼完毕”。
至此,TLS 两次握手就已经完成了,目前客户端和服务端通过明文共享了这几个信息:Client Random、Server Random 、使用的椭圆曲线、椭圆曲线基点 G、服务端椭圆曲线的公钥,这几个信息很重要,是后续生成会话密钥的材料。
#TLS 第三次握手
客户端收到了服务端的证书后,自然要校验证书是否合法,如果证书合法,那么服务端到身份就是没问题的。校验证书的过程会走证书链逐级验证,确认证书的真实性,再用证书的公钥验证签名,这样就能确认服务端的身份了,确认无误后,就可以继续往下走。
客户端会生成一个随机数作为客户端椭圆曲线的私钥,然后再根据服务端前面给的信息,生成客户端的椭圆曲线公钥,然后用「Client Key Exchange」消息发给服务端。
至此,双方都有对方的椭圆曲线公钥、自己的椭圆曲线私钥、椭圆曲线基点 G。于是,双方都就计算出点(x,y),其中 x 坐标值双方都是一样的,前面说 ECDHE 算法时候,说 x 是会话密钥,但实际应用中,x 还不是最终的会话密钥。
还记得 TLS 握手阶段,客户端和服务端都会生成了一个随机数传递给对方吗?
最终的会话密钥,就是用「客户端随机数 + 服务端随机数 + x(ECDHE 算法算出的共享密钥) 」三个材料生成的。
之所以这么麻烦,是因为 TLS 设计者不信任客户端或服务器「伪随机数」的可靠性,为了保证真正的完全随机,把三个不可靠的随机数混合起来,那么「随机」的程度就非常高了,足够让黑客计算不出最终的会话密钥,安全性更高。
算好会话密钥后,客户端会发一个「Change Cipher Spec」消息,告诉服务端后续改用对称算法加密通信。
接着,客户端会发「Encrypted Handshake Message」消息,把之前发送的数据做一个摘要,再用对称密钥加密一下,让服务端做个验证,验证下本次生成的对称密钥是否可以正常使用。
#TLS 第四次握手
最后,服务端也会有一个同样的操作,发「Change Cipher Spec」和「Encrypted Handshake Message」消息,如果双方都验证加密和解密没问题,那么握手正式完成。于是,就可以正常收发加密的 HTTP 请求和响应了。
#总结
RSA 和 ECDHE 握手过程的区别:
- RSA 密钥协商算法「不支持」前向保密,ECDHE 密钥协商算法「支持」前向保密;
- 使用了 RSA 密钥协商算法,TLS 完成四次握手后,才能进行应用数据传输,而对于 ECDHE 算法,客户端可以不用等服务端的最后一次 TLS 握手,就可以提前发出加密的 HTTP 数据,节省了一个消息的往返时间(这个是 RFC 文档规定的,具体原因文档没有说明,所以这点我也不太明白);
- 使用 ECDHE, 在 TLS 第 2 次握手中,会出现服务器端发出的「Server Key Exchange」消息,而 RSA 握手过程没有该消息;
五、TLS1.2和TLS1.3区别
5.1 更快的访问速度
使用 TLS 1.2 需要两次往返( 2-RTT )才能完成握手,然后才能发送请求。
TLS 1.3 的握手不再支持静态的 RSA 密钥交换,这意味着必须使用带有前向安全的 Diffie-Hellman 进行全面握手。使用 TLS 1.3 协议只需要一次往返( 1-RTT )就可以完成握手。如下图所示:
客户端发送Client Hello时,附带了DH算法的公共参数和公钥,服务器返回Server Hello时,附带了DH算法生成的服务器公钥,即一次传输就完成了密钥交换
5.2 更强的安全性
TLS 1.3 在之前版本的基础上删除了那些不安全的加密算法,这些加密算法包括:
- RSA 密钥传输 —— 不支持前向安全性
- CBC 模式密码 —— 易受 BEAST 和 Lucky 13 攻击
- RC4 流密码 —— 在 HTTPS 中使用并不安全
- SHA-1 哈希函数 —— 建议以 SHA-2 取而代之
- 任意 Diffie-Hellman 组—— CVE-2016-0701 漏洞
- 输出密码 —— 易受 FREAK 和 LogJam 攻击
六、wareshark抓包验证TLS1.2流程
6.1 搭建https server(springboot)
- 生成自签名证书
使用jdk的keytool生成证书
keytool -genkey -alias wxl -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore wxl-ssl-key.p12 -validity 3650
- -genkey:表示要创建一个新的密钥
- -alias:keystore别名
- -keyalg:加密算法
- -keysize:密钥长度
- -storetype:密钥类型
- -keystore:文件存放位置
- -validity:密钥有效期,单位为天
- springboot配置https
server:
port: 8443
ssl:
enabled: true
key-store: classpath:wxl-ssl-key.p12
key-store-password: 123456
key-store-type: PKCS12
enabled-protocols:
- TLSv1.2
enabled-protocols表示支持启用的TLS版本,这里配置仅TLS1.2
6.2 请求并抓包
请求服务
curl https://localhost:8443/hello -k
抓包
6.3 抓包结果分析
客户端:
- Client Hello:客户端发送支持的最高版本,客户端随机数,加密套件列表
服务端:
2. Server Hello:服务端下发选定版本,服务端随机数,选定的加密套件
3. Certificate:服务器下发证书
4. Server Key Exchange:服务器下发ECDHE算法公开参数和公钥
5. Server Hello Done:服务端消息发送完毕
客户端:
6. Client Key Exchange:客户端上传根据ECDHE算法参数生成的随机公钥
7. Change Cipher Spec:通知对方此消息以后会以之前协商的密钥加密发送数据
8. Encrypted Handshake Message:即Finish消息
服务端:
9. Change Cipher Spec:通知对方此消息以后会以之前协商的密钥加密发送数据
10. Encrypted Handshake Message:即Finish消息
七、参考
Wireshark 抓包理解 HTTPS 协议_wireshark抓ssl包-CSDN博客
HTTPS(三):使用ECDHE加密算法的TLS握手流程-CSDN博客
https://juejin.cn/post/6895624327896432654
https://zhuanlan.zhihu.com/p/44980381