正因种种严苛条件,TLS才够安全,因为满足这些前提条件后,真正数据传送就令人放心。除非你调动超级计算机,要不然一个TLS连接里的加密数据,你真无法破解。

但若排查工作确实需要解开密文,查看应用层信息,又该咋办?

研究TLS解密的技术要点及背后技术原理,最后实战。

1 TLS加密原理

TLS结合了对称加密和非对称加密这两大类算法的优点,密码套件是四种主要加密算法的组合。

1.1 解读TLS证书

下面这证书是访问站点 https://sharkfesteurope.wireshark.org 时获取。

解密HTTPS流量?TLS加解密教程告诉你!_抓包

站点名称跟证书名称不一致?浏览器为啥不报错?

解密HTTPS流量?TLS加解密教程告诉你!_客户端_02

这的站点名称跟证书实际是匹配的,但它匹配的不是Common Name,而是SAN。

TLS证书为支持更多域名,设计了扩展选项Subject Alternative Name,SAN,它就包含多个域名。比如还是这张证书,它的SAN里的域名里就有:

  • wireshark.org
  • sni.cloudflaressl.com
  • 还有跟这次访问的站点名直接相关的*.wireshark.org。这是通配符域名,即sharkfesteurope.wireshark.org也被支持

SAN列表:

解密HTTPS流量?TLS加解密教程告诉你!_Wireshark_03

通配符证书只支持一级域名,如 *.wireshark.org 证书支持域名:

  • a.wireshark.org
  • b.wireshark.org

但不支持:

  • a.b.wireshark.org
  • a.b.c.wireshark.org

密码套件

这证书里密码套件是啥?

密钥交换算法是啥?证书里看不出,需根据握手协商的结果判定。但也可初步判断。如这次通信用TLS版本1.3,那就是DHE或ECDHE这样“前向加密”密钥交换算法。结尾E是Ephemeral,“短时间的”,即密钥是每次会话临时生成。

身份验证和签名算法

证书里明确写的ECDSA,EC就是Elliptic Curve,椭圆曲线算法,它可用更短密钥达到跟RSA同样密码强度。后面的SHA-256哈希摘要算法,证书内容用这SHA-256算法做哈希摘要,然后ECDSA算法对摘要值做签名,这样客户端就可验证这张证书的内容有没有被篡改。

解密HTTPS流量?TLS加解密教程告诉你!_客户端_04

对称加密算法是啥?证书这里看不出,因为它也是通过握手协商出的。用OpenSSL或者curl命令就可观察到,稍后演示。

完整性校验算法

2里面已经提过了,是SHA-256。

OpenSSL直接观测到这次TLS里协商出的密码套件:

1$ openssl s_client -connect sharkfesteurope.wireshark.org:4432......3New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA3844......

这里用的就是TLS1.3版本,密码套件TLS_AES_256_GCM_SHA384。它跟TLS1.2的那些密码套件相比还有个区别?比如跟下面这个TLS1.2密码套件比较:

TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA

TLS1.3的套件 TLS_AES_256_GCM_SHA384 少俩算法:身份验证和签名算法,还有密钥交换算法。身份验证和签名算法倒是可从证书看到,ECDSA。

密钥交换算法哪去了?

解密HTTPS流量?TLS加解密教程告诉你!_抓包_05

因为TLS1.3只允许前向加密(PFS)的密钥交换算法,所以使用静态密钥的RSA已被排除,它默认使用DHE和ECDHE,所以就不写在密码套件名称里。

2 前向加密(PFS)

又称“完美前向加密”,Forward Secrecy和Perfect Forward Secrecy。

TLS1.3去掉了RSA,不过TLS1.3只是把RSA从密钥交换算法中排除,证书签名算法还是能用RSA。

为啥TLS1.3强制前向加密?

若密钥交换时用非前向加密算法(如RSA),一旦黑客取得服务端私钥,并抓取历史TLS密文,他就用这私钥和抓包文件,把这些TLS会话的对称密钥给还原,从而破解所有这些密文。因为可以把之前的密文都破解,RSA就不属于“前向”加密。

解决的关键: 每次参与协商对称密钥时的非对称密钥都不一样。即使黑客破解其中一次会话的密钥,也无法用这密钥破解其他会话。

