11

真正“搞”懂HTTPS协议17之TLS握手 - Zaking

 1 year ago
source link: https://www.cnblogs.com/zaking/p/17120542.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

  经过前两章的学习,我们知道了通信安全的定义以及TLS对其的实现~有了这些知识作为基础,我们现在可以正式的开始研究HTTPS和TLS协议了。嗯……现在才真正开始。

  我记得之前大概聊过,当你在浏览器的地址栏输入一个URL地址会发生什么,大致是浏览器从URI中获取协议名和域名,获取默认端口号,再用DNS解析出IP地址,然后就可以三次握手与网站建立TCP连接了,然后就会立即进行报文的传递。

  但是在HTTPS中,三次握手后,还不能立即发送报文,它还需要再用另外一个握手过程,在TCP上建立安全连接,之后才是收发HTTP报文。

  这个握手过程与TCP类似,是HTTPS和TLS协议里最重要、最核心的部分,搞懂了TLS握手,你就掌握了HTTPS。

一、TLS协议的组成

  在讲TLS握手之前,我们先来了解下TLS协议的组成。

  TLS 包含几个子协议,你也可以理解为它是由几个不同职责的模块组成,比较常用的有记录协议、警报协议、握手协议、变更密码规范协议等。

  记录协议(Record Protocol)规定了 TLS 收发数据的基本单位:记录(record)。它有点像是 TCP 里的 segment,所有的其他子协议都需要通过记录协议发出。但多个记录数据可以在一个 TCP 包里一次性发出,也并不需要像 TCP 那样返回 ACK。

  警报协议(Alert Protocol)的职责是向对方发出警报信息,有点像是 HTTP 协议里的状态码。比如,protocol_version 就是不支持旧版本,bad_certificate 就是证书有问题,收到警报后另一方可以选择继续,也可以立即终止连接。

  握手协议(Handshake Protocol)是 TLS 里最复杂的子协议,要比 TCP 的 SYN/ACK 复杂的多,浏览器和服务器会在握手过程中协商 TLS 版本号、随机数、密码套件等信息,然后交换证书和密钥参数,最终双方协商得到会话密钥,用于后续的混合加密系统。

  最后一个是变更密码规范协议(Change Cipher Spec Protocol),它非常简单,就是一个“通知”,告诉对方,后续的数据都将使用加密保护。那么反过来,在它之前,数据都是明文的。

  其中握手协议是最最重要的,也是本章的重点。我们先来看张简要图:

1184971-20230215135755800-1850454799.png

   我们来分析下上图,我们可以粗略的看到三种颜色,嗯……没错,我们还可以看到两次数据的往返。嗯……也没错。

  首先,图中的每一个圆角框,就是一个record,就是一个记录。多个记录可以组合成一个TCP包发送。所以最多两次往返,4个消息,就可以完成握手,然后底下的紫色部分,就是加密后的信息了。

  其次,在TLS握手完成之前,也就是出现ChangeCipherSpec之前,所有的消息都是明文传输的。欸?明文传输,那不会泄露交换的数据么?这个问题我就不回答了,回忆一下我们前两章讲的东西。

  最后,我们简要的看词说话一下,首先客户端会发起一个消息,携带了TLS的版本号,客户端随机数,还有一个Cipher Suites,Cipher Suites是啥呢?就是密码套件。还记得我们之前说过,客户端会把它支持的密码套件发送给服务器,让服务器来选择一个。然后呢,服务器会把一大堆东西传给客户端,其中包括TLS版本、服务器的随机数、和Cipher Suite、然后还有其它的消息,比如服务器证书Certificate,比如PubKey,就是服务器的公钥,再有就是传一个Server完事的数据。下一波呢,客户端会传一个PubKey给服务器,还有ChangeCipherSpec以及结束标记发送给服务器。服务器也返回结束。接下来就开始加密传输了。

  当然,这是简单的过程,大家先消化一下,接下来我们再看看详细的流程:

1184971-20230215172421867-2131080198.jpg

  嗯……这张图确实有点大。不用怕啦,我们一点一点来分析,其实这张图就是我们本章要讲的重点了,都在这张图里了。

  第一部分,很好理解,我相信大家都很熟悉了,就是TCP的三次握手嘛~不说了。

  TCP握手完成之后,就是第二部分,也就是第一个来回。我就不截图了,大家看上面的就好了。浏览器会首先发一个“Client Hello”消息,也就是跟服务器“打招呼”。里面有客户端的版本号、支持的密码套件,还有一个随机数(Client Random),用于后续生成会话密钥。

Handshake Protocol: Client Hello
    Version: TLS 1.2 (0x0303)
    Random: 1cbf803321fd2623408dfe…
    Cipher Suites (17 suites)
        Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
        Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)

  服务器在收到”Client Hello“后,会返回一个”Server Hello“消息。把版本号对一下,也给出一个随机数(Server Random),然后从客户端的列表里选一个作为本次通信使用的密码套件,比如下面的代码选择了“TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384”。

Handshake Protocol: Server Hello
    Version: TLS 1.2 (0x0303)
    Random: 0e6320f21bae50842e96…
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)

  然后,服务器为了证明自己的身份,就把证书也发给了客户端(Server Certificate)。

  接下来是一个关键的操作,因为服务器选择了 ECDHE 算法,所以它会在证书后发送“Server Key Exchange”消息,里面是椭圆曲线的公钥(Server Params),用来实现密钥交换算法,再加上自己的私钥签名认证。

Handshake Protocol: Server Key Exchange
    EC Diffie-Hellman Server Params
        Curve Type: named_curve (0x03)
        Named Curve: x25519 (0x001d)
        Pubkey: 3b39deaf00217894e...
        Signature Algorithm: rsa_pkcs1_sha512 (0x0601)
        Signature: 37141adac38ea4...

  然后就是”Server Hello Done“消息,服务器的信息就传递完了。

  这样,第一部分消息往返就结束了,消耗了两个TCP包,结果是客户端和服务器通过明文共享了三个信息:Client Random、Server Random、Server Params

  此刻,客户端拿到了服务器给的证书证明服务器它是它,但是客户端怎么知道这个证书是真的呢?嗯,就是我们之前讲的信任链的追溯了,确认证书的真实性后,再用证书公钥验证签名,就确认了服务器的身份。

  然后,我们看第三部分,客户端按照密码套件的要求,也生产一个椭圆曲线的公钥(Client Params),用“Client Key Exchange”消息发给服务器。

Handshake Protocol: Client Key Exchange
    EC Diffie-Hellman Client Params
        Pubkey: 8c674d0e08dc27b5eaa…

  现在客户端和服务器手里都拿到了密钥交换算法的两个参数(Client Params、Server Params),就用 ECDHE 算法一阵算,算出了一个新的东西,叫“Pre-Master”,其实也是一个随机数。计算过程很复杂~~特别复杂,略。

  现在客户端和服务器手里有了三个随机数:Client Random、Server Random 和 Pre-Master。用这三个作为原始材料,就可以生成用于加密会话的主密钥,叫“Master Secret”。而黑客因为拿不到“Pre-Master”,所以也就得不到主密钥。那,为啥要三个随机数来生成Master Secret呢?其实就是为了提高破解的复杂度。

  那~~再多说两句,Master-Secret怎么计算出来的呢?

master_secret = PRF(pre_master_secret, "master secret",
                    ClientHello.random + ServerHello.random)

  这是RFC中的计算公式。

  这里的“PRF”就是伪随机数函数,它基于密码套件里的最后一个参数,比如这次的 SHA384,通过摘要算法来再一次强化“Master Secret”的随机性。主密钥有 48 字节,但它也不是最终用于通信的会话密钥,还会再用 PRF 扩展出更多的密钥,比如客户端发送用的会话密钥(client_write_key)、服务器发送用的会话密钥(server_write_key)等等,避免只用一个密钥带来的安全隐患。

  有了主密钥和派生的会话密钥,握手就快结束了。客户端发一个“Change Cipher Spec”,然后再发一个“Finished”消息,把之前所有发送的数据做个摘要,再加密一下,让服务器做个验证。

  服务器也是同样的操作,发“Change Cipher Spec”和“Finished”消息,双方都验证加密解密 OK,握手正式结束,后面就收发被加密的 HTTP 请求和响应了。

  最后,我们来看最后一部分,怎么有点奇怪呢?客户端怎么在服务器返回握手结束的消息之前就发送HTTP加密数据了呢?

首先,我们上图中的握手过程,其实是TLS主流握手过程,这与传统的握手过程有两点不同。

  第一,是使用ECDHE实现密钥交换,而不是RSA,所以会在服务器端发送”Server Key Exchange“消息。

  第二,因为使用了 ECDHE,客户端可以不用等到服务器发回“Finished”确认握手完毕,立即就发出 HTTP 报文,省去了一个消息往返的时间浪费。这个叫“TLS False Start”,意思就是“抢跑”,和“TCP Fast Open”有点像,都是不等连接完全建立就提前发应用数据,提高传输的效率。

  所以你看,关键就在于用了ECDHE来作为核心参数生成Master Secret。

二、双向认证

  其实到这里TLS握手的核心就基本完事了。只不过大家发现一个问题没有,上图中,只有服务器传了Certificate,让客户端验证服务器的身份。而服务器并没有验证客户端的身份。这是因为通常单向认证通过后已经建立了安全通信,用账号、密码等简单的手段就能够确认用户的真实身份。

  但为了防止账号、密码被盗,有的时候(比如网上银行)还会使用 U 盾给用户颁发客户端证书,实现“双向认证”,这样会更加安全。

  熟悉不?现在知道为啥你之前去银行的时候,银行会给你个U盾,在网站上操作转账啥的时候,都必须插上U盾才行,现在知道这个U盾是用来干啥的了吧?就是给你本地的电脑安装证书。当然,现在好像基本上不用U盾了,直接从可信的网站上下载证书就可以了。

  双向认证的流程也没有太多变化,只是在“Server Hello Done”之后,“Client Key Exchange”之前,客户端要发送“Client Certificate”消息,服务器收到后也把证书链走一遍,验证客户端的身份。大家可以参照本章的图,自己理解一下噢~

  本篇,很重要,还有点复杂。大家要熟悉一下本章的两张握手图,理解TLS的握手过程。这个总结好像有点糊弄~~哈哈哈

  哦对,大家还可以在Chrome浏览器里的Security中查看HTTPS的相关信息。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK