Wireshark分析TLS协议

周末闲着无聊,随便看看TLS,巩固一下理解

主要参考文献:https://www.cnblogs.com/theseventhson/p/14613527.html

一、基本信息

1.TLS概述

​ TLS(Transport Layer Security)是一种用于在计算机网络上建立安全通信的协议,作为SSL协议的继承者,其能够有效的保证数据在传输过程中的机密性、完整性和可靠性。在TCP\IP模型的应用层和传输层之间提供了可靠的数据保护措施。

​ TLS包括如下四种协议:

  1. 握手协议(Handshake Protocol):在TLS连接建立时,客户端和服务器之间会执行一个握手协议,用于交换密钥、协商加密算法、认证服务器等。握手协议通常使用非对称加密算法(如RSA)和数字签名来实现安全性。
  2. 记录协议(Record Protocol):在TLS连接建立之后,数据会使用记录协议来加密、压缩和传输。记录协议通常使用对称加密算法(如AES)来实现高效性和安全性。
  3. 警告协议(Alert Protocol):用于向对等方发送警告消息,例如安全性问题或连接关闭。
  4. 更改密码协议(Change Cipher Spec Protocol):用于通知对等方加密参数已更改,以便使用新的加密算法或密钥。

​ 其中,最为核心的协议即握手协议,对标TCP的三次握手,TLS具有四次握手的过程,可参见官方文档给出的步骤(TLS1.2):https://datatracker.ietf.org/doc/rfc5246/

image-20230407124522609

​ 在此,不对步骤进行赘述。在2018年,TLS1.3被发布,与1.2的区别是:Session Ticket 机制和 Session ID 机制都被废弃了,其使用了新的机制来代替,并且改变了密钥派生的机制,废弃了一大堆已经不安全的算法,同时直接用 RSA 来交换密钥的方式和 DH 密码套件被废弃,现在使用的是 DHE/ECDHE,并且握手信息也被部分的进行了加密。

2.TLS1.3

具体我们直接来看 TLS 1.3 的握手流程:

  1. Client 生成 key_share 向 Server 发送

    key_share的结构为一个列表,其中具有多个 <密钥组名,通过 an 和对应组使用的 p 、g生成的 An > 的数据项。

    为了安全起见,每一个数据项中的 a(Client 私钥) 都是不一样的

    以上包含的多个组中并不只有 DHE 类型的组,还有 ECDHE 类型,其值中的加密组件所需参数不同

  2. Server 从多个密钥组中选择自己能够接受的密钥组;如果没有,则会直接响应自己能够接受的密钥组的组名,让 Client 重新生成对应的 A 并发送(此时 1⋅RTT 会退化为 2⋅RTT)。

    在选定密钥组后,Server 发送决定使用的密钥组的名称,同时使用该组的 p 和 g 和自己的 b 计算出 B ,紧接着根据密钥交换原理通过收到的 A 计算出 K 。

    在将”服务器端随机数”和 B 发送出去后,接下来,将通过 K 导出以下三个密钥:

    • master secret :主密钥
    • 客户端握手密钥:用来加密 Client -> Server 的握手信息
    • 服务器端握手密钥:用来加密 Server -> Client 的握手信息

    导出密钥后,接下的所有要发送的信息都将被对应的密钥进行加密。

    需要注意的是,尽管以上描述的结果是正确的的,但是”导出”的过程被省略了,实际上对于每个被导出的密钥,所参与的参数与过程都存在不同,且该过程中会使用到 [客户端随机数] 和 [服务器端随机数],这两参数的交换过程也被省略。如果你对于”导出”的过程感兴趣,建议阅读 RFC8446

    导出的操作可理解为使用单向散列函数,以上导出的每个密钥中,参与导出的参数并不完全相同,所以导出的密钥的值也并不同的。

  3. Client 收到来自 Server 的服务器端随机数和公钥 B 后,也进行以上的计算过程,并根据得出的”服务器端握手密钥”解密接下来收到的握手信息。

    在解密并验证完被加密的证书后,就能确保服务器的身份。同时仍会验证发过来的握手阶段的报文的摘要,确认握手过程未被篡改。

    在保证通信安全后,接下来会和 TLS 1.3 前一样发送握手阶段报文的摘要(当然,这会被”客户端握手密钥”加密)。

  4. 在两边做完自己的工作后,双方将从 master secret 中导出以下四个密钥:

    • 客户端通信密钥:用来加密 Client -> Server 的通信信息

      (上文的两个用于握手报文加密的密钥将会被丢弃)

    • 服务器端通信密钥:用来加密 Server -> Client 的通信信息

    • 恢复密钥:用来参与 PSK 的计算

    • 导出密钥:用来参与默认的密钥导出计算

  5. 接下来,双方将开始使用各自的通信密钥发送应用数据

以下为简易的握手流程图:

image-20230407133206529

二、抓包分析

1.概览

​ 选取TLS协议的握手协议进行分析,对通过本机浏览器访问 www.zhihu.com 的流量进行分析,详细拆解TLS1.2握手协议的信息。

​ 首先,需要获取对应域名的IP地址,以更好地筛选流量:

image-20230407130714024

​ 确定知乎IP后,可以在wireshark中限制 ip.addr == 111.13.143.153 进行抓包,排除无关数据包的影响。

值得注意的一点是,抓到完整的握手内容,需要在浏览器没有打开zhihu.com时,第一次打卡,如果浏览器的某个窗口已经打开了知乎的某个页面,或者短期曾经打开过,则不会进行握手协议。推测是浏览器直接通过已有的TLS 通道进行通信。

​ 成功抓包后,如下所示:

image-20230407133303505

​ 可以看到,在传输层通过TCP三次握手,建立了TCP通信后,开始进行SSL握手。这里有一个问题,即622为什么要发一个ACK,而不是直接回复Server Hello,推测这是因为相应时间较长,为了让Client继续等待,故发送ACK。

​ 从第627个包开始,server开始发送application data(猜测应该是网页相关的数据,比如html、js、图片、音视频等文件);从这里可以看出,client和server互相发送application data,仅耗费1-RTT,这也是tls1.3比tls1.2改进的地方.

2.握手流程

client hello

参考官网给出的消息格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Structure of this message:

uint16 ProtocolVersion;
opaque Random[32];

uint8 CipherSuite[2]; /* Cryptographic suite selector */

struct {
ProtocolVersion legacy_version = 0x0303; /* TLS v1.2 */
Random random;
opaque legacy_session_id<0..32>;
CipherSuite cipher_suites<2..2^16-2>;
opaque legacy_compression_methods<1..2^8-1>;
Extension extensions<8..2^16-1>;
} ClientHello;

Version:

​ 在TLS1.3中客户端在supported_versions扩展名中添加客户端所支持的TLS版本列表,与此同时,legacy_version必须设置为0x0303,即TLS1.2的版本号。如下所示:

image-20230407133453412

而后可以依次向下分析:

  • random:client随机生成一个32byte的数发给server。通常,在通信中以访给另一方发随机数的作用有:1)防止重放攻击 2)后续用于完整性校验,这里也不例外 3)生成encrypt-key,正式开始加密应用层的数据;
  • session ID:CS通信双方为了安全通信,需要使用非对称算法协商密钥,对称算法加密数据。服务器生成session id,存放本次session协商好的对称密钥。下次通信时,客户端带上session id,server就知道用哪个对称密钥继续加密数据了。
  • cipher suites:client列举自己支持的加密套件(共17种),可以看出前面一般是非对称加密算法,协商对称算法密钥的。后面都是加密应用数据的对称算法;后续server会根据自己的实际情况选择合适的加密套件。

​ 同时,较为重要的还有supported_groupskey_sharesignature_algorithms分别用来装载支持的曲线,对应的公钥参数,以及签名算法。如下所示:

image-20230407134010728

​ 其余的,如application_layer_protocol_negotiation用于表示应用层被保护的协议,servername表示服务端的域名,等等。

Server Hello

1
2
3
4
5
6
7
8
struct {
ProtocolVersion legacy_version = 0x0303; /* TLS v1.2 */
Random random;
opaque legacy_session_id_echo<0..32>;
CipherSuite cipher_suite;
uint8 legacy_compression_method = 0;
Extension extensions<6..2^16-1>;
} ServerHello;

官网查看,其协议格式如上:

​ 上文中,我们提到Server Hello前,Server先发了一句tcp ack数据包,在此过程中,根据client的加密套件、公钥继续算出自己公钥和私钥。

image-20230407134450491

Random: 32byte的随机数,作用和之前client发给server的随机数一样,1)防止重放 2)后续用于消息完整性校验,防止被篡改 3)生成encrypt-key,正式开始加密应用层的数据;

Cipher Suite:client在client hello包里面列举了其能支持的加密套件,这里server在里面选择了这个加密套件;后续双方就用AES_GCM来做对称加密;

Extensive-Key Share extension:对称加密算法是AES_GCM,通过Client的hello包,server获得了他的公钥,因此Server也要发送自己的公钥;

image-20230407142637787

  • Change Cipher Spec:用于通知client,后续通信时改变现在的加密方式,即改成对称密钥加密通信数据;

至此,Server Hello与Client Hello分析完毕。

3.加密信息分析

image-20230407144412240

​ Hello信息解析完毕后,可以看到,在e4之后,直接携带了加密后的传输数据。要使用wireshark对余下的数据进行解密,需要wireshark能直接访问对应的随机数等信息,可以使用指令(以Chrome为例):

"C:\Program Files\Google\Chrome\Application\chrome.exe" --ssl-key-logfile=%SSLKEYLOGFILE%

​ 将密钥dump到文件中(数量非常多):

image-20230407153022984

​ 而后,在wireshark中配置此文件,即可进行解密。那么,为什么Wireshark不能直接接收网络数据包中的信息,通过这些信息进行解密呢?这是因为使用ECDHE算法让双方互相交换公钥,然后各自根据曲线类型和对方的公钥生成对称加密的密钥pre-master-secret。这个对称密钥并未通过网络传输,所以wireshark是抓不到的。通过让浏览器把pre-master-secret导出到指定的log文件,然后wireshark去指定的文件读密钥,就能解密application data了。