案例理解

假设不断生成一个个保险箱,相当于一个个TLS加密报文,如每个箱子用同样锁,一旦其中一把锁被破解,所有保险箱都可被打开。用“前向加密”锁,每次新的保险箱都用不同锁,即使一把锁被破解,损失只是一个保险箱,其他箱仍安全。

3 TLS的软件实现

TLS只是协议,具体还得是代码。目前最流行SSL/TLS实现是OpenSSL:

  • 开发库
  • 也是命令行工具的名称

NSS、GnuTLS也是开源TLS实现。应用程序会基于这些TLS库来实现TLS加解密功能。

解密HTTPS流量?TLS加解密教程告诉你!_Wireshark_06

像OSI分层模型,业务代码工作在应用层,TLS库工作在表示层和会话层,两层有交互有解耦,很好协同。

解密HTTPS流量?TLS加解密教程告诉你!_Wireshark_07

来看TLS抓包解密。

4 客户端咋解密TLS?

客户端包括:

  • Chrome等浏览器
  • 也包括curl等命令行工具

为解密TLS,需完成前提条件:

  • 创建一个存放key信息的日志文件,然后在系统 配置环境变量SSLKEYLOGFILE,值就是这文件路径
  • 重启浏览器,启动抓包程序,然后访问HTTPS站点,此时TLS密钥信息将会导出到这个日志文件,而加密报文也会随着抓包,被保存到抓包文件

补充:若是Mac又不想改动全局配置,可在terminal export SSLKEYLOGFILE= 路径,然后执行 open "/Applications/Google\ Chrome.app",这时Chrome就继承这shell父进程的环境变量,而terminal退出后,这个环境变量就自动卸除

  • Wireshark打开Preferences菜单,Protocol列表里找到TLS,把 (Pre)-Master-Secret log filename配置为那个文件的路径

解密HTTPS流量?TLS加解密教程告诉你!_客户端_08

之后用Wireshark打开抓包文件,就能看到解密后报文,如HTTP请求和响应,还有TLS的控制信息,都会展示为明文。

如默认看到密文:

解密HTTPS流量?TLS加解密教程告诉你!_抓包_09

抓包示例文件已经上传至 Gitee,结合抓包文件和文稿。

配置解密步骤后,看到明文:

解密HTTPS流量?TLS加解密教程告诉你!_客户端_10

很多应用层的信息都可以辅助你做排查。

背后原理是啥?

浏览器启动过程尝试读SSLKEYLOGFILE环境变量。如存在这变量,而它指向一个有效文件,浏览器就会调TLS库,让TLS库把访问HTTPS站点的过程中的key信息导出到SSLKEYLOGFILE文件:

解密HTTPS流量?TLS加解密教程告诉你!_Wireshark_11

为啥这日志文件这么强,能解密TLS?如这文件“被黑客利用”,咋办?*

5 SSLKEYLOGFILE

能解密TLS,最关键是,TLS库把密钥交换阶段的核心信息Master secret导出到这文件。基于这信息,Wireshark就可还原出当时的对称密钥,破解密文。

5.1 格式

由很多条记录组成,TLS1.2,每行是一条记录,每条记录由3部分组成,空格分隔:

  • <Label1> <ClientRandom1> <Secret1>
  • <Label2> <ClientRandom2> <Secret2>
  • ……

① Label - 记录的描述

TLS1.2这值一般是CLIENT_RANDOM**。RSA也是一个可能的值,但RSA算法在密钥交换方面不是前向加密,所以不推荐使用。如你在日志文件里看到RSA,小心,说明你的TLS不是前向加密,不是很安全。

② ClientRandom-客户端生成的随机数

随机数原始长度为32字节,但在这里是用16进制表示的,每个字节用16进制表示就会成为2个字符,所以就变成了64个字符的字符串。我们在抓包文件里也能看到它,因为在密钥交换算法的设计中,ClientRandom就是要在网络上公开传输的。

③ Secret:Master secret 通过它可以生成对称密钥

Master secret固定是48字节,也是十六进制表示的关系,成为96个字节的字符串。你应该明白了,这个Master secret就是最为关键的信息了,也正是黑客苦苦寻求的东西。它是万万不能在网络上传输的,自然也不可能在抓包文件里看到它,只有TLS库才能导出它。

