概述

在之前我写过一篇介绍 HTTPS 如何握手的文章:HTTPS 握手过程,但是,那篇文章是纯嘴炮,从理论上介绍 HTTPS 的握手过程是怎么样的,虽然对我的帮助挺大,但是,这几天在我真的分析 HTTPS 数据包的时候,发现和现实还是有一点点的差距,于是今天我就介绍一下实战,从真实的数据包出发查看一下 HTTPS 握手的全过程。

HTTPS 握手过程回溯

在实战之前,我们回溯一下理论上的握手过程:

  1. Client 生成随机数 1,将使用的协议版本,支持的加密套件以及随机数,通过发送 hello 数据包给 Server
  2. Server 生成随机数 2,将首选的密码套件,服务器证书以及随机数 2 响应给 Client(明文)
  3. Client 生成随机数 3,所谓的 “准 master secret”,然后通过服务器证书中的 公钥 加密发送给服务器
  4. Server 通过 私钥 解密获取 “准 master secret”(唯一一次使用私钥)
  5. Client 和 Server 都有客户端随机数、服务器随机数和准主密钥。将这三个输入组合起来可得到 “会话主密钥(master secret)”,会话期间的所有后续通信都用这些新密钥进行加密。

然后我们先来个抓包的概览,这个包我感觉抓得非常好,因为它和理论的流程是一一匹配的(😂):

图 1:HTTPS 握手全过程

握手过程实战

Client Hello

图 2:Client Hello

所以 Client Hello 也是一个普通的 TCP 包,Server 端也会回一个 ack response(没有data)。

Server Hello

图 3:Server Hello

所以 Server Hello 也是一个普通的 TCP 包,Client 端也会回一个 ack response。

master secret

图 4:Master Secret

数据传输

图 5:HTTPS 传输的数据

高级功能

解密数据包

从前面的数据包中,我们可以看到 24 和 26 的数据包都写着 Application Data,但是因为是加密的数据,所以我们无法知道是什么内容。但是,在我们平时调试内容的时候,这其实很重要,因为我们业务上是关注这些内容的,比如 HTTP 的 header 值是否正确,值是什么,body 的长度有多长,这些都是我们关系的。

但是,我没有发现很方便地解密方式,我验证成功的一种方式是通过给浏览器设置环境变量,然后获取到一个 sslkey log 文件的方式来解密,具体的操作步骤为:

  1. 关闭你要用来抓包的浏览器,比如 Chrome 或者 Firefox
    • 注意:这里必须是完全关闭
  2. 打开终端,设置环境变量:export SSLKEYLOGFILE="/home/liuliqiang/ssl/sslkey.log"
  3. 在终端打开浏览器(必须和环境变量同个终端,不然不会生效):
    • Mac环境:open /Applications/Firefox.app &
  4. 运行 tcpdump,示例:sudo tcpdump -s0 -nn -w decrypt-ssl.pcap dst host 192.168.1.3 or src host 192.168.1.3
    • 这里需要将 192.168.1.3 换成你自己的服务器地址
  5. 在浏览器中访问你需要抓包的地址,例如:”http://3.localdomain.com
  6. 关闭 tcpdump,此时你会在你的当前目录下发现两个文件,分别是:
    • sslkey.log:这是浏览器生成的用于 wireshark 解密的 sslkey log 文件;
    • decrypt-ssl.pcap: 这个是 tcpdump 抓取的数据包。

然后我们打开 Wireshark:

  1. 打开 “Preferences…” -> “Protocols” -> “TLS” -> “(Pre)-Master-Secret log filename”,然后添加我们的 sslkey.log 文件;

  2. 打开 decrypt-ssl.pcap 文件,然后你就会发现之前显示为 “Application Data” 的数据就变成明文了:

从下面的详情里面我们可以看到,Hypertext Transfer Protocol 是在 Transport Layer Security 之下的,也就是表示这是加密的数据,只是现在被我们解密出来查看了。

其他方式

除了让浏览器生成 sslkey log 文件之外,curl 文件也也可以生成这个文件。但是,有些时候,可能我们无法获得这个文件,这个时候我们只能期望这里的加密算法是比较弱(相对)的 RSA:

  1. 打开 “Preferences…” -> “Protocols” -> “TLS” -> “RSA keys list”,然后添加我们的服务器证书私钥

  2. 然后就可以通过私钥解密了。

不过这种方式我没有验证成功(😂),因为我使用的是 let’s encrypt 的证书,他们使用的是 ECDHE_RSA 加密,这种加密方式的好处是即使你的证书泄密了,黑客无法通过证书解密它之前抓到的 HTTPS 数据;而使用 RSA 的加密算法,却是有这个问题。

Ref