概述
在之前我写过一篇介绍 HTTPS 如何握手的文章:HTTPS 握手过程,但是,那篇文章是纯嘴炮,从理论上介绍 HTTPS 的握手过程是怎么样的,虽然对我的帮助挺大,但是,这几天在我真的分析 HTTPS 数据包的时候,发现和现实还是有一点点的差距,于是今天我就介绍一下实战,从真实的数据包出发查看一下 HTTPS 握手的全过程。
HTTPS 握手过程回溯
在实战之前,我们回溯一下理论上的握手过程:
- Client 生成随机数 1,将使用的协议版本,支持的加密套件以及随机数,通过发送 hello 数据包给 Server
- Server 生成随机数 2,将首选的密码套件,服务器证书以及随机数 2 响应给 Client(明文)
- Client 生成随机数 3,所谓的 “准 master secret”,然后通过服务器证书中的 公钥 加密发送给服务器
- Server 通过 私钥 解密获取 “准 master secret”(唯一一次使用私钥)
- 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 文件的方式来解密,具体的操作步骤为:
- 关闭你要用来抓包的浏览器,比如 Chrome 或者 Firefox
- 注意:这里必须是完全关闭
- 打开终端,设置环境变量:
export SSLKEYLOGFILE="/home/liuliqiang/ssl/sslkey.log"
- 在终端打开浏览器(必须和环境变量同个终端,不然不会生效):
- Mac环境:
open /Applications/Firefox.app &
- Mac环境:
- 运行 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
换成你自己的服务器地址
- 这里需要将
- 在浏览器中访问你需要抓包的地址,例如:”http://3.localdomain.com“
- 关闭 tcpdump,此时你会在你的当前目录下发现两个文件,分别是:
- sslkey.log:这是浏览器生成的用于 wireshark 解密的 sslkey log 文件;
- decrypt-ssl.pcap: 这个是 tcpdump 抓取的数据包。
然后我们打开 Wireshark:
打开 “Preferences…” -> “Protocols” -> “TLS” -> “(Pre)-Master-Secret log filename”,然后添加我们的 sslkey.log 文件;
打开 decrypt-ssl.pcap 文件,然后你就会发现之前显示为 “Application Data” 的数据就变成明文了:
从下面的详情里面我们可以看到,Hypertext Transfer Protocol 是在 Transport Layer Security 之下的,也就是表示这是加密的数据,只是现在被我们解密出来查看了。
其他方式
除了让浏览器生成 sslkey log 文件之外,curl 文件也也可以生成这个文件。但是,有些时候,可能我们无法获得这个文件,这个时候我们只能期望这里的加密算法是比较弱(相对)的 RSA:
打开 “Preferences…” -> “Protocols” -> “TLS” -> “RSA keys list”,然后添加我们的服务器证书私钥
然后就可以通过私钥解密了。
不过这种方式我没有验证成功(😂),因为我使用的是 let’s encrypt 的证书,他们使用的是 ECDHE_RSA 加密,这种加密方式的好处是即使你的证书泄密了,黑客无法通过证书解密它之前抓到的 HTTPS 数据;而使用 RSA 的加密算法,却是有这个问题。