由于最近在思考搭建一个属于自己的网站,为了安全性,决定仔细研究了一下HTTPS。看了很多资料,对其中的很多细节做了研究,再加上亲手抓包来做参考,终于算是对整个握手过程和保证有了认识,所以尝试用文字性的语句描述下来,以最通俗易懂且详尽的方式来让更多读者理解。所以即使你是完全不懂https的小白,也能通过我的文章对其有一定的学习收获。
握手
在全篇文章开始,我们先理解一下什么是握手。我们思考一个场景,两个陌生人见面了,如何进行交谈呢?握手是很好的一个方法来使双方达成共识。一人伸出手来,表示我想和你交谈,另一个人握住他的手,表示我愿意与你交谈,双方从而达成共识,可以开始愉快地交谈了。
我们将场景映射到计算机环境中,那个伸出手的人是客户端,而握住手的人就是服务器。双方进行握手,完成连接,随后开始传输数据。
但实际的场景可能会复杂很多,如果这两个陌生人要交谈的内容是机密性的,不希望被别人听到,那这个握手过程可能不仅仅只是握手,他们可能会商量好在哪交谈,用什么方式交谈等等,确保没有问题后才会握手达成共识。对于HTTPS来说,就是这个场景,为了安全性,其握手过程也会比普通的HTTP握手过程复杂。
整个HTTPS握手流程
整个HTTPS的握手大概可以分为4个步骤:
- 客户端发起请求,向服务器端传递一个随机数p,以及客户端支持的加密算法
- 服务器接到请求,回复客户端使用哪套加密算法,并向客户端回传一个随机数q以及服务器证书(包含公钥)
- 客户端拿到服务器回传的信息,开始验证服务器证书,确定无误后,生成第三个随机数k,并使用公钥加密这个随机数并传递给服务器。
- 服务器拿到公钥加密后的随机数k,使用私钥解密,并使用已经拥有的3个随机数(p,q,k)生成最终的对称加密密钥,并告诉客户端其已经得到最终的对称加密密钥,可以开始通信了。
整个过程差不多就是这样,如果你对“对称加密”和“非对称加密”没有了解的话,可能理解其整个握手过程会很头疼,这里我只做简单的讲解:
- 对称加密:加密和解密都用同一个密钥。
- 非对称加密:拥有密钥对(公钥和私钥),一种密钥加密的密文必须用另一个密钥才能解密。
更详细内容可能需要小白们自己搜资料学习了,我们所需要知道的,HTTPS同时使用了这两种加密方式来完成通信,是因为直接使用非对称加密来加密信息的计算代价太大了,而对称加密却很快,所以HTTPS使用非对称加密的方法来传递对称加密的密钥(实际上只是生成密钥的信息),再使用对称加密来实现信息加密。
在流量角度来看整个握手过程
为了展示整个握手过程的流量交互,我使用wireshark抓取了数据,但所捕捉的数据不够典型,所以我借用RAZEEN博客中的截图来进行讲解说明。
下面我会解释每一个数据包与其对应的过程。
Client Hello
客户端开始向服务器发起访问请求,在这个过程中,客户端会生成一个随机数p,然后把随机数和一些信息一起打包发送给服务器。这里的信息包括:我支持的协议版本,可以使用的加密算法等等,您看看我们用哪个好?
我们可以从流量的第一个Client Hello数据包看到这些携带的信息:
Server Hello
服务器端接收到了客户端的请求,回复已经收到请求,并告诉客户端我们使用什么版本的协议,以及使用什么样的加密算法来通信。在回复的内容中,还会包含一个服务器端产生的随机数q,用以后面生成密钥。
我们可以从第二个数据包Server Hello看到这些信息:
我们可以看到这个Cipher Suite的格式,其包含了一套加密算法,包括非对称的加密算法以及对称加密算法等等,可以参考一下下面的图:
之后,服务器端还会把自己的电子证书(Certificate)传递给客户端,用以让客户端验证自己的身份,证书中还包含了服务器的公钥,用以等会传递第三个随机数。完成这一步后,服务器会回复一个 “Server Hello Done” 来表明已经将所有预计的握手消息发送完毕。
Client Key Exchange
客户端接收到了服务器端的证书,验证证书的有效性和签名,确定无误后,开始准备传递第三个随机数。
这是很关键的一步,上面传递的2个随机数都是明文传递,是可以被攻击者获取的,接下来要传递的第三个随机数是绝密的。客户端使用服务器证书中的公钥加密这个随机数,并将其传递给服务器,由只有服务器拥有私钥,所以只有服务器可以拿到这个随机数。
这完成随机数传输后,客户端发送一个 “Change Cipher Spec” 来告诉服务器我已经生成加密密钥了,而且生成加密密钥的第三个随机数也已经发送给你了,接下来我们将切换到加密通信模式。
Finished
服务器接收到经过公钥加密的第三个随机数,然后用私钥解密他得到随机数k,再使用随机数p,q,k来生成最终的加密密钥,因为随机数相同,所以客户端和服务器端现在手里的密钥是相同的,所以可以进行通信。这个时候,服务器会回复一个 “Change Cipher Spec”,目的和客户端发送的一样,表示接下来的通信使用加密的方式。可以看到,在上一个数据包中,客户端会给服务器发送一个 “Encrypted Handshake Message” ,而服务器也会在最后回复一个Encrypted Handshake Message,这是由客户端服务器之间协商的算法和密钥保护的第一个消息,它意味着握手已经完成。
密钥
通过整个流程我们可以分析出,服务器和客户端的加密通信基础在于双方拥有相同的对称加密密钥。但是直接传递对称加密密钥是不可行的,一旦被攻击者截获,那通信将无秘密可言。所以怎么安全地传递对称密钥是一个问题,HTTPS采取的方法是使用非对称加密体制来传递密钥信息,事实上Client Hello和Server Hello都是在为非对称加密做准备。
HTPPS的设计是客户端和服务器端各生成一个随机数,并在前两次通信中进行传递,这两个随机数是可以被截获的,但无伤大雅。在前两次通信中,客户端和服务器端出来传递随机数,也商量好了使用哪种非对称算法来进行第三个随机数传输,并且已经完成了公钥的传递。第三个随机数由客户端生成,并将随机数使用公钥加密后传递给服务器。这个过程可能被截获,但是由于没有私钥,攻击者依旧无法得到第三个随机数,只有服务器可以得到。至此,客户端和服务器端都拥有了这三个随机数,所以能够生成相同的对称密钥。
我们思考个问题,为什么是3个随机数?我们知道现在的随机数都算是伪随机数,无法保证真正的随机性,但是3个伪随机数生成的新数在一定意义上能保证随机性,达到随机的安全标准。随机数的意义在于抵抗重放攻击,当然客户端和服务器各自生成随机数也能防止一方产生的随机数可能出现的问题。
DH握手
上面我们一直在以RSA握手形式来描述整个握手流程,因为RSA握手是最经典而常规的HTTPS实现方式,方便小白理解。实际上,HTTPS一共有两种方式来握手。在实际的抓包观察中,我发现大部分的网站HTTPS都是采用DH握手的。
说起来DH算法真的是一个很神奇的算法,原理上它是非对称加密的基础,这里不对算法进行分析描述,有兴趣的可以参考:DH算法原理。本博文只讲述一下DH握手与RSA握手有什么不同。
整个HTTPS的握手大概可以分为4个步骤:
- 客户端发起请求,向服务器端传递一个随机数p,以及客户端支持的加密算法
- 服务器接到请求,回复客户端使用哪套加密算法,并向客户端回传一个随机数q以及服务器证书(包含公钥)
- 服务器先发起DH参数交换请求,使用自己的私钥签名DH参数后发送给客户端。客户端验证服务器证书,确定无误后将自己的DH参数发送给服务器。
- 服务器拿到客户端的DH参数后计算出第三个随机数k,并使用已经拥有的3个随机数(p,q,k)生成最终的对称加密密钥,并告诉客户端其已经得到最终的对称加密密钥,可以开始通信了。
我们可以看到,其实RSA握手和DH握手的差异只在于第三个随机数的传递方式,RSA握手使用非对称加密的公钥加密方式来传递第三个随机数,而DH握手使用服务器端和客户端相互交换DH参数的方式来传递第三个随机数。
在流程上,在 Server Hello 后,服务器会将DH参数用私钥加密传递给客户端。客户端用公钥解密DH参数,从而验证这条信息来自于服务器,而后将自己的DH参数传递给服务器。DH算法的巧妙在于,客户端和服务器端都有一个属于自己的信息,他们将自己的信息和另一个信息结合后公布出来,而他们再将对方公布的信息和自己的信息结合,就能生成一样的密钥。所以,从始到终,客户端和服务器端都不知道对方的私密信息是什么,但这不重要,只要他们拿到对方的公开信息和自己的私密信息,就能生成一样的密钥。因此攻击者只知道公开信息,完全没有任何作用,根本无从下手。
因此DH握手和RSA握手的差别只在于如何交换对称密钥信息,但他们的最终目的还是生成一个双方共享的对称密钥。
我们看看在流程数据包中的具体DH握手:
可以和RSA握手的图进行比较,多了一个Server Key Exchange,也就是服务器端发出的信息多了一次(因为DH参数是客户端和服务器端相互交换,比起客户端直接加密随机数返回自然多了一步。
Client Hello
这一步的过程和RSA握手基本上没有差别,依旧是客户端开始发起请求,可以看一看流量数据包截图:
Server Hello
第二部初始的Server Hello和RSA握手也没有什么差别:
这时服务器会和RSA握手一样将自己的证书发送给客户端。不同的是,此时RSA握手已经发送Server Hello Done表示Server Hello结束,而DH握手还会将自己经过计算的DH参数传递给客户端(Server Key Exchange),最后发送一个Server Hello Done 来表示发送的信息已经完成。
Client Key Exchange
收到了服务器端发送的证书和DH参数后,客户端会先验证服务器证书,确定无误后,使用服务器的DH参数来计算自己的DH参数,然后将其发送给服务器。剩下来的过程就一样了,发送Change Cipher Spec来表示自己已经生成加密对称密钥了,接下来的通信将切换到加密方式,发送发送第一个加密信息。
Finish
这一步也是一样的,服务器回复Change Cipher Spec和Encrypted Handshake Message,随后加密通信数据传输开始。
证书验证
HTTPS证书的验证是我的一个兴趣点,所以我深入进行了研究与观察。我们首先需要知道,证书是用来验证服务器身份的。我们联系到我们实际的生活场景,假如我们完成了某次国家编程等级考试,那么我们会得到一张证书。
在某次工作应聘的过程中,对方要求我们提供编程等级证书,于是我们提供这张证书。但是对方招人是很严格的,他们会对这张证书进行审查。
首先,他们需要确保获得这张编程等级证书的人的确是我。所以这个证书上获得人的名字必须是我的名字(可以映射到电子证书上的域名)。确定了这张证书的获得人的确是我,接下来他们需要验证这张证书是否有效,所以他们会去查这个证书颁发机构是否权威。这个过程是通过验证签名,如果是阿猫阿狗颁发的(非权威机构签名和自签名证书),那他们会去问老板(实际用户)这个要不要收。如果是权威机构颁发的,那他们就确定了这张证书有效,收了。
在实际的客户端和服务器通信中,服务器将证书传递给客户端,证书中包含了公钥。客户端拿到证书,先会去验证证书是否在时效内,域名信息什么的是否正确等等。然后去看签名,看看是哪个机构做的签名,验证签名的方式就是拿这个机构的公钥去解密签名,如果能解那就的确是这个机构签的,所以这里也有一个非对称加密的应用。当然,很可能给证书签名的机构并不是CA机构,我们去得到他公钥的方式也是获取他的证书,那么我又要去验证他证书上的签名。所以这就会变成一个递归问题,我们需要不断验证上一个证书的签名,直到最终那个我们信任的证书就在我们本地,这个证书我们称为根证书,比如CA权威证书。而这一连串需要验证的证书,我们又称之为证书链。直到客户端确定了这个证书有效,那么就会进行下一步。
所以我们可以随之想到,当我们使用自签名证书时,客户端能验证这个签名是我们签的,但我们并不是权威机构,所以会生成一个警告并提醒用户。
结束
关于https的握手基本上到这里就差不多了,最后分享一个给自己网站配置https的官方应用:Let’s Encrypt,这个网址应用可以免费申请电子证书以及自动配置https,其建立就是为了推广https,所以大家可以去试试,自己搭个网站研究研究,可以更好地理解https。