摘要
- 基于RSA密钥协商算法的TLS1.2握手分析
- 数字证书
- DHE和ECDHE算法
- 基于ECDHE密钥协商算法的TLS1.2握手分析
TLS握手时采用的加密方式
非对称加密和对称加密。
非对称加密主要用来保护对称加密密钥交换的安全性,一旦客户端和服务端交换密钥完成,即可使用密钥采用对称加密的方式进行通信。
基于RSA密钥协商算法的TLS1.2握手分析
TLS握手的总过程
TLS1.2握手的过程由于本人本地密码套件不支持,相关图片来源于网络。
- 第一次握手客户端的发起的ClientHello请求
- 第二次握手ServerHello,发送相关信息和证书给客户端
- 第三次握手客户端发送请求给服务端
- 第四次握手服务端响应客户端的第三次握手
客户端的ClientHello
- Version:客户端使用的TLS的版本号,这里是1.2
- Random:随机数,这个随机数会被服务端保留,后续用来生成对称密钥
- CipherSuites:支持的加密方式列表
服务端的ServerHello
- version:确认支持TLS的版本,这里为1.2
- Random:服务器端的随机数,此随机数后续用来生成对称密钥
- Cipher Suite:确认使用的加密算法
Cipher Suite的格式
Cipher Suite虽然很长,但是它是一个固定格式,基本的格式为:
密钥交换算法 签名算法 WITH 对称加密算法 摘要算法。
比如上图中的我们的使用的算法分别为:
- 密钥交换算法:RSA
- 证书的签名验证算法:RSA(这里由于WITH前面只有一个RSA,所以签名算法也是RSA)
- 握手后的通信对称加密算法:AES对称算法,密钥长度为128位,分组模式是GCM
- 摘要算法:SHA256
服务端如何证明自己的身份
服务端为了证明自己的身份会发送Server Certificate给客户端,这个消息里面包含数字证书。具体信息如下:
除了发送了证书,服务端还发送了Server Hello Done消息,目的是告诉客户端你要的东西我都给你了。具体内容如下:
TLS第三次握手
客户端在验证证书无误后,会发起第三次握手请求,首先会发送Client Key Exchange消息,内容如下:
客户段会生成pre-master随机数,并使用服务端RSA公钥进行加密发送给服务器,Encrypted PreMaster就是经过加密后的随机数,服务器收到后可以使用私钥解密。
至此,客户段使用三个随机数可以生成会话密钥了,于是生成会话密钥以后会发送一个Change Cipher Spec消息告诉服务端开始使用加密的方式传输。
最后客户端会发送一个Encrypted HandShake Message,这个就是把之前的数据做个摘要,然后再用会话密钥加密发送给服务器,供服务器验证。
TLS第四次握手
第4次握手其实就是服务器发送个Change Cipher Spec消息(由于服务器收到了第三个随机数,因此也可以生成会话密钥,后续可以加密传输了)和个Encrypted HandShake Message消息,握手完成。
可以看出,Change Cipher Spec之前的消息都是明文传输,之后都是通过对称密钥加密传输。
数字证书
数字证书包含的内容
- 公钥
- 持有者信息
- CA(证书认证机构)信息
- CA对这份文件的数字签名及使用的算法
- 证书有效期
- 其他信息
数字证书如何签发
- CA会对持有者的公钥、用途、有效时间等信息打成一个包,然后对着信息进行计算,获得一个hash值
- CA会使用自己的私钥将该hash值加密,生成签名
- 最后将签名添加在文件证书上,形成数字证书
客户端如何校验服务端的数字证书
- 客户端使用相同的算法计算服务器数字证书的hash值(假设这里的代号为H1)
- 由于浏览器和操作系统集成了CA的公钥信息,浏览器在收到证书后通过CA进行解密签名内容获得一个hash值(H2)
- 比较H1和H2的值,相同证书可信,否则不可信
证书链是啥
我们向CA申请的证书一般都不是由根证书签发,而是由中间证书签发,比如上述思否(segmentfault.com)的证书,证书有三级。
那么具体的验证过程如下:
- 客户端在收到思否(segmentfault.com)的证书以后,发现证书的签发者不是根证书,因此无法根据本地已有根证书中的公钥去验证segmentfault.com是否可信。因此客户端需要找到该证书的签发者DigiCert TLS RSA SHA256 2020 CA1,然后向该CA请求中间证书
- 请求到DigiCert TLS RSA SHA256 2020 CA1证书以后,发现它还不是根证书,但是它的上级签发机构DigiCert Global Root CA已经没有上级,说明DigiCert Global Root CA是根证书。此时应用软件会检查DigiCert Global Root CA证书是否已经预载到系统中,如果有可以根据证书中的公钥验证DigiCert TLS RSA SHA256 2020 CA1证书,验证通过则认为中间证书可信。
- DigiCert TLS RSA SHA256 2020 CA1证书被信任以后,可以使用它包含的公钥去验证思否(segmentfault.com)的证书,验证通过则信任该证书。
从上图中可以看出思否证书的签发根证书DigiCert Global Root CA已经在我的MacOS操作系统中预置。
DHE和ECDHE算法
RSA密钥协商算法的缺陷
由于我们的客户端传递随机数都是使用公钥加密,服务端收到后使用私钥解密。所以一旦我们服务器的私钥遭到泄漏,我们就可以对过往截获服务器客户端的TLS报文进行解密(无法向前保密)。
今日截获数据,明日(或未来)解密数据。
RSA有缺陷如何解决
RSA密钥协商算法既然无法向前保密,后面使用了新的协商算法:
- DH
- DHE
- ECDHE
DH算法是什么?
DH算法是非对称加密算法,因此也可用作密钥交换,核心思想就是离散对数。
代码语言:javascript复制# 对数运算
x = log2y
上述公式其中x为对数,y为真数,底为2。对于上述例子,32的对数是5,64的对数是6,可以看出对数的取值是能够连续的。
但是离散对数的取值是无法连续,离散也是因此得名。
代码语言:javascript复制a ^ i (mod p) = b
对于一个整数b和一个质数p的一个原根a,能够找到唯一一个指数i使得上述公式成立,那么指数i称为b的以a为底数的模p的离散对数。
底数a和和模p是离散对数的公开参数,b是真数,i是对数,知道了对数i我们很轻松就能算出真数b,但是如果知道真数b但是很难计算对数i,尤其是模p很大的时候,现有的计算机的计算能力是无法破解的。
DH算法如何交换密钥
- 客户端(称为小c)和服务端(称为小s)需要交换密钥,首先需要确定公开参数底数g和模p。
- 客户端小c和服务端小s分别生成一个随机数作为私钥(分别称为小a和小b),在根据底数a和模p即可计算出各自的公钥(称为大A和大B):
小c的公钥为A:A = g ^ a (mod p) 小s的公钥为B: B = g ^ b (mod p)
可以看出我们通过私钥生成公钥很容易,但是通过公钥逆向计算私钥绝对是地狱级别的困难,如果您能算出来说不定你就是未来量子(手动狗头)。
双方交换公钥以后,客户端小c有:
- A:客户端公钥
- a:客户端私钥
- B:服务端公钥
- g
- p
服务器端小s有:
- A:客户端公钥
- b:服务端私钥
- B:服务端公钥
- g
- p
客户端小c会做以下计算:
K = B ^ a (mod p)
服务端小s会做以下计算:
K = A ^ b (mod p)
由于离散对数具有幂的交换律,因此客户端和服务端计算出来的K值就是相等的,这个K就是对称加密密钥。
DH算法的种类
目前有两种:
- static DH
- DHE
static DH就是有一方私钥固定不变(通常为服务器端),虽然很难破解但是随着时间的推移还是有一定几率被破解的,不具备向前保密。
DHE就是让每次双方在进行密钥协商时,都随机生成私钥,这样每次通信过程都是独立的,即使你破解了此次的私钥,也无法破解过往的通信。
ECDHE算法是啥?
在DHE算法的基础上利用了ECC椭圆曲线的特性,用更少的计算量计算公钥。
为什么需要ECDHE算法
DHE算法由于需要大量的乘法运算,计算性能不高,所以出现了ECDHE算法来替代他。
ECDHE算法密钥交换过程
- 客户端和服务端确定好使用哪种椭圆曲线以及曲线上的基点G,这个对外公开
- 客户端和服务端各自生成一个私钥(假设分别为c和s),将私钥与基点G相乘可以分别得到其公钥(假设分别为CP和SP)
- 双方交换公钥,最后服务器计算(x1,y1)= CP * s,客户端计算(x2,y2)= c * SP,由于椭圆曲线的满足交换律和结合律,所以(x1, y1)= CP * s = s * c * G = c * s * G = c * SP = (x2, y2),因此(x1,y1)和(x2, y2)相等,这个坐标即可当做对称密钥。
基于ECDHE密钥协商算法的TLS1.2握手分析
客户端发起第一次握手请求这个采用RSA协商算法的握手无区别,这里就不说了。
TLS第二次握手
当服务端进行响应,进行第二次握手就可以有区别了,主要有两步如下图:
首先回应ServerHello消息,如下:
这里使用TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256密码套件,说明我们的密钥协商算法要使用ECDHE。
然后第二步中先是发送证书给客户端以示清白,紧接着这里就和之前RSA密钥协商的TLS握手有区别了,这里会发送Server Key Exchange的消息,我们看一下消息内容:
- Curve Type:选取了名为named_curve的椭圆曲线,选好椭圆曲线相当于基点G也好了
- Pubkey:根据基点G和服务器生成的私钥计算出的公钥
- Signature:为了保证这个椭圆曲线的公钥不被第三方篡改,服务端会用RSA签名算法给服务端的椭圆曲线公钥做个签名。
最后服务端在发送一个Server Hello Done消息,告诉客户端这是我提供的信息。
目前客户端和服务端已经共享了两个随机数、使用的椭圆曲线,椭圆曲线基点G,服务器的椭圆公钥
TLS第三次握手
这一步客户端会发送Client Key Exchange消息,消息如下:
- Pubkey:客户端根据服务端共享的曲线、基点加上自己的私钥计算出的公钥,该公钥会共享给服务器使用
到这里服务器和客户端又多共享了客户端公钥,按道理已经可以计算出对称密钥了,但最终的会话对称密钥为了更加安全是使用「客户端随机数 服务器随机数 椭圆曲线计算出来的共享密钥」来生成最终的会话密钥。
算好会话密钥以后,紧接着会发送一个Change Cipher Spec消息告诉服务段后续的数据我会通过会话对称密钥加密传输。
最后会发送Encrypted Handshake Message,这部分消息就是对之前发送的数据做个摘要,然后使用对称密钥加密以后传输给服务器。
第三次握手以后,不需要等待服务器的第四次握手既可以进行传输应用数据,如下图:
TLS第四次握手
服务器在收到客户端的请求后,也会发送Change Cipher Spec消息和Encrypted Handshake Message。
至此握手完成,以后数据就可以正常加密传输了。
ECDHE和RSA密钥协商算法的区别
- RSA不支持向前保密,ECDHE支持向前保密
- ECDHE在三次握手结束以后就可以传输应用数据,节省了一个消息的往返时间,RSA必须等待四次握手结束后才可以传输应用数据
- ECDHE在第二次握手会发送Server Key Exchange的消息,RSA没有该消息。
目前TLS已经发展到1.3,1.3对比1.2也有了很多优化,有兴趣的读者可以去了解一下。