解密 Java 的 https 流量
最近有个同事发现生产环境中有些服务比较慢, 检查一番后, 发现一个服务调用在tcp 3次握手和 ssl client hello 包之间竟然有200ms 左右的延迟. 接下来, 该同事就想弄清楚: 为什么在3次握手和 client hello之间有200ms 延迟. 他知道有些语言是直接调用的 OpenSSL 的库, 于是问我 Java 里面是不是也是使用 OpenSSL的封装?
我之前写过2篇 Windows 上解密 chrome 的 https 流量 & MAC 上解密 chrome 的 https 流量, 当时就想: 是不是Java 也可以自动把 ssl key 劫持下来, 然后后边自动揭秘. 当时就找到了这个已经持续好多年的项目: jsslkeylog, 这个项目就是通过 Java Agent的方式, 注入相关的代码, 把ssl key 劫持下来.
所以, 当同事问我是不是 OpenSSL 封装的时候, 我立马想到这个项目. 在这个项目里面, 它通过注入的方式把 ssl key 记录下来, 那么肯定是Java 自己原生写的 SSL 的代码处理.
一些核心的代码类: https://github.com/jsslkeylog/jsslkeylog/tree/master/src/main/java/net/sf/jsslkeylog
关于 NSS(Network Security Services)
NSS (Network Security Services) 是由 Mozilla 项目开发的一个模块化、可复用的网络安全服务库,它提供了一系列的安全协议、密钥管理、证书管理、加密算法等安全功能的实现。NSS 被广泛地应用于多种网络应用程序中,包括 Web 浏览器、电子邮件客户端、虚拟专用网络 (VPN) 和移动设备等。
NSS 的功能涵盖了多种协议和技术,包括 SSL、TLS、PKCS#11、S/MIME、X.509 证书、公钥基础设施 (PKI)、安全套接字层 (SSL) 和传输层安全 (TLS) 等。NSS 在这些领域是具有广泛应用的库之一,是开源社区中信任和可靠的安全库。
NSS 还为应用程序提供了一些基本的加密功能,包括随机数生成器、哈希函数、密钥交换、数字签名、加密和解密、证书签名和认证等。这些功能被设计成易于使用和可扩展的,帮助应用程序开发者更快速、更容易地构建安全的网络应用程序。
需要注意的是,NSS 是一个独立的项目,并不仅限于 Mozilla 项目中。它在其他开源项目中也被广泛使用,例如 Red Hat、Fedora、OpenSSL 和 OpenSSH 等。
项目地址: https://github.com/nss-dev/nss
wiki: https://en.wikipedia.org/wiki/Network_Security_Services
关于 NSS 的 key log 格式
官方文档已经找不到了, 这是一个缓存: https://udn.realityripple.com/docs/Mozilla/Projects/NSS/Key_Log_Format
你可以认为这是 NSS 暴露出的一个接口, 这样使用 Wireshark 就能够解密加密传输.
key log 的最新格式
这是 Mozilla 提交给 IEEE 的最新格式, 可以认为之前都不是经过认证, 只是 NSS 和 wireshark 之间商量的一个格式.
https://datatracker.ietf.org/doc/draft-thomson-tls-keylogfile/
wireshark 支持的格式
https://github.com/boundary/wireshark/blob/d029f48e4fd74b09848fc309630e5dfdc5d602f2/epan/dissectors/packet-ssl-utils.c#L4164-L4182
https://github.com/boundary/wireshark/blob/07eade8124fd1d5386161591b52e177ee6ea849f/epan/dissectors/packet-ssl-utils.c#L4204-L4207
两个揭秘 Java TLS 的工具
https://github.com/neykov/extract-tls-secrets/
https://github.com/jsslkeylog/jsslkeylog/tree/master/src/main/java/net/sf/jsslkeylog
使用 -Djavax.net.debug=all
生成的日志直接提取key的工具(TLS 1.3不适用):
https://dimosr.github.io/decrypting-tls-traffic-with-wireshark/
https://gist.github.com/tsaarni/14cc3341d0996e25671f5ca894842ec9
JDK 关于 javax.net.debug 的说明
https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/ReadDebug.html
JDK 使用到的关于TLS 版本的启动参数
-Djdk.tls.client.protocols=TLSv1.2,TLSv1.3
jdk.tls.client.protocols
是一个 JDK (Java Development Kit) 的系统属性,这个属性可以用来指定与客户端建立安全连接时所支持的 TLS 版本的范围。具体来讲,这个属性可以用来限制客户端连接到服务端时可用的 TLS 协议版本。jdk.tls.client.protocols 属性的值是一个以逗号分隔的 TLS 协议版本列表.-Dhttps.protocols=TLSv1.2,TLSv1.3
https.protocols
是一个 Java 系统属性, 可以用来指定在执行 HTTPS 请求时支持的协议版本。特别是当我们使用 Java 编写的程序向使用 HTTPS 存储在远程服务器上的资源发出请求时,这个属性就非常有用了。参数的值表示 Java 程序只会使用 TLS 1.2 和 TLS 1.3 协议版本来发起 HTTPS 请求。如果远程服务器不支持这些协议,则连接将失败。-Djavax.net.debug=ssl,keygen,handshake