ARTICLE DETAIL

资讯详情

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

国密SSL抓包实战:Wireshark双证书导出与证书链验证

国密SSL抓包实战:Wireshark双证书导出与证书链验证 最近调一个国密SSL的联调问题页面卡在握手阶段过不去我习惯性打开Wireshark抓包分析结果迎面遇到两件让人愣住的事Cipher Suite那一栏显示Unknown未知套件而Certificate消息里竟然躺着两张证书。标准TLS看多了这套组合拳确实容易让人懵。后来硬是靠着逐字节翻报文、对着标准文档来回比对才把整套GMTLS握手和国密证书链验证流程彻底跑通。这篇文章就把这套经验完整写出来从抓包前的认知准备、Wireshark里的过滤和报文定位到双证书导出与证书链验证再到实际排查中踩过的坑一次性讲清楚。适合正在做国密改造、安全测评或者只是想把国密SSL抓包这件事弄明白的工程师。1. 抓包前先建立坐标系GMTLS与普通TLS的差异点在哪1.1 GMTLS的本质TLS 1.2框架里的中国商用密码组合先说结论国密SSL不是什么从零设计的全新协议它本质上是沿用TLS 1.2的握手框架把里面的公钥算法、哈希算法、对称加密算法整体换成国密算法家族。目录编号可以参考GM/T 0024系列以及GB/T 38636-2020《信息安全技术 传输层密码协议》整套协议在密码学界经常被称作GMTLS。对应关系很简单国密算法作用对标国际算法SM2公钥加密、数字签名、密钥交换ECDSA/ECDH、RSASM3消息摘要、签名散列SHA-256SM4分组对称加密AES-128在TLS握手层面国密SSL主要用TLS_ECC_SM4_CBC_SM3这类套件常见CipherSuite值是0xE013部分实现里还能看到0xE014。0xE013这个段落在IANA的标准分配表里长期没有正式登记所以Wireshark拿到手后不认识显示Unknown是常态不代表报文有问题。理解这一点很重要抓包分析时整个TLS握手消息结构、状态机、记录层格式都和标准TLS 1.2一致Wireshark能正常解析大部分握手字段。真正让工具有可能“翻白眼”的是算法相关的字段比如套件名、签名算法、证书里的公钥结构。所以分析时要有两手准备——视图能解析就看视图视图解析不了就直接看十六进制。1.2 双证书模型为什么一个Certificate消息里会有两张证普通TLS里服务器下发的Certificate消息里通常是一条证书链服务器证书、中间CA证书根证书一般不主动下发。但国密SSL不一样它采用的是“双证书”模型服务器同时持有两张功能不同的证书签名证书用于对ServerKeyExchange等握手消息做数字签名完成身份认证。加密证书用于SM2密钥协商保护后续会话密钥的生成。这两张证书在TLS握手时一起放在同一个Certificate消息里通过certificate_list下发。于是从Wireshark里看就是同一个Handshake消息里连续出现两个Certificate条目。很多初接触的人会把“双证书”和“证书链”搞混其实这是两个维度的事情证书链体现的是“信任层级”双证书体现的是“同一层级、不同用途”。抓包时看到的两个证书可能是同一条信任链上的两张叶子证书比如由同一个CA签发而不是“服务器证书CA证书”的关系。为什么要知道这个因为后面导出证书时你必须知道自己导出的是“签名证”还是“加密证”否则验证时会把两张证书当作链上的上下级来处理越验越迷糊。1.3 先准备一个可复现的测试目标如果手头没有现成的国密SSL站点建议本地搭一个最小可复现环境。我常用的方式是借助GmSSL它原生支持SM2/SM3/SM4集成的SSL栈比标准OpenSSL方便。先准备两套SM2密钥和证书。签名证书、加密证书各一份生成命令大致是gmssl ecparam -genkey -name sm2p256v1 -out sign.key gmssl ecparam -genkey -name sm2p256v1 -out enc.key # 生成签名证书和加密证书需自建CA或用GmSSL提供的样例CA gmssl req -new -key sign.key -subj /CNtest-sign -out sign.csr gmssl req -new -key enc.key -subj /CNtest-enc -out enc.csr证书签发好后用GmSSL自带的服务端直接起一个测试服务gmssl s_server -accept 443 -cert sign.crt -key sign.key -dcert enc.crt -dkey enc.key -www这里-dcert和-dkey就是用来指定第二张加密证书的参数。服务起来后用浏览器或gmssl s_client访问接下来就可以用Wireshark抓本机回环或者网卡流量了。本地测试环境的好处是能把“抓包—定位—导出—验证”整个闭环走通不用依赖外部站点适合反复练习。2. 让Wireshark“开口说话”抓取完整GMTLS握手的准备与过滤条件2.1 选择正确的抓包位置与接口抓包位置决定了你能看到什么。国密SSL有一个隐蔽的坑很多单位在生产环境部署的是国密安全网关网关靠近服务器的一侧可能已经转换成标准TLS才会和后端应用通信。如果你把Wireshark挂在服务器侧网卡上抓到的可能根本不是国密流量。一定要在真正承载国密SSL连接的客户端侧抓包也就是国密浏览器或测试客户端所在的主机上抓。接口选择上Windows下用WLAN或以太网如果浏览器和服务器跑在同一台机器上就直接选Loopback回环接口。抓包前确认npcap驱动已经正确安装别和旧版WinPcap混用不然莫名其妙出现抓不到包、蓝屏一类的问题时先怀疑驱动层。抓包时长方面国密握手本身只有几个报文几十毫秒就结束了。我习惯先设置一个捕获过滤器只抓和目标IP相关的443端口流量避免把无关广播、DNS、后台流量全部塞进来host 192.0.2.10 and tcp port 443如果目标站点用了非443端口把端口号换成实际值即可。这种捕获过滤器在驱动层就把不相关的包丢掉了比事后用显示过滤器筛更省内存也避免大pcap文件卡顿。2.2 显示过滤器精准捞出握手包抓包完成后定位握手包最快的方式是直接用显示过滤器。TLS1.2的握手消息类型里ClientHello对应1Certificate对应11ServerHello对应2。所以这样过滤tls.handshake.type 1 tls.handshake.type 11老版本Wireshark里字段名可能是ssl.handshake.type如果你用的版本比较陈旧把tls换成ssl就能用。想快速看完整握手顺序可以直接在某个ClientHello上右键选择“Follow TLS Stream”然后在弹出的流内容里按横条查看每个握手记录的序号。完整的国密握手记录顺序大概是ClientHelloServerHelloCertificate同时带两张证书ServerKeyExchangeServerHelloDoneClientKeyExchangeChangeCipherSpecFinished看到1、2、3基本就能确认抓到了核心部分。不用被Wireshark把TLS记录拆成多个TCP分片吓到在Packet Details里看到“TLS segment of a reassembled PDU”这类提示时说明Wireshark已经帮你重组了应用层记录。2.3 会话复用为什么抓不到Certificate很多人在国密站点上抓包发现只有ClientHello和ServerHello后面直接跳到ChangeCipherSpec怎么都找不到Certificate消息。这种情况八成是TLS会话复用Session Resumption。TLS握手在第一次连接完成后客户端和服务器会协商出一个会话票据或会话ID后续短连接可以直接复用之前的会话参数跳过完整握手。这时抓包看到的握手消息被大幅精简自然没有Certificate和ServerKeyExchange。对策很简单使用浏览器的无痕/隐私模式它们一般不复用旧会话。抓包前先关闭相关标签页再开新标签页访问。如果还是不行可以在Wireshark里定位到ClientHello后右键发送RST复位连接有些工具支持或者等服务端会话超时。也可以在ClientHello消息里查看扩展项如果有session_ticket扩展且内容是空的说明这是一个全新的会话握手能抓到完整流程。会话复用问题在国密SSL联调里出现频率极高因为很多应用长连接复用同一个TLS会话导致你抓了几百个包也找不到Certificate消息。记住这个排查顺序能省下大量时间。3. 逐包拆解ClientHello与ServerHello识别“国密身份”的四处特征3.1 Cipher Suite值看到0xE013就别慌定位到ClientHello后展开协议树里的Secure Sockets Layer依次看TLSv1.2 Record Layer、Handshake Protocol: Client Hello往下找Cipher Suites字段。Wireshark会把客户端支持的所有套件列成一个列表如果目标启用国密列表里大概率会看到Cipher Suite: Unknown (0xe013)这个0xE013就是TLS_ECC_SM4_CBC_SM3也就是SM2做密钥协商、SM4做对称加密、SM3做PRF哈希的国密套件。部分新一点的洞洞里还会出现0xE014。看到“Unknown”时先稳住这不是Wireshark识别失败而是它没有这个套件的注册信息。你可以直接在Packet Bytes视图里搜索十六进制字节e0 13确认套件值确实在报文里。ServerHello里服务器最终选中的套件如果也是0xE013那么基本可以断定这条连接走的就是国密SSL。3.2 扩展字段里的“身份标记”SM2曲线与签名算法除套件之外国密SSL的ClientHello扩展字段里还有几个固定“身份标记”。重点关注supported_groups和signature_algorithms这两个扩展supported_groups中如果出现了sm2p256v1曲线其OID是1.2.156.10197.1.301而标准的NIST P-256曲线OID是1.2.840.10045.3.1.7两者完全不一样。signature_algorithms里如果包含SM2签名算法或相关哈希组合也可以佐证这是国密握手。不同网关实现可能扩展字段略有差异但“国密套件双证书”这两个特征同时出现时基本不会误判。3.3 只靠视图不够用时用十六进制补漏万一碰上比较老的Wireshark版本对TLS握手的解析比较粗糙很多字段没有被单独列出来这时候就得靠十六进制视图手动定位。我自己常用的锚点是这样的在Packet Details里点击Handshake Protocol报文头下方Packet Bytes面板会高亮对应字节。TLS握手消息的结构是第一个字节是Handshake Type后面3个字节是长度再往后是消息体。Certificate消息的类型值是0x0B所以看到0b 00 xx xx开头的握手消息就是Certificate。证书本身是DER编码的ASN.1结构所有X.509证书都以字节30 82开头看到这个特征就说明从那里开始是一张完整的证书。摘自实际抓包的一个ServerHello在十六进制里搜索e0 13能找到类似... 01 00 00 c0 e0 13 00 00 ...这段就表示ServerHello选择的CipherSuite是0xE013。用这种原始字节确认的方式比依赖工具解析要可靠得多关键是工具版本和解析器把你带偏时还能用自己的眼睛兜底。4. 双证书的分离与导出把Wireshark里的X.509证书落成文件4.1 用GUI复制证书字段的十六进制流抓包分析到这一步你已经看到Certificate消息里有两张证书了。接下来要做的是把它们从pcap里“抠”出来变成可独立验证的.der或.pem文件。最简单的GUI操作在Packet Details里展开Certificate消息找到形如Certificate: 3082...的字段右键该字段选择“复制”再选“作为十六进制流”。Wireshark会把当前字段的原始值以十六进制字符串形式复制到剪贴板这个值就是从30 82开始的DER编码证书。把它存成文本文件后在Linux或Windows的WSL里执行xxd -r -p leaf.hex leaf.der然后马上用openssl看一眼是不是有效的DER证书openssl x509 -inform DER -in leaf.der -text -noout如果能看到证书的Subject、Issuer、Public Key等字段说明导出成功了。注意复制时一定要点Certificate字段本身而不是外层的certificate_list或者整个Handshake消息否则会把长度前缀一起复制进来导致openssl解析失败。DER开头必须是30 82如果不是基本就是选错字段了。4.2 用tshark批量提取证书链多个证书的推荐姿势如果Certificate消息里有两张证书手工一条条复制也不是不行但效率太低。我一般直接用tshark从pcap里批量提取一条命令就把两张证书全部导出来tshark -r handshake.pcap -Y tls.handshake.type 11 -T fields -e tls.handshake.certificate certs.hex这里-e tls.handshake.certificate会输出Certificate消息里每个证书的DER数据格式是十六进制字符串每个证书占一行。接下来把每行转成独立的.der文件n0 while IFS read -r line; do n$((n1)) echo $line | xxd -r -p cert_$n.der done certs.hex执行完目录下会有cert_1.der、cert_2.der按服务器下发顺序对应两张证书。如果tshark输出为空先排查显示过滤器字段名问题可以运行tshark -G fields | grep -i certificate看看你手里这个版本里到底有哪些可用字段。Wireshark升级频繁字段名偶尔会调整不要死记硬背学会自己查字段列表才是真技能。4.3 确认导出结果从字节特征到证书内容导出后建议做一遍快速检查避免后面验证时被脏数据浪费半天file cert_1.der openssl x509 -inform DER -in cert_1.der -noout -subject -issuerOpenSSL能正常输出subject和issuer说明这个文件内容完整。再检查一下文件大小一张SM2证书大概在700到1600字节之间如果某个文件特别大比如几十KB那很可能把整个TLS记录或者多个证书连体导出来了。到这一步你已经拥有两张真实的国密证书接下来才进入整篇文章的核心价值区证书链验证。5. 证书链验证从根到叶查SM2签名与扩展项5.1 先把DER转成PEM并检查关键字段证书验证的第一步是确认这张证书到底是什么证书。先把DER转成PEM格式方便后续所有命令统一处理openssl x509 -inform DER -in cert_1.der -out cert_1.pem openssl x509 -in cert_1.pem -text -noout输出内容里有几个关键位置要特别看Signature Algorithm这一栏正常国密证书会显示SM2-with-SM3对应OID是1.2.156.10197.1.501。Public Key Algorithm这一栏如果是SM2公钥通常能看到id-ecPublicKey并附带sm2p256v1曲线参数曲线OID是1.2.156.10197.1.301。Subject和Issuer字段用来建立证书链关系。X509v3 extensions里的Key Usage和Basic Constraints用来判断这张证书的用途和CA属性。如果openssl版本较老或者编译时没带SM2支持解析国密证书可能直接报unsupported signature algorithm。这时候别怀疑证书坏了换GmSSL命令行工具再试gmssl x509 -in cert_1.pem -text -nooutGmSSL对国密算法的支持是最完整的实测下来验证国密证书链的体验比标准OpenSSL顺畅很多。5.2 手工确认信任路径并构建verify命令证书链验证的本质是沿着“叶子证书 → 中间CA证书 → 根证书”这条路径逐级验证签名关系。手工确认时最容易用的判断就是检查每张证书的Subject和Issuer字段能否首尾相接服务器证书的Issuer必须等于中间CA证书的Subject。中间CA证书的Issuer必须等于根证书的Subject。根证书的Subject和Issuer完全一致因为它是自签的信任锚。确认路径没有问题后再执行实际的链验证命令openssl verify -CAfile root.pem -untrusted intermediate.pem leaf.pem注意几个细节-CAfile放的是根证书也就是你的信任锚。-untrusted放的是中间CA证书和叶子证书之外的所有辅助证书通常就是中间CA。叶子证书直接作为最后一个参数传进去。不要把根证书塞进-untrusted否则openssl会报self-signed certificate in certificate chain让你白折腾很久。国密环境下建议用GmSSL执行同款命令gmssl verify -CAfile root.pem -untrusted intermediate.pem leaf.pem看到输出leaf.pem: OK说明这条链走到根都是可信的。这里有个容易踩的坑国密SSL的Certificate消息里下发的是“签名证书加密证书”两张同级证书而不是“服务器证书中间CA”。如果你一上来就把第一张证书当叶子、第二张当中间CAverify命令大概率会报错因为它们的Subject、Issuer根本不是上下级关系。判断方法很简单看两张证书的Issuer如果相同它们就是同一条信任链上的兄弟节点不是父子节点。5.3 国密证书特有的扩展项检查除了OpenSSL/GmSSL自动验证的签名链之外证书链验证还应该人工检查几个影响实际信任的扩展项。我每次测评都会按这个表过一遍检查项要求典型问题Key Usage签名证书要含digitalSignature加密证书要含keyEncipherment或keyAgreement证书用途与套件不匹配握手会在密钥协商阶段失败Basic ConstraintsCA证书必须是CA:TRUE叶子证书必须是CA:FALSE一张叶子证书被误做成CA证书可能被接受为中间CASubject Alternative Name必须包含站点域名或IP浏览器报域名不匹配Validity证书有效期包含当前时间服务器时间异常或证书过期CRL Distribution Points应有可访问的CRL地址无法正常吊销检查Authority Information Access通常包含OCSP服务地址OCSP访问不通客户端可能拒绝信任国密证书的具体格式和扩展项定义配套标准里规定得比较细。实际验证时建议把下载下来的根证书、中间CA证书和两张服务器证书一起保存到固定目录方便反复验证和审计。5.4 验证失败时的判断方向证书链验证失败时不要一头扎进去反复重跑命令先看报错类型再对症下药。常见的几类报错和对应原因我整理成一个速查表报错信息含义解决办法self-signed certificate in certificate chain根证书被当成非信任证书处理把根证书移到-CAfile从-untrusted中去掉unable to get local issuer certificate缺少中间CA或根证书抓包导出完整证书链确认中间CA也导出来了unsupported signature algorithm当前openssl/GmSSL不支持SM2签名换用GmSSL或确认openssl版本和编译选项SM2 signature verification failed证书签名验不过重新导出证书检查DER数据是否完整排除导出时截断问题certificate verify failed通用失败结合详细-verbose输出确认具体原因最后还有一招如果实在卡住直接用openssl asn1parse -inform DER -in cert_1.der查看证书的ASN.1结构逐层看签名算法OID和签名值。这能帮你确认是不是工具解析问题。6. 实操中踩过的坑按出现频率排序6.1 Wireshark把国密套件显示为unknown不等于报文有问题这是新人最容易被吓到的一步。看到Unknown CipherSuite就以为抓错包或者怀疑服务器配置有问题。实际原因是Wireshark的TLS dissector依赖一份套件注册表而0xE013这系列的国密套件没有正式注册进Wireshark的默认表里所以只能显示Unknown。判断方法是组合看其他特征ClientHello里有没有SM2曲线、ServerHello是不是同样选了0xE013、Certificate消息里是不是出现两张证书。这几点都满足就可以自信地认定这就是一条国密SSL流量无需为了“让Wireshark认识国密套件”去折腾自定义插件——虽然理论上能做但对接下来的分析没有实质帮助。6.2 不完整握手的迷惑性第二个高频坑是抓了半天只有ClientHello和ServerHello没有Certificate。大多数时候不是抓包姿势错了而是TLS会话复用。遇到这种情况先看ClientHello里有没有session ticket扩展再看ServerHello是否返回了新的会话票据。如果两者都是复用就意味着当前连接复用了一个老会话证书和密钥协商步骤都被跳过了。处理方式我前面提过开无痕窗口、关掉旧标签页重新访问或者在Wireshark里定位到ClientHello后用客户端工具断开连接强制走一次全新握手。调试时还可以在客户端代码里显式禁用会话缓存比如用golang的配置把ClientSessionCache设为nil或者用curl时加上--no-sessionid。这样能稳定复现完整握手。6.3 标准s_client拉不到国密证书很多人习惯用openssl s_client -connect host:443 -showcerts来拉证书链。这个命令在标准TLS站点上没问题但遇到国密SSL站点时经常直接报错连接还没建立就结束了。原因很简单openssl默认的s_client只携带国际标准套件客户端Hello里根本没带0xE013服务器自然直接给你一个handshake_failure证书链根本不会下发到客户端。处理办法是改用GmSSL的s_clientgmssl s_client -connect 127.0.0.1:443 -state -showcertsGmSSL原生支持国密套件能完成握手并把服务器下发的证书链打印出来。不过更通用、可追溯性的做法还是回到Wireshark抓包导出证书因为pcap文件里的数据是原始报文不依赖客户端工具对证书链的解析逻辑审计时更可靠。6.4 误把“签名证书加密证书”当成CA链最后这个坑属于国密特有的概念混淆。很多人在Certificate消息里看到两张证书第一反应是“第一张是服务器证书第二张是中间CA证书”然后直接拿第二张去验证第一张的签名结果自然失败。正确的理解是国密SSL下发的两张证书通常是同一信任层级下的两个不同功能实体一张签名、一张加密。它们的Issuer可能完全相同都是同一个CA所以它们之间没有签名与被签名的关系。验证时要分别把每张证书归到同一条根路径下而不是把两张证书串成一个链。识别方法我反复用过先分别看两张证书的Subject和Issuer。如果Subject不同但Issuer相同说明是兄弟证书走同一条CA路径如果Issuer和Subject能上下衔接才是父子证书链。另外KeyUsage也很关键签名证书往往带digitalSignature加密证书带keyEncipherment或keyAgreement用途区分一目了然。写在后面个人经验是排查国密SSL问题最稳的路径永远是抓完整握手 → 确认套件和双证书特征 → 把证书导出成文件 → 逐张验证签名链和扩展项 → 再回到握手日志看密钥协商是否成功。这套流程熟练之后大多数联调问题半小时内就能定位到具体环节无论是工具链识别、证书链不完整还是算法套件不匹配都有清晰的判断依据。最后分享一个操作细节Wireshark里双击任意字段后按CtrlC复制出来的值就是当前字段的原始值。比如双击Cipher Suite字段再CtrlC出来的是Unknown (0xe013)双击Certificate字段再CtrlC出来是DER证书完整的十六进制流。这个小技巧在写分析报告、比对报文时特别顺手希望对你也有用。
返回列表