如何让QUICHE生成Google QUIC密钥日志以解密Wireshark数据包
问题背景
使用bilibili维护的QUICHE仓库,通过toy_client.cc和toy_server.cc模拟QUIC连接,修改quiche/gquiche/quic/core/crypto/tls_server_connection.cc的KeylogCallback函数后,仅IETF QUIC版本(draft29、00000001)能生成密钥日志,Google QUIC早期版本(Q043/Q046/Q050)无日志输出,无法用Wireshark解密数据包,环境为Ubuntu 18.04且无法升级依赖。
原因分析
Google QUIC早期版本(Q043等)采用自定义加密握手栈,而非IETF QUIC基于标准TLS 1.3的实现。你修改的TlsServerConnection::KeylogCallback仅针对IETF QUIC的TLS逻辑触发,Google QUIC的密钥生成与日志逻辑完全封装在自身的加密流类中,不会调用该回调。
解决方案
针对Google QUIC的加密流实现添加密钥日志逻辑,步骤如下:
1. 修改服务端Google QUIC加密流代码
找到quiche/gquiche/quic/core/crypto/quic_crypto_server_stream.cc,这是Google QUIC服务端握手的核心实现文件,添加密钥日志写入逻辑:
第一步:添加密钥日志辅助函数
在文件头部引入依赖后,添加标准SSLKEYLOG格式的写入函数:
#include <fstream> #include <string> void WriteQuicKeyLog(const std::string& label, const std::string& client_random, const std::string& secret) { const char* filePath = "sslkeylogfile.log"; std::ofstream keylogFile(filePath, std::ios::app); if (keylogFile.is_open()) { // 严格遵循SSLKEYLOG格式:<标签> <客户端随机数十六进制> <密钥十六进制> keylogFile << label << " " << client_random << " " << secret << std::endl; keylogFile.close(); } else { std::cerr << "Failed to open keylog file: " << filePath << std::endl; } }
第二步:在握手完成后写入密钥
找到QuicCryptoServerStream::OnHandshakeMessage方法,在握手完成的分支中添加密钥日志代码:
void QuicCryptoServerStream::OnHandshakeMessage(const CryptoHandshakeMessage& message) { // 保留原有逻辑... if (handshake_state_ == STATE_HANDSHAKE_COMPLETE) { const QuicCryptoNegotiatedParameters& params = crypto_negotiated_params(); // 提取客户端随机数(十六进制格式) std::string client_random = params.client_random.ToHexString(); // 提取主密钥(不同Google QUIC版本可能字段名略有差异,如forward_secure_master_secret) std::string master_secret = params.master_secret.ToHexString(); // 写入CLIENT_RANDOM标签的密钥(Wireshark解密必需) WriteQuicKeyLog("CLIENT_RANDOM", client_random, master_secret); // 可选:写入EXPORTER_SECRET用于扩展解密场景 std::string exporter_secret = params.exporter_secret.ToHexString(); WriteQuicKeyLog("EXPORTER_SECRET", client_random, exporter_secret); } // 保留原有逻辑... }
2. 客户端端补充(可选)
若需要客户端侧的密钥日志,同理修改quiche/gquiche/quic/core/crypto/quic_crypto_client_stream.cc,在握手完成后调用相同的WriteQuicKeyLog函数,确保双向密钥都被记录。
3. 重新编译QUICHE
执行编译命令重新构建工具:
bazel build //quiche/quic/tools:simple_quic_server //quiche/quic/tools:simple_quic_client
4. 验证效果
启动服务端和指定Q043版本的客户端后,检查sslkeylogfile.log是否生成对应版本的密钥条目,在Wireshark中配置该日志文件即可解密Google QUIC数据包。
注意事项
- 不同Google QUIC版本的密钥字段可能有差异,若
master_secret不存在,可尝试forward_secure_master_secret等字段; - 必须严格遵循SSLKEYLOG格式,否则Wireshark无法识别密钥;
- 日志文件路径可根据需求调整,建议使用绝对路径避免权限问题。
内容的提问来源于stack exchange,提问作者Soumyajit

