英文:
Extracting TLS secrets in Go
问题
我不完全确定这是否真的是一个关于Wireshark、Go或Syncthing的问题;我尝试过Wireshark开发者列表和Go开发者列表,但没有得到回应,所以我想在这里试试:
我正在开发一个Wireshark Syncthing解析器。由于大部分Syncthing协议都封装在TLS中,我需要将TLS密钥提供给Wireshark。
我阅读了Wireshark的TLS文档;Syncthing是用Go编写的,所以我对其进行了补丁以导出TLS密钥,像这样(这只是Syncthing上游代码加上了最后两行):
// TLS配置用于监听套接字和出站连接。
var tlsCfg *tls.Config
if a.cfg.Options().InsecureAllowOldTLSVersions {
l.Infoln("允许在同步连接上使用TLS 1.2。这不是最优安全性。")
tlsCfg = tlsutil.SecureDefaultWithTLS12()
} else {
tlsCfg = tlsutil.SecureDefaultTLS13()
}
tlsCfg.Certificates = []tls.Certificate{a.cert}
tlsCfg.NextProtos = []string{bepProtocolName}
tlsCfg.ClientAuth = tls.RequestClientCert
tlsCfg.SessionTicketsDisabled = true
tlsCfg.InsecureSkipVerify = true
// 下面两行打开一个文件,并配置应用程序将其TLS密钥转储到该文件中
// 参见:https://pkg.go.dev/crypto/tls#example-Config-KeyLogWriter
w, err := os.OpenFile("tls-secrets.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
tlsCfg.KeyLogWriter = w
这个方法是有效的,指定的文件中写入了各种内容,但是将该文件提供给Wireshark并不能启用TLS解密。我检查了文件,发现它包含CLIENT_HANDSHAKE_TRAFFIC_SECRET
、SERVER_HANDSHAKE_TRAFFIC_SECRET
、CLIENT_TRAFFIC_SECRET_0
和SERVER_TRAFFIC_SECRET_0
行,但没有关键的CLIENT_RANDOM
行。我是不是做错了什么或者漏掉了什么?
英文:
I'm not totally sure whether this is really a Wireshark, Go, or Syncthing question; I tried the Wireshark dev list and the Go dev list but got no response, so I figured I'll try here:
I'm working on a Wireshark Syncthing dissector. Since most of the Syncthing protocols are encapsulated in TLS, I need to provide the TLS secrets to Wireshark.
I read the Wireshark TLS documentation; Syncthing is written in Go, so I patched it to export TLS secrets, like this (this is just Syncthing upstream code with the addition of the two final lines):
// The TLS configuration is used for both the listening socket and outgoing
// connections.
var tlsCfg *tls.Config
if a.cfg.Options().InsecureAllowOldTLSVersions {
l.Infoln("TLS 1.2 is allowed on sync connections. This is less than optimally secure.")
tlsCfg = tlsutil.SecureDefaultWithTLS12()
} else {
tlsCfg = tlsutil.SecureDefaultTLS13()
}
tlsCfg.Certificates = []tls.Certificate{a.cert}
tlsCfg.NextProtos = []string{bepProtocolName}
tlsCfg.ClientAuth = tls.RequestClientCert
tlsCfg.SessionTicketsDisabled = true
tlsCfg.InsecureSkipVerify = true
// The following two lines open a file in the current directory and configure the application to dump its TLS secrets there
// See: https://pkg.go.dev/crypto/tls#example-Config-KeyLogWriter
w, err := os.OpenFile("tls-secrets.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
tlsCfg.KeyLogWriter = w
This works, and various stuff is written to the specified file, but providing that file to Wireshark doesn't enable TLS decryption. I examined the file, and I see that it contains CLIENT_HANDSHAKE_TRAFFIC_SECRET
, SERVER_HANDSHAKE_TRAFFIC_SECRET
, CLIENT_TRAFFIC_SECRET_0
, and SERVER_TRAFFIC_SECRET_0
lines, but not the crucial CLIENT_RANDOM
lines. Am I doing something wrong or missing something?
答案1
得分: 1
根本原因是tls1.2
和tls1.3
之间的差异,可以在这里找到差异。
根据golang的tls代码,以下是一些测试样例:
首先,启动一个HTTPS服务器:
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
if req.URL.Path != "/" {
http.NotFound(w, req)
return
}
})
w, err := os.OpenFile("/keypath/https-key.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
fmt.Printf("failed to open file err %+v", err)
return
}
cs := make([]uint16, len(cipherSuites))
copy(cs, cipherSuites)
var tlsCfg tls.Config
tlsCfg.Certificates = make([]tls.Certificate, 1)
tlsCfg.Certificates[0], err = tls.LoadX509KeyPair(*certFile, *keyFile)
tlsCfg.NextProtos = []string{"h2"}
tlsCfg.ClientAuth = tls.RequestClientCert
tlsCfg.SessionTicketsDisabled = true
tlsCfg.InsecureSkipVerify = true
tlsCfg.KeyLogWriter = w
tlsCfg.MinVersion = tls.VersionTLS13
tlsCfg.CipherSuites = cs
tlsCfg.PreferServerCipherSuites = true
srv := &http.Server{
Addr: *addr,
Handler: mux,
TLSConfig: &tlsCfg,
}
log.Printf("Starting server on %s", *addr)
err = srv.ListenAndServeTLS("", "")
log.Fatal(err)
然后,使用curl
进行测试,使用tls1.3
:curl -Lv https://localhost:4000 --cacert /crtpath/ca.crt --tlsv1.3
。
我们可以在https-key.txt
中找到以下内容:
> cat https-key.txt
CLIENT_HANDSHAKE_TRAFFIC_SECRET xxxx yyyyy
SERVER_HANDSHAKE_TRAFFIC_SECRET xxxx yyyyyyyyy
CLIENT_TRAFFIC_SECRET_0 xxxxxxx yyyy
SERVER_TRAFFIC_SECRET_0 xx yyyyyyyyyyyy
然后,在WireShark中将keyFile
设置为Preferences -> Protocols -> TLS -> (Pre)-Master-Secret log filename
,现在WireShark可以进行TLS解密。
对于TLS1.2的测试,您可以将服务器代码更改为tlsCfg.MinVersion = tls.VersionTLS12
,然后使用tls1.2
进行测试:curl -Lv https://localhost:4000 --cacert /crtpath/ca.crt --tlsv1.2
。再次检查https-key.txt
,您会发现内容可能是CLIENT_RANDOM xxxxxx yyyyyyyy
。
英文:
The root cause is the difference between tls1.2
and tls1.3
, the difference could be found here
Per golang tls code
keyLogLabelTLS12 = "CLIENT_RANDOM"
keyLogLabelClientHandshake = "CLIENT_HANDSHAKE_TRAFFIC_SECRET"
keyLogLabelServerHandshake = "SERVER_HANDSHAKE_TRAFFIC_SECRET"
keyLogLabelClientTraffic = "CLIENT_TRAFFIC_SECRET_0"
keyLogLabelServerTraffic = "SERVER_TRAFFIC_SECRET_0"
- For
tls1.3
, those parametersCLIENT_HANDSHAKE_TRAFFIC_SECRET
,SERVER_HANDSHAKE_TRAFFIC_SECRET
,CLIENT_TRAFFIC_SECRET_0
, andSERVER_TRAFFIC_SECRET_0
could be exported as client secrets - For
tls1.2
,CLIENT_RANDOM
could be exported as client secrets
All of them could be used in Wireshark to decrypt TLS1.2 and TLS 1.3.
Here is one test sample
First, start one HTTPS server
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
if req.URL.Path != "/" {
http.NotFound(w, req)
return
}
})
w, err := os.OpenFile("/keypath/https-key.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
fmt.Printf("failed to open file err %+v", err)
return
}
cs := make([]uint16, len(cipherSuites))
copy(cs, cipherSuites)
var tlsCfg tls.Config
tlsCfg.Certificates = make([]tls.Certificate, 1)
tlsCfg.Certificates[0], err = tls.LoadX509KeyPair(*certFile, *keyFile)
tlsCfg.NextProtos = []string{"h2"}
tlsCfg.ClientAuth = tls.RequestClientCert
tlsCfg.SessionTicketsDisabled = true
tlsCfg.InsecureSkipVerify = true
tlsCfg.KeyLogWriter = w
tlsCfg.MinVersion = tls.VersionTLS13
tlsCfg.CipherSuites = cs
tlsCfg.PreferServerCipherSuites = true
srv := &http.Server{
Addr: *addr,
Handler: mux,
TLSConfig: &tlsCfg,
}
log.Printf("Starting server on %s", *addr)
err = srv.ListenAndServeTLS("", "")
log.Fatal(err)
Then, test it through curl
with tls1.3
curl -Lv https://localhost:4000 --cacert /crtpath/ca.crt --tlsv1.3
We could find the content of https-key.txt
as below
> cat https-key.txt
CLIENT_HANDSHAKE_TRAFFIC_SECRET xxxx yyyyy
SERVER_HANDSHAKE_TRAFFIC_SECRET xxxx yyyyyyyyy
CLIENT_TRAFFIC_SECRET_0 xxxxxxx yyyy
SERVER_TRAFFIC_SECRET_0 xx yyyyyyyyyyyy
Then set the keyFile /keypath/grpc-key.txt
to Preferences -> Protocols -> TLS -> (Pre)-Master-Secret log filename
in WireShark, now the Wireshark could do TLS decryption
For TLS1.2 test, you could change the server code to tlsCfg.MinVersion = tls.VersionTLS12
and then test it through curl
with tls1.2
curl -Lv https://localhost:4000 --cacert /crtpath/ca.crt --tlsv1.2
. And check the https-key.txt
again, you could find the content could be CLIENT_RANDOM xxxxxx yyyyyyyy
.
答案2
得分: 0
我弄清楚了,得到了@zangw的很多帮助。有两个问题:
- 尽管一些关于Wireshark中TLS解密的讨论提到了
CLIENT_RANDOM
,但这只适用于TLS 1.2;当前的Syncthing通常使用TLS 1.3,其中涉及到我看到的其他密钥(CLIENT_HANDSHAKE_TRAFFIC_SECRET
、SERVER_HANDSHAKE_TRAFFIC_SECRET
、CLIENT_TRAFFIC_SECRET_0
和SERVER_TRAFFIC_SECRET_0
)- 参见官方NSS密钥日志格式文档。 - 我开始捕获的时间太晚了(因为我在等待密钥被写入文件以便能够提供给Wireshark),所以Wireshark错过了初始的TLS握手,导致协议被错误地识别为TLS 1.2而不是1.3,并且无法解密流量。正确的做法是在TLS协商开始之前开始捕获,然后在创建密钥文件后将其提供给Wireshark。(这可能需要保存和重新加载捕获文件。)
Wireshark现在成功解密了TLS数据;可以通过选择“Encrypted Application Data”,然后点击窗口底部的“Decrypted TLS”选项卡来查看。
英文:
I figured it out, with a lot of help from @zangw. There were two problems:
- Although some discussions of TLS decryption in Wireshark mention
CLIENT_RANDOM
, this is only for TLS 1.2; current Syncthing generally uses TLS 1.3, which involves the other secrets I was seeing (CLIENT_HANDSHAKE_TRAFFIC_SECRET
,SERVER_HANDSHAKE_TRAFFIC_SECRET
,CLIENT_TRAFFIC_SECRET_0
, andSERVER_TRAFFIC_SECRET_0
) - see the official documentation of the NSS Key Log Format. - I was starting the capture too late (since I was waiting for the secrets to be written out to the file in order to be able to provide them to Wireshark), and so Wireshark was missing the initial TLS handshake, which resulted in the misidentification of the protocol as TLS 1.2 instead of 1.3 as well as the inability to decrypt the traffic. The correct way to proceed is to start the capture before the TLS negotiation begins, and then subsequently provide Wireshark with the secrets file after it is created. (This may require saving and reloading the capture.)
Wireshark now successfuly decrypts the TLS data; it can be viewed by selecting "Encrypted Application Data" and then clicking on the "Decrypted TLS" tab at the bottom of the window.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论