上一篇:零门槛学习https–(3)https的安全策略

https其实就是在TLS之上的http协议,所以各种头信息以及数据格式和http其实都一样,主要区别就在TLS,下面我们来看看TLS是如何工作的。

本章咱们讨论一下TLS的一个整体思路,和一些重要的细节,所有的细节请参看RFC文档

TLS握手

和TCP的握手一样,TLS在工作之前也需要握手,保证客户端和服务端的正确运行,下面的图片里面包含了TLS握手的整个过程。

下面是一次对https://www.baidu.com请求的TLS握手过程,我们结合上面的图一起来看看整个过程在实际连接中是什么样子的。

Client Hello

和TCP握手一样,客户端首先要告知服务端自己的来意,总共会传递以下信息:

1
2
3
4
5
6
7
8
9
10
11
1. 客户端支持的TLS协议版本,这里会有一个list,目前主流的是TLS1.2。
2. 一个随机数,我们记为CR,具体的作用是用来生成对话密钥,我们稍后会用到。
3. 支持的加密方法套件。例如:TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,由四个部分组成:
1. 密钥交换算法。用来交换对称加密密钥的算法。
2. 加密算法。对称加密算法,真正的加密通讯内容的加密算法。
3. 报文认证信息码(MAC)算法。对通讯内容做一个摘要,然后附加到每一段消息尾部,
这样服务端就知道消息是否被更改。这一步要防止的是:虽然加密了,但是如果中间人随便更改一个字节,
也许要表达的内容就不一样了。
4. PRF(伪随机函数)。
4. 支持的压缩方法。
5. 扩展信息。扩展信息会有非常多,类似时间戳,域名等等信息,具体的可以参看RFC文档。

有一个扩展信息可以提一下,早先的TLS协议是没有域名信息的,造成一个问题就是,如果服务端采用一些虚拟化技术虚拟了很多主机,每个主机服务不同的域名,这时候有客户端向服务端发出TLS请求,服务端这时候并不知道应该把哪个证书返回,所以为了解决这个问题,在2006年TLS协议加入了域名扩展信息。

所以说,如果你在公司上网,即使你使用了HTTPS,公司依然能够知道你访问是t66y还是115,或者是taobao。有人也向官方指出这会泄露隐私,但是官方认为这是一个低安全级别问题,并且现在还没有什么好的解法,所以这个问题就搁置了。

1
2
3
4
5
红框中分别是:

1. 客户端随机数:CR。
2. 本次请求的域名。
3. 扩展信息,要求服务端直接返回证书的校验结果,校验方式是:OCSP。

Server Hello

1
2
3
4
1. 确认TLS协议的版本,如果客户端的协议服务端不支持的话,服务端会选择关闭这个连接。
2. 从客户端支持的加密方法中,选择一个加密方式,并告知客户端。
3. 生成一个随机数,我们记为SR,后续我们会结合CR生成对话密钥,稍后会用到。
4. 从客户端支持的压缩算法中,选择一个压缩算法。

1
2
3
4
5
6
7
8
红框中内容分别是:

1. 服务端随机数:SR。
2. Session Id。跟SSO的cookie的概念有点类似,就是当你这次握手成功之后,
下次直接带上这个session Id,如果服务端认为合法,那么就不用再进行握手,
直接可以开始用之前已经商量好的对称加密密钥开始通信了。
3. 服务端选择的加密套件。
4. 服务端设置了`status_request`的应答标志位,答应客户端会直接返回服务端证书的OCSP校验结果。

Server Certificate

这一步主要是返回证书链供客户端验证。

服务端返回了证书链,里面包含三张证书。

[Certificate Status]

这一步也是可选的,当Client Hello的时候,客户端会在extension里面要求服务端直接返回证书的校验结果,以加快访问速度,这是TLS的一个扩展,在RFC6066中规定了这一个扩展。当然即使客户端要求了,服务端在Server Hello的时候应答了,服务端也可以不返回,这样客户端就要自己去校验了。

服务端会返回OCSP验证的结果,这样,服务端可以缓存OCSP的结果,服务端就无需再次访问OCSP获取证书验证结果,提高客户端的访问速度。

说的直白一点,这一步的作用就是:服务端返回证书给客户端,然后再告诉客户端说,这个证书我已经验证过了,你放心用吧。客户端会选择信任中间证书,直接校验根证书,所以,这里即使被中间人攻击直接返回验证成功,但是因为客户端任然会通过操作系统内置的根证书来进行验证,这一步任然是安全的。

这里返回了OCSP的结果:successful。

[Server Key Exchange]

这一步也是可选的,取决于双方的加密方法,这里就不得不提到TLS的两种密钥协商方式:

  1. TLS_RSA_XXXX。这类算法里面,RSA的作用是Key Transmission,也就是说对称加密的密钥是由客户端生成,然后通过证书里面的公钥加密发送给服务端。如果采用的是RSA算法,那么这一步就不需要了。
  2. TLS_DHE_XXXX。这类算法里面,使用DH算法进行密钥协商,DH的作用就是Key Exchange,密钥是由客户端和服务端共同生成的。

DH密钥协商可以总结如下:

1
2
3
4
5
1. 通讯双方(张三、李四)需要先约定好算法参数(algorithm parameters):一个素数 p 作为模数,一个素数 g 作为基数(g 也称为“生成元”)。这两个算法参数是可以对外公开滴。
2. 对于张三而言,需要先想好一个秘密的自然数 a 作为私钥(不能公开),然后计算 A = ga mod p 作为自己的公钥(可以公开)。
3. 对李四而言也类似,先想好一个秘密的自然数 b 作为私钥(不能公开),然后计算 B = gb mod p 作为自己的公钥(可以公开)。
4. 张三和李四互相交换各自的公钥。
5. 然后张三计算出 k = Ba mod p,李四计算出 k = Ab mod p

1
2
3
4
上图中,服务器端把p、g还有服务端算出的DH公钥,组合成Pubkey,
用SHA512求得hash之后,用服务端私钥做RSA签名防伪。

客户端收到后,会用证书里面的公钥解密,验证签名正确性,然后拿出服务端DH公钥

[Client Certificate Request]

这一步也是可选的,要求客户端出示自己的证书,默认服务端不会向客户端索取证书。主要用于银行等金融领域,只有持有相应的证书(各类U盾)才允许客户端访问自己的网络,这里的客户端证书和CA证书类似,不过签发的一方通常是银行自己。

Server Done

这一步就是个标志位,告诉客户端,整个Server Hello阶段已经结束,轮到你回消息了。

Client Certificate

如果Server Hello的时候,服务端要求验证客户端证书,客户端会在这里给服务端发送自己的证书。

这是客户端在Server Hello Done之后发送的第一条数据。

Client Key Exchange

这条消息必须在Client Certificate(如果有的话)之后立即发送。这里的Key Exchange交换的就是pre-master key,有了pre-master key和我们之前生成两个随机数CRSR就能够计算出我们的对称加密的密钥了,这里总共分为两种情况:

  1. 如果采用的是RSA算法,这里就是一个客户端产生的48 byte的随机数,用服务端的公钥加密之后发送。这里需要用公钥加密的原因在于,前两个随机数都是明文传输的,而采用RSA方式传输密钥,如果三个随机数都是明文,那么就可以计算出对称加密的密文了,所以这里一定是要加密传输。
  2. 如果采用的是DH算法及各种变种算法(如:ECDHE),这里发送的就是客户端公钥。这个消息不用公钥加密,原因在于DH算法本身的作用就是在不安全的通信通道交换一个安全的加密密钥,真是的一个神奇的算法。

这里把DH算法的客户端公钥发送给服务端。

至于为什么不能全部都由客户端或者都由服务端生成呢?这里引用别的地方看到的一段话,解释得非常清楚:

1
2
3
4
5
6
7
8
9
10
11
不管是客户端还是服务器,都需要随机数,这样生成的密钥才不会每次都一样。
由于SSL协议中证书是静态的,因此十分有必要引入一种随机因素来保证协商出来的密钥的随机性。
对于RSA密钥交换算法来说,pre-master-key本身就是一个随机数,再加上hello消息中的随机,
三个随机数通过一个密钥导出器最终导出一个对称密钥。

pre master的存在在于SSL协议不信任每个主机都能产生完全随机的随机数,
如果随机数不随机,那么pre master secret就有可能被猜出来,
那么仅适用pre master secret作为密钥就不合适了,因此必须引入新的随机因素,
那么客户端和服务器加上pre master secret三个随机数一同生成的密钥就不容易被猜出了,
一个伪随机可能完全不随机,可是是三个伪随机就十分接近随机了,
每增加一个自由度,随机性增加的可不是一。

总得来说即使客户端或者服务器端被人做了手脚,采用的随机数生成函数变成了可控的随机,那么只要另外一方不是被同一人做手脚,整体通信任然是安全的。

Change Cipher Spec(客户端)

这一步就是个标志位,告诉服务端:客户端之后通信就开始使用之前约定好的加密方式来加密传输了。

Encrypted Handshake Message(客户端)

这一步的作用是把之前握手的所有消息,用加密套件里的hash算法计算出一个值,然后用刚刚协商好的对称加密的密钥进行加密,发送给服务端,即能验证之前发送的消息有没有被篡改,又能验证下服务端计算的密钥对不对,如果计算不对就解不出明文。

因为内容已经加密了,就看不到具体的东西了

Change Cipher Spec(服务端)

跟客户端一样,一个标示位,告诉客户端:服务端之后通信就开始使用之前约定好的加密方式来加密传输了。

Encrypted Handshake Message(服务端)

同样的,把之前发送的所有东西都计算hash,然后用自己计算出来的对称加密密钥加密,发送给客户端解密。至此,服务端在握手阶段就结束了。客户端在收到消息,并且解密验证通过了之后,客户端也就结束了。

TLS 数据传输

总得来说跟HTTP差不多,只是在每个包后面添加了一个由报文认证信息码(MAC)算法计算出来的hash,保证信息传输的健全性。

数据传输协议的是http

TLS session id

这个其实和SSO 登陆的cookie类似,有了cookie,第二次访问的时候服务端会去校验cookie是否有效,如果有效那就不用重新登陆了,如果cookie无效,那么就要重新登陆了。

session id也是如此,整个TLS握手过程还是比较复杂的,如果每个连接都这么握手一次,那体验也真是太差了,所以TLS设计了session id的概念,如果session id没有过期,那么就可以跳过握手阶段,直接开始加密传输。

所以有时候我们会发现,第一次访问某个HTTPS网站的时候很慢,但是第一次打开之后,第二次刷新,速度就很快了。

好了,到这里我们的TLS协议就介绍得差不多了,其实TLS协议还有非常多的细节,大家可以仔细参看RFC文档。TLS协议看起来似乎很完美,无法破解,但是现实中还是有很多基于TLS的攻击方式,下一篇就给大家来分享一下:

零门槛学习https–(5)常见的https攻击原理