You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何借助SSL密钥日志文件在Go中解密TLS数据包?

在Go中利用SSLKEYLOGFILE解密HTTPS数据包

核心逻辑

Wireshark的HTTPS解密依赖SSLKEYLOGFILE中记录的TLS会话密钥(格式多为CLIENT_RANDOM <客户端随机值十六进制> <主密钥十六进制>),要在Go中复现该功能,核心步骤是:解析密钥日志文件→捕获并重组TCP流中的TLS数据包→匹配客户端随机值与对应密钥→解密应用数据。

推荐工具库

标准库crypto/tls仅支持TLS握手与在线加密解密,不提供离线数据包解密能力,推荐使用专门适配的第三方库:

  • github.com/ameshkov/go-tlsck:封装了SSLKEYLOGFILE解析、TLS数据包解析及解密逻辑,与gopacket兼容性良好,支持TLS 1.2/1.3等多版本。

实现步骤

1. 解析SSLKEYLOGFILE

通过go-tlsck加载密钥日志文件,构建客户端随机值到主密钥的映射:

keyLog, err := keylog.NewKeyLog("/path/to/sslkeylogfile.txt")
if err != nil {
    log.Fatal(err)
}

也可自行解析日志文件,按照CLIENT_RANDOM条目格式提取客户端随机值(16进制转字节)和主密钥。

2. 捕获并重组TCP流中的TLS数据

使用gopacket捕获数据包后,需通过tcpassembly包重组同一TCP流的分片数据,再提取TLS载荷:

// 初始化TCP流重组器
streamFactory := &tlsStreamFactory{keyLog: keyLog}
streamPool := tcpassembly.NewStreamPool(streamFactory)
assembler := tcpassembly.NewAssembler(streamPool)

// 处理捕获的数据包
for packet := range packetSource.Packets() {
    if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
        tcp, _ := tcpLayer.(*layers.TCP)
        // 将TCP包送入重组器
        assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp)
    }
}

3. 匹配密钥并解密TLS应用数据

在TCP流重组的回调中,解析TLS记录:

  • 从Client Hello握手包中提取客户端随机值,关联到当前TCP流;
  • 对应用数据记录,用匹配到的主密钥解密:
type tlsStream struct {
    keyLog        *keylog.KeyLog
    clientRandom  []byte
    reassembled   []byte
}

func (s *tlsStream) Reassembled(rs []tcpassembly.Reassembly) {
    for _, r := range rs {
        s.reassembled = append(s.reassembled, r.Bytes...)
        // 尝试解析完整的TLS记录
        for len(s.reassembled) >= 5 { // TLS记录头至少5字节
            recordLen := int(binary.BigEndian.Uint16(s.reassembled[3:5]))
            if len(s.reassembled) < 5+recordLen {
                break // 记录不完整,等待后续分片
            }
            recordData := s.reassembled[:5+recordLen]
            s.reassembled = s.reassembled[5+recordLen:]

            record, err := tlsck.ParseTLSRecord(recordData)
            if err != nil {
                continue
            }

            // 提取Client Hello中的客户端随机值
            if record.ContentType == tlsck.ContentTypeHandshake {
                hs, err := tlsck.ParseHandshake(record.Data)
                if err != nil {
                    continue
                }
                if ch, ok := hs.(*tlsck.ClientHello); ok {
                    s.clientRandom = ch.Random
                }
            }

            // 解密应用数据
            if record.ContentType == tlsck.ContentTypeApplicationData && s.clientRandom != nil {
                ms := s.keyLog.GetMasterSecret(s.clientRandom)
                if ms == nil {
                    continue
                }
                plaintext, err := tlsck.DecryptTLSRecord(record, s.clientRandom, ms)
                if err != nil {
                    log.Printf("解密失败: %v", err)
                    continue
                }
                log.Printf("解密明文: %s", string(plaintext))
            }
        }
    }
}

注意事项

  • 必须正确重组TCP流:TLS数据会被TCP分片,直接处理单个数据包无法得到完整的TLS记录;
  • 权限要求:捕获网络数据包需要root权限,运行程序时需确保权限充足;
  • TLS版本兼容:go-tlsck已处理TLS 1.3与1.2的密钥格式差异,无需额外适配。

内容的提问来源于stack exchange,提问作者TehYoshi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 20:51:14