ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

国密HTTPS抓包解密实战:从双证书握手到明文还原

国密HTTPS抓包解密实战:从双证书握手到明文还原 做国密HTTPS对接的兄弟十有八九都经历过这种场景Wireshark已经开起来tcp.port443的过滤条件也填好了包一条没漏但点开TLS记录全是Encrypted Application DataClient Hello里报了一堆密码套件Server Hello选出来的那个套件在Wireshark里直接显示成unknown后面的Client Key Exchange、Finished看起来全是乱码更别提应用层数据了一帧明文都看不到。这不是你不会抓包而是国密https握手协议在很多通用工具里根本不在默认支持列表里。这篇文章就围绕“国密HTTPS握手流程抓包解密”这条主线把国密SSL从密码套件协商、双证书交换、预主密钥传递到会话密钥派生的每一步拆开讲再给你一套实际能跑通的抓包解密方案以及我在排查国密链路时踩过的那些坑。1. 国密HTTPS为什么抓了包也看不明白1.1 从“抓黑盒”到“抓结构化数据”普通HTTPS抓包大家已经很熟。你设置好SSLKEYLOGFILE浏览器开了代理Wireshark或者Fiddler把TLS握手解析出来证书、密钥交换、加密扩展一条条列得清清楚楚应用层HTTP请求甚至能直接看到明文。这套流程之所以顺畅是因为主流的TLS协议栈、Wireshark、Fiddler、Charles这些工具对国际算法的密码套件认识得足够多解析器写得足够完整。国密HTTPS就不一样了。还记得我第一次拿Wireshark抓国密浏览器的流量过滤器里输入tcp.port443数据包倒是全抓下来了。可点开第一条Client HelloWireshark把密码套件那一段解析成了一串十六进制的Cipher Suites套件名称全是unknown后面跟着的扩展字段也支离破碎。当时我就明白了这个抓包问题不是一个简单配置能解决的而是工具本身不认识国密算法。国密TLS走的是SM2、SM3、SM4这套算法栈。SM2负责公钥加密和数字签名SM3负责摘要计算SM4负责对称加密。这三样东西在国际标准的TLS协议栈里基本不出现Wireshark虽然做了不少TLS拓展支持但真正对国密套件做完整解析和解密的版本直到近几年才开始逐步跟上。你抓到的包是真实的、完整的但解析器不认识这些算法自然就没法把密文还原成明文。1.2 国密算法栈三件套与抓包解析的对应关系要搞明白抓包里看到的东西先把算法栈和抓包字段对应起来SM2非对称算法对应TLS握手阶段的签名和密钥交换。你在Wireshark里应该能看到证书里的SM2公钥、签名算法标识以及Client Key Exchange里那个经过SM2加密的预主密钥。SM2的密钥长度是256位证书算法OID通常是1.2.156.10197.1.301这种国产OID。SM3摘要算法对应Finished消息、证书签名以及密钥派生过程中的PRF/HMAC。国密TLS里用SM3替代了SHA-256。SM4对称分组密码对应握手完成后的应用数据加密。SM4是128位分组、128位密钥在TLS里通常以SM4-CBC或者SM4-GCM模式出现。你抓包看到的Encrypted Application Data如果链路协商的是国密套件且能正确导出密钥这部分数据是可以被还原成明文的。理解了这三者的角色再看抓包就不慌了。你看到握手阶段的密文本质上是SM2加密的预主密钥你看到Finished消息本质上是SM3参与计算的完整性校验值你看到的大量应用数据密文本质上是SM4加密的HTTP/2或HTTP/1.1内容。整个抓包解密的难点核心就落在怎么把SM4的会话密钥拿回来以及让解析工具能识别这些国密算法。2. 双证书握手的完整流程拆解2.1 协商阶段ClientHello与ServerHello国密HTTPS的握手流程整体骨架和标准TLS 1.2类似但有几个关键差异。第一个差异在ClientHello阶段。客户端发送的密码套件列表里会包含国密套件常见的有TLS_ECC_SM4_CBC_SM3、TLS_ECC_SM4_GCM_SM3、TLS_ECDHE_SM4_CBC_SM3、TLS_ECDHE_SM4_GCM_SM3。同时扩展字段里会带上对国密曲线的支持有的实现会在Supported Groups里携带SM2曲线的标识。我看过不少抓包记录国密浏览器的ClientHello里还会带一个特殊的扩展用来声明客户端支持国密TLS的版本或标识字段。不同厂商的浏览器在这块实现有差异有的叫GMTLS扩展有的直接复用ALPN。排查问题的时候如果ServerHello没有选定国密套件优先回去看ClientHello的套件列表和扩展字段确认客户端到底有没有把国密能力发出来。服务端收到ClientHello后会从客户端支持的套件列表里选一个国密套件也可能是国际套件取决于服务端配置的优先策略在ServerHello里返回。从这个时刻开始后续的握手消息就用SM2、SM3这套体系来完成了。2.2 身份阶段签名证书与加密证书国密TLS最典型的特征是“双证书”机制。服务端需要准备两张证书一张SM2签名证书一张SM2加密证书。签名证书用于对握手过程中的关键参数进行签名加密证书用于加密客户端过来的预主密钥。在抓包里看这个过程是很有趣的。服务器在Certificate消息里会下发签名证书这是主证书。加密证书的下发方式在不同实现里略有差异有的通过额外的Certificate消息下发有的通过证书扩展字段携带还有的厂商实现是把加密证书链直接拼接在签名证书链后面。我踩过一个坑用通用TLS解析器看Certificate消息只看到一张证书以为加密证书没下发后来换了支持国密的解析器才看到第二张证书才发现其实是通用的解析器漏解析了扩展字段。如果服务端配置了双向认证mTLS在ServerHelloDone之后服务端还会发送CertificateRequest消息同样会带上请求的是SM2签名证书还是加密证书的信息。这个时候客户端需要把自己的SM2签名证书发过去并在后面的CertificateVerify消息里用客户端私钥对握手摘要做SM2签名。2.3 密钥交换与派生的关键细节双证书机制的精华在ClientKeyExchange这一步。标准TLS里最常见的是RSA密钥交换客户端生成一个48字节的pre-master secret用服务端RSA公钥加密后发过去。国密TLS走的是另一个逻辑客户端生成预主密钥后用服务端加密证书里的SM2公钥进行加密然后把密文通过ClientKeyExchange消息发送给服务端。服务端用自己的SM2加密私钥解密得到相同的pre-master secret。两边拿到pre-master secret之后结合ClientHello.random和ServerHello.random用SM3作为哈希函数去派生master secret再派生出一堆会话密钥。这个过程在Wireshark的TLS解析里能看到一个单独的Key Exchange派生过程如果你导入了密钥日志Wireshark会替你算好直接在Application Data记录上标出解密后的明文。我后来做过一次实现级别的排查发现一个很有意思的细节有些国密协议栈在密钥派生时PRF函数用的是基于SM3的定制版本而标准TLS 1.2用的是基于HMAC-SHA256的PRF。这意味着即使你拿回了keylog通用解析器也不一定按国密派生逻辑去重放密钥计划。这时候就必须依赖一款真正支持国密TLS密钥派生的工具或者用GmSSL命令行配合抓包来做交叉验证。3. 抓包方案选型怎么把SM4流量变成明文3.1 三条路线的取舍做国密HTTPS抓包没有一招通吃的方案。我实际用下来可行的路线主要有三条各有各的适用场景。第一条是SSLKEYLOGFILE配合Wireshark解密。这是标准TLS调试的常用套路对国密TLS同样适用前提是抓包工具和浏览器都支持国密套件的密钥导出。这条路线的好处是无侵入不影响协议栈行为缺点是部分国产浏览器为了安全考虑移除了SSLKEYLOGFILE这个端口你可能拿不到密钥日志。第二条是调试代理解密典型代表是Fiddler和Charles。但这里有个大坑Fiddler内部的TLS栈是基于Windows SChannel的默认不支持国密套件。也就是说你把国密浏览器的代理指向FiddlerFiddler自己去和服务端做国密TLS握手就已经失败了两边根本协商不出国密套件。除非你额外装了支持国密的代理插件否则这条路基本走不通。它在抓普通HTTPS时很神在国密链路里就没什么用武之地。第三条是旁路镜像加服务端私钥解密。在网络设备上做端口镜像把443端口的流量完整镜像到抓包机然后导入服务端的SM2加密私钥让解析工具尝试用私钥解出pre-master secret再完成后续解密。这个方案对生产环境最友好不需要在终端上做任何操作但对工具支持度的要求也最高能正确执行SM2私钥解密的解析工具并不多。3.2 密钥日志导出的多种姿势既然keylog在抓包解密里这么关键那实际操作中怎么拿到国密链路的密钥日志我整理过几种可行的方式。Chromium内核的浏览器可以尝试启动参数加--ssl-key-log-file/path/to/key.log。很多国密浏览器本身就是Chromium内核改造的如果厂家没有把这个特性裁剪掉这个参数能直接把TLS密钥交出去。有的浏览器还支持环境变量SSLKEYLOGFILE效果一样。如果浏览器不支持就转移阵地到服务端。服务端如果用的是Tongsuo、BabaSSL或者GmSSL这些支持国密算法的OpenSSL兼容库一般都能通过配置环境变量导出keylog。比如在启动服务进程前设置SSLKEYLOGFILE只要底层调用了sslkeylog这个回调钩子密钥日志就会落盘。我在排查一个Java网关时还发现哪怕Java应用用的是JSSE栈只要加载了支持国密的JCE Provider依然有办法把主密钥导出关键在于Provider是否实现了对应的hook。实在拿不到keylog还有一条终极大法用GmSSL命令行手动发起一次握手把密钥日志和抓包过程一并完成。比如在测试服务器上直接执行配合tcpdump抓包你既能拿到完整的密钥计划又能看到实际的网络报文。这在没有国密浏览器、没有代理支持的纯命令行环境里是验证国密链路最有效的方式。3.3 没有keylog时怎么判断加密链路有时候你确实拿不到keylog或者抓包工具不支持国密解密那你至少还能做链路状态判断。判断的思路有两个层面。第一层是看密码套件协商结果。打开ServerHello消息找到选定的密码套件。如果显示的是SM4相关套件比如TLS_ECDHE_SM4_GCM_SM3或者TLS_ECC_SM4_CBC_SM3说明链路走的是国密加密。这一层判断完全不依赖解密能力Wireshark新版基本都能识别套件名称。第二层是看证书算法。在Certificate消息里展开证书字段查看SubjectPublicKeyInfo的算法OID。如果是1.2.156.10197.1.301说明是SM2公钥。签名算法里的摘要算法如果是SM3也说明这条链路是国密体系。把套件、证书、签名算法这三样兑在一起即使看不到明文你也能很肯定地告诉对方链路确实走国密了加密强度符合预期。4. 实操Wireshark解密国密HTTPS流量4.1 环境准备与过滤器这一节我把操作步骤完整走一遍读者可以直接照着做。先准备环境。抓包机装好Wireshark尽量用4.0以上版本新版本对国密套件名称的识别和解密支持比老版本好很多。需要解密的那一端按第3.2节的方法准备一个SSLKEYLOGFILE无论是浏览器导出的还是服务端导出的先确认文件确实在增长。如果文件一直是空的说明程序没走这个钩子解密无从谈起。然后配置Wireshark。打开Edit - Preferences - Protocols - TLS在“(Pre)-Master-Secret log filename”一栏填入密钥日志路径。如果你的加密流量跑在443以外的端口还要在TLS协议设置里把对应端口加进去。如果Wireshark没识别出TLS协议右键报文选择Decode As手动指定为TLS。抓包过滤器可以使用tcp.port443配合具体IP或者更精准一点host 目标IP and tcp.port443。建议不要只用ssl.handshake.type这类显示过滤器来做捕获过滤那样可能漏掉握手之外的TCP重传和分片排查起来反而被动。4.2 定位握手关键帧抓完包、导好keylog之后看握手的四个关键节点。第一个节点是ClientHello。在显示过滤器里输入tls.handshake.type1点开第一个TLS记录看Cipher Suites列表里有没有国密套件Supported Groups里有没有SM2曲线。这里能看到客户端的能力集如果国密套件完全没出现在列表里说明客户端侧要么没启用国密要么连的是国际TLS入口。第二个节点是ServerHello。过滤器输入tls.handshake.type2看服务端选出的套件。如果这里选回了国际套件说明服务端策略可能优先国际算法国密链路并未建立。如果选了中国密套件继续往下看密钥交换。第三个节点是Certificate。过滤器输入tls.handshake.type11展开看证书列表。重点看是否同时存在两张SM2证书签名证书和加密证书都要在。我遇到过只发了一张证书的服务端配置结果客户端握手直接报错这种问题在抓包里一眼就能看出来。第四个节点是Finished之前的ClientKeyExchange和ChangeCipherSpec。这里能观察预主密钥交换是否完成。如果启用了keylogWireshark会在Finished之后的Application Data记录上自动解密你会看到解密后的HTTP/2 HEADERS帧或者HTTP/1.1的请求行。4.3 验证解密结果解密成功的标志是在Wireshark的包列表里看到Application Data下面出现“Decrypted TLS”字样点开之后能看到HTTP协议层。我看到过不少人在这一步卡住明明keylog文件有内容解密还是失败原因集中在三个地方。第一keylog文件里的CLIENT_RANDOM和实际握手的ClientHello.random对不上。多半是因为同时开了多个浏览器窗口、多个进程抓的包里混了其它连接的握手。解决方法是把抓包过滤条件收窄只保留目标IP和端口。第二密钥派生算法不匹配。国密TLS的PRF和标准TLS有差异部分Wireshark版本在解密SM4套件时没有完全按国密派生逻辑计算导致解密失败。这种情况我建议换一个支持GM/T 0024派生逻辑的工具或者在新版Wireshark上重新解析一遍老版本是确实有这类bug的。第三套件是SM4-GCM而Wireshark按CBC逻辑去爆破导出的nonce和tag对不上。这个属于解析器兼容问题没有太多好办法优先升级Wireshark版本或者把抓包文件分享给用TG/Tongsuo相关插件的人交叉验证。5. 握手阶段常见故障的定位思路5.1 套件协商失败国密HTTPS对接中出现频率最高的一个报错是“no shared cipher”或者客户端直接终止握手。抓包看一下ServerHello或者Alert消息基本能定位是套件协商失败。常见原因是服务端配置国密套件时优先级排得太靠后。客户端也支持国密和国际算法服务端默认选了国际套件链路就变成普通HTTPS了。想走国密两端要么都只放国密套件要么服务端把国密套件优先级提到最前。另一个原因是客户端没有启用国密能力比如浏览器装的是普通版而不是支持国密的版本或者代码里的SSLContext压根没加载SM2的Provider。5.2 证书链不受信任国密链路抓包里发现Certificate消息之后马上跟了一个Alert类型是unknown_ca或者bad_certificate十有八九是证书链问题。国密体系用的SM2根证书是需要单独导入到系统的信任库或者浏览器证书管理器的。我用红莲花浏览器实测系统信任库里有国际根证书但国密根证书单独放在另一个目录里如果不手动导入客户端验证签名证书链时就会失败。抓包的时候如果看到客户端在收到证书后直接断开、没有任何密钥交换消息优先检查目标机器的证书信任列表。还有朋友遇到过一个比较隐蔽的情况服务端下发了完整的证书链但是签发给加密证书的CA和签名证书不是同一个CA。这种情况下有些严格的客户端会校验两条证书链的交叉信任关系校验不通过也会中止握手。抓包看到两张证书在两条证书链里各自成立但客户端仍然报错基本就是这个原因。5.3 双向认证阶段失败国密双向认证的排错比单向多一个步骤。抓包时关注CertificateRequest消息和客户端的Certificate、CertificateVerify两条上行消息。如果CertificateRequest发出来了但服务端迟迟没收到客户端的证书多半是客户端自己没装当地CA签发的SM2证书。如果证书发上去了却在CertificateVerify阶段被拒通常是签名验证失败。签名验证失败的背后要么是证书的SM2公钥和私钥不匹配要么是签名时使用的摘要算法和服务端验证时使用的算法不一致。抓包看不出私钥匹配问题但可以在Wireshark里看到CertificateVerify的签名长度、签名算法标识如果算法标识是SM3-SM2而服务端期望的是别的组合就能从这里切入。在数据库等业务系统做国密改造时这条同样适用。像OceanBase这类数据库开启国密SSL双向认证后客户端JDBC需要同时配置签名证书和加密证书。如果只配置了加密证书没配签名证书抓包就能看到客户端始终没有传出CertificateVerify服务端持续等待然后超时。这个在测试环境里我用抓包定位过多次效率和盲查配置完全不是一个量级。5.4 数据库场景的国密链路验证最后补充一个我在数据库国密测试里的抓包方法。链接数据库时SSL/TLS握手发生在业务端口上比如OceanBase的2883端口。同样用Wireshark抓包过滤条件改成tcp.port2883配置好keylog之后能看到客户端的ClientHello里是否携带SM4-GCM相关套件。如果数据库服务端开启了国密ServerHello会返回国密套件之后的握手消息和前面讲的流程一致。做SM2数据库国密测试时我建议用GmSSL的s_client去连数据库端口验证一次完整的国密握手。gmssl s_client -connect 10.0.0.5:2883 -gmtls -state -msg这个命令会直接打印握手消息如果数据库侧的国密协议实现有偏差在输出里能看到具体是哪一步失败。拿这个输出和Wireshark抓包互相印证定位问题就快得多。写在最后的一点体会国密HTTPS抓包这件事说到底就是两句话先把国密握手流程吃透再找到能导出密钥的入口。流程吃透了你在Wireshark里看到未知套件就不会慌知道下一步该看哪条消息、哪个字段思路始终是清晰的密钥入口找到了SM4密文就能在工具里还原成明文问题定位就从猜变成了看。如果你在某个环节卡住我个人的建议是先放弃一步到位的幻想用GmSSL命令行和服务端做一次最小化握手把流程跑通再回头去对照浏览器或业务客户端的抓包文件。很多国密实现看似复杂剥掉双证书和SM2加密密钥交换这些外壳和标准TLS的调试思路是同一个套路。希望这篇实测记录能帮你少走几步弯路。
返回列表