TLS握手的过程,其实就是秘钥交换的过程
TLS握手的过程,其实就是秘钥交换的过程,这也是整个TLS加密技术里最复杂的一个环节,因为客户端与服务器端协商秘钥的过程对第三方都是透明的,那么秘钥协商的过程不仅要保证秘钥的保密性,还要考虑前向安全性。
A. RSA算法交换秘钥
RSA交换秘钥流程:
1:服务器给客户端发送数字证书(服务器公钥、域名、CA签名)。
2:使用CA公钥对证书进行验签,验签成功即可证明服务器身份(公钥合法,没被篡改)。
3:生成对称加密的秘钥,使用公钥对其加密,发送给服务器。
4:服务器使用私钥解密,得到对称加密的秘钥。
5:客户端与服务器使用对称加密传输数据。
优点:通俗易懂,实现方便。
缺点:不具备前向安全性,因为使用RSA非对称加密参与了秘钥的协商过程,一旦将来私钥泄漏,那么之前所有的传输数据,都可以被解密。目前这种方式已经被淘汰了。
B. Diffie-Hellman算法交换秘钥
优点:RSA不参与协商秘钥,协商过程使用的参数N\G\X\Y都是一次性的,在客户端可服务器的内存中,即使销毁,整个加密过程具有前向安全性。
缺点:计算需要很大的系统开销,效率低下。目前这种方式也已经不被使用了。
A. ECDHE椭圆曲线算法交换秘钥
ECDHE 算法是在 DHE 算法的基础上利用了 ECC 椭圆曲线特性,可以使用较少的开销计算出会话密钥。定理的推导过程:https://zhuanlan.zhihu.com/p/66794410 可以参考此文章。
我用 Wireshark 工具抓了用 ECDHE 密钥协商算法的 TSL 握手过程,可以看到是四次握手:
a. TLS 第一次握手
客户端首先会发一个「Client Hello」消息,消息里面有客户端使用的 TLS 版本号、支持的密码套件列表,以及生成的随机数(Client Random)。
b. TLS 第二次握手
服务端收到客户端的「Client Hello」,会返回「Server Hello」消息,消息面有服务器确认的 TLS 版本号,也给出了一个随机数(Server Random),然后从客户端的密码套件列表选择了一个合适的密码套件。
密码套件:Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
密钥协商算法使用 ECDHE
签名算法使用 RSA
握手后的通信使用 AES 对称算法,密钥长度 128位,分组模式是CBC
摘要算法使用 SHA
接着,服务端为了证明自己的身份,发送「Certificate」消息,会把证书也发给客户端。
继续发送「Server Key Exchange」消息
这个过程服务器做了三件事:
选择了名为 named_curve 的椭圆曲线,选好了椭圆曲线相当于椭圆曲线基点 G 也定好了,这些都会公开给客户端。
生成随机数作为服务端椭圆曲线的私钥,保留到本地。
根据基点 G 和私钥计算出服务端的椭圆曲线公钥,这个会公开给客户端。为了保证这个椭圆曲线的公钥不被第三方篡改,服务端会用 RSA 签名算法给服务端的椭圆曲线公钥做个签名。
继续发送[Server Hello Done」消息,服务端跟客户端表明:“这些就是我提供的信息,打招呼完毕”。
至此,TLS 两次握手就已经完成了,目前客户端和服务端通过明文共享了这几个信息:Client Random、Server Random 、使用的椭圆曲线、椭圆曲线基点 G、服务端椭圆曲线的公钥,这几个信息很重要,是后续生成会话密钥的材料。
c. TLS 第三次握手
客户端收到了服务端的证书后,自然要校验证书是否合法,如果证书合法,那么服务端到身份就是没问题的。校验证书到过程,会使用CA的公钥验证证书的签名,没问题了就可以继续往下进行。
如果证书验证不合法:
如果是Chrome浏览器,会给出警告提示,告诉你证书来源不合法,例如当年的12306,当然你也可以忽略警告,继续使用该证书也是可以的。
如果是SSL客户端程序,例如JAVA,会抛出证书验证不合法的错误。
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
解决办法:
1:去官方的CA认证中心对证书进行签名。
2:将我们自己创建的根证书提前导入浏览器。
3:配置客户端不检查证书来源。
客户端会生成一个随机数作为客户端椭圆曲线的私钥,然后再根据服务端前面给的信息,生成客户端的椭圆曲线公钥,然后用「Client Key Exchange」消息发给服务端。
至此,双方都有对方的椭圆曲线公钥、自己的椭圆曲线私钥、椭圆曲线基点 G。于是,双方都就计算出了共享秘钥。
还记得 TLS 握手阶段,客户端和服务端都会生成了一个随机数传递给对方吗?
最终的对称加密密钥,就是用「客户端随机数 + 服务端随机数 + (ECDHE 算法算出的共享密钥) 」三个材料生成的。
之所以这么麻烦,是因为 TLS 设计者不信任客户端或服务器「伪随机数」的可靠性,为了保证真正的完全随机,把三个不可靠的随机数混合起来,那么「随机」的程度就非常高了,安全性更高。
算好会话密钥后,客户端会发一个「****Change Cipher Spec****」消息,告诉服务端后续改用对称算法加密通信。
接着,客户端会发「Encrypted Handshake Message」消息,把之前发送的数据做一个摘要,再用对称密钥加密一下,让服务端做个验证,验证下本次生成的对称密钥是否可以正常使用。
d. TLS第四次握手
最后,服务端也会有一个同样的操作,发「Change Cipher Spec」和「Encrypted Handshake Message」消息,如果双方都验证加密和解密没问题,那么握手正式完成。于是,就可以正常收发加密的 HTTP 请求和响应了。