TLS1.3格式很不同 链接

TLS1.2的KEYLOGFILE例子:

1CLIENT_RANDOM 770c2c73ef1ab58dda9360a94587e5f8b0a80c0b1abf628ddd7b55a118ec18ec bea2c01c5b6f9c577e8ba251c8f262adf33c5aa31a238d464a9c56dbd1bf30cf55cbf14e6175102fa1db9b8a0183a721

Gitee,可按照前面介绍的3个步骤,结合上面的抓包文件和这个日志文件,观察解密前后区别。

在输出这个key信息的时候,我们也做了对应的抓包,现在看一下抓包文件。我们选中Client Hello报文,点开TLS详情部分,继续点开TLSv1.2 Record Layer -> Handshake Protocol -> Random,你看看它是不是就是前面日志文件里,第二列的值(开头是770c)?

解密HTTPS流量?TLS加解密教程告诉你!_抓包_12

SSLKEYLOGFILE日志文件格式我们了解了,接下来了解Wireshark是怎么跟它协同工作,解开密文的。

6 Wireshark咋解密?

TLS1.2的SSLKEYLOGFILE中,每条记录的:

  • 第一列是CLIENT_RANDOM这个字符串
  • 第二列是这client random值,Wireshark就是通过它,找到对应TLS会话(可理解为TCP流)。就像上图所示,通过这个随机数,就找到这条KEYLOG记录对应的TLS会话。

解密HTTPS流量?TLS加解密教程告诉你!_Wireshark_13

接下来,Wireshark就知道真正的Master secret在哪里了:它就是前面匹配了这个客户端随机数的记录的第三列,也就是那96个字节的字符串。

解密HTTPS流量?TLS加解密教程告诉你!_Wireshark_14

由于在抓包文件里就有ECDHE密钥交换算法所需要的各种参数,结合这里的Master secret,Wireshark就可以解析出对称密钥,从而把密文解密了!

解密HTTPS流量?TLS加解密教程告诉你!_Wireshark_15

SYSKEYLOGFILE的安全性

想破解密文,既要有抓包文件,也要有SSLKEYLOGFILE日志文件,才能解密。即使都有,要是没抓到TLS握手阶段报文,还是不能解密,因为缺少客户端随机数、加密算法参数等信息,Master secret你也无法下嘴。

7 服务端如何解密TL

上面的客户端解密过程。

服务端做TLS解密,TLS是双方的加密任务,但我们一边倒关心客户端如何解密,却对服务端的解密不闻不问:

  • 多数人接触的还是客户端,能在客户端解密已满足大多需求。服务端只有一部分专职运维或开发维护,关注度少
  • 服务端一般有详细日志,如Nginx可向日志里输出HTTP头部和性能数据,还可输出TLS选择的密码套件等信息,这些在一般场景下也够用
  • 很多服务端程序并没有提供TLS解密功能,即想做抓包解密也做不了。而要自己实现这个特性,难度跟简单的参数配置,不在一个等级。难度高

软件架构上,服务端和客户端类似,也是基于TLS库构建TLS加解密能力。

解密HTTPS流量?TLS加解密教程告诉你!_Wireshark_16

eBay软件负载均衡方案,第七层是基于 Envoy 实现。在把一套新系统搬上生产环境之前有一系列的考量,就像体检检查表逐一校验。Envoy体检有项“不合格”:Envoy不提供对TLS流量进行抓包解密功能。

Envoy是硅谷的共享出行公司Lyft于2016年发起的开源项目,可以认为是云原生时代的Nginx。

在不少情况下,这个抓包解密特性对排查很有用。像一些商业产品比如Netscaler就是可以一边抓包,一边导出TLS key。就跟我们在客户端做解密类似,我们结合这两个文件,就可以在Wireshark中既观察到TCP行为,也读到应用层信息了。

你可能会问:“Envoy可以输出Web日志呀,把各种HTTP性能指标、访问头部,甚至包括用了什么Cipher,都输出到文件,这样还不香吗?”

其实,我在开篇词和 第4讲 中都提到过网络排查的两大鸿沟。

  • 应用现象跟网络现象之间的鸿沟:你可能看得懂应用层的日志,但是不知道网络上具体发生了什么。
  • 工具提示跟协议理解之间的鸿沟:你看得懂 Wireshark、tcpdump 这类工具的输出信息的含义,但就是无法真正地把它们跟你对协议的理解对应起来。

而包括Envoy在内的反向代理和LB软件,虽然也都提供了应用层日志,但跟实际的网络行为还有距离,这就是“应用现象跟网络现象之间的鸿沟”:日志是日志,网络是网络。如果日志里说某个HTTP请求耗时很长,你是无法知道网络上到底什么问题导致了这么长的耗时,是丢包引起了重传?还是没有丢包,纯粹是传输速度慢呢?

为了跨越第一个鸿沟,我们选择做tcpdump抓包。但是,如果抓取到的TLS密文无法被解密,就无法知道这些究竟是应用层的什么信息,这个鸿沟依然没有被跨越。

解密HTTPS流量?TLS加解密教程告诉你!_客户端_17

当然,我们可以选择“妥协”,采用一些灵活的策略来开展抓包分析。比如,可以把抓包分析的重心转移到TCP和TLS本身的层面,而不再关心其承载的应用层信息。但是“隔靴搔痒”总让排查工作不是特别“痛快”,我们犹豫许久之后,还是决定把这个解密的特性实现!

这并不是一件容易的事情。不过通过调研,我们发现,其实在服务端启用跟客户端类似的TLS解密功能,技术上是可行的,其中最为关键的信息就在2017年一位Wireshark开发工程师的 演讲 中:

Applications using OpenSSL 1.1.1 or BoringSSL d28f59c27bac (2015-11-19) can be configured to dump keys: void SSLCTXsetkeylogcallback(SSL CTX ∗ctx, void (∗cb)(const SSL ∗ssl, const char ∗line));

也就是说,只要我们使用这个BoringSSL(是谷歌Fork自OpenSSL的项目)的 SSLCTXsetkeylogcallback() 回调函数,就可以把TLS信息导出来,于是我们信心大增。核心突破就是要把这个BoringSSL的回调函数给用起来。

我们需要:

  • 在Envoy代码中增加调用SSLCTXsetkeylogcallback()的逻辑
  • 增加了对外的接口,使得用户可以通过某种方式让Envoy知道,它需要去使用这个调用逻辑。

第二点,其实就是一种接口方式,比如SSLKEYLOGFILE环境变量就是一种,我们也可以选择API接口,或者某种别的接口。总之,只要让程序(这里是Envoy)知道:它需要去叫BoringSSL这个小弟去办点事情,整个功能就可以运作起来了。你可以参考这张示意图:

解密HTTPS流量?TLS加解密教程告诉你!_Wireshark_18

“体检通过”!我们在服务端Envoy上也可以做到方便的TLS解密了。其实,不仅实现了这个具体的需求本身,也实现了我们作为技术工作者,对“自我实现”的需求。

这个特性的主要开发者张博已经提交了PR,相信不久之后我们就能在正式版的Envoy里用上这个特性了。

实现Envoy的TLS抓包解密的具体的做法跟客户端解密的步骤差不多:

  • 调用Envoy接口,启用SSL KEY导出功能。
  • 做tcpdump抓包,然后把抓包文件和KEY文件复制出来。
  • 在Wireshark里同样配置好 TLS协议的(Pre)-Master-Secret log filename,打开抓包文件后,就可以跟在客户端类似,直接看到明文了。

8 总结

解读一张真实的TLS证书,复习各加密算法在现实场景中的实现:

  • 证书中的SAN列表包括了它所支持的站点域名,所以只要被访问的站点名称在这个列表里,名称匹配就不是问题了。
  • 证书中的域名通配符只支持一级域名,而不支持二级或者更多级的域名。
  • 在TLS1.3中,密钥交换算法被强制要求是前向加密算法,所以默认采用DHE和ECDHE,而RSA已经弃用。
  • RSA依然可以作为可靠的身份验证和签名算法来使用。另外一种验证和签名算法是ECDSA,它可以用更短的密钥实现跟RSA同样的密码强度。
  • 前向加密可以防止黑客破解发生在过去的加密流量,提供了更好的安全性。

之后就是这节课的核心了: 如何做到对抓包文件进行解密。这里又分客户端和服务端两个不同场景,你也需要重点关注。

首先,在客户端做抓包解密,需要做三件事:

  • 创建一个文件,并设置为SSLKEYLOGFILE这个环境变量的值;
  • 重启浏览器,开始做抓包,此时key信息被浏览器自动导入到日志文件;
  • 在Wireshark里把该日志文件配置为TLS的(Pre)-Mater-Secret log filename。

这样,我们就能在Wireshark里直接读取到应用层信息了。

而在服务端抓包解密,就要依托于软件实现了,但是有些软件并没有提供这种功能,比如Envoy。借助底层BoringSSL库的接口,eBay流量管理团队实现了对这个接口的调用,我们也可以在Envoy上完成抓包解密了。

Wireshark解读密文的原理:

  • 从抓包文件中定位到client random
  • 从日志文件中找到同样这个client random,然后找到紧跟着的Master secret
  • 用这个Master secret导出对称密钥,最后把密文解密

9 FAQ

能实时查看解密信息?

行。只要设置好:

  • SSLKEYLOGFILE环境变量
  • 再启动浏览器,然后直接在Wireshark里开始抓包
  • 设置Wireshark的TLS协议,配置(Pre-)Master secret logfile

这时访问HTTPS站点,在Wireshark里看到的就直接是解密好的信息。因为Wireshark已经能从SSLKEYLOGFILE读到密钥信息,同时又在实时抓取到TLS密文,这种解密工作可实时进行。

为啥停止抓包后再启动抓包,抓包文件又变成密文?

重启浏览器后,Wireshark里马上就能看到HTTP数据包,确实能解密。但停止抓包后,再启动抓包,看到又变成TLS密文。重启浏览器才行。

密文是用对称密钥加密。而对称密钥的生成,是在TLS握手阶段完成的。我们前面提到过,Wireshark(也包括其他需要读取SSLKEYLOGFILE的程序)正是根据第二列的客户端随机数,来找到抓包文件中的TLS session,然后运用第三列的Master secret来获取到对称密钥的。

抓包停止后,新的HTTPS请求所触发的TLS握手就不会被抓取到。这也就意味着,Wireshark没有抓取到客户端随机数这个关键信息,尽管SSLKEYLOGFILE里依然在输出着一行行的key信息,但是Wireshark已经不知道用哪个Master secret了。自然,解密就无从做起。

解密HTTPS流量?TLS加解密教程告诉你!_抓包_19

而在浏览器重启后,事实上造成了TLS的重新握手,此时就又可以抓取到客户端随机数了,这样,解密工作就可以恢复。你看,这其实跟 第3讲 中,没抓到TCP握手报文就无法知道Window Scale参数这个问题差不多,也是关于握手的,只不过这次是TLS握手。“技术是相通的”,这句话真不是随便说说。

DH、DHE、ECDHE,这三者的联系和区别是啥?

DH、DHE和ECDHE都是用于加密通信中的密钥交换算法。它们的联系是都是通过非对称加密来实现密钥交换,从而确保加密通信的安全性。

区别在于它们所使用的算法不同。DH是基于离散对数问题的算法,DHE是DH算法的一种实现,其中E表示"ephemeral",即临时的。ECDHE则是基于椭圆曲线离散对数问题的算法。

总体来说,ECDHE相对于DH和DHE来说更加安全且效率更高,因此在现代加密通信中被广泛应用。

浏览器会根据SSLKEYLOGFILE这个环境变量,把key信息导出到相应的文件,那么curl也会读取这个变量并导出key信息吗?

是的,curl也可以读取SSLKEYLOGFILE环境变量并将key信息导出到指定的文件中。在使用curl时,可以设置该环境变量来启用key信息的导出功能,例如:

1export SSLKEYLOGFILE=/path/to/ssl_key.log2curl https://example.com

这将会将key信息导出到/path/to/ssl_key.log文件中。需要注意的是,只有启用了TLS调试模式的curl才能够导出key信息。可以使用-v选项来启用调试模式,例如:

1curl -v https://example.com

这将会打印出TLS握手过程中的详细信息,包括key信息。