跳到主要内容

流量分析:Yakit MITM 国密双向认证证书处理

· 阅读需 5 分钟
Yak Project
网络安全垂直语言团队

随着国家网络安全战略的深入实施,国密算法(SM2、SM3、SM4 等)在我国关键信息基础设施和重要领域得到了广泛应用,旨在构建自主可控、安全可靠的密码体系。国密 TLS(GMTLS)作为其在传输层安全协议的体现,正逐步取代传统的 RSA/ECC TLS 协议。然而,这种安全性的提升也给传统的网络分析工具,尤其是中间人(MITM)类工具带来了新的挑战。近期我们收到了部分用户咨询有关双向认证的问题,问题大致集中在:

如何使用 Yakit MITM与采用双向国密认证的服务器通信

这里我们采用互联网公开的 GMSSL 在线 demo 网站进行测试。启动 MITM 然后访问 URL:https://demo.gmssl.cn:2443

这时我们会发现网站返回了 400 Bad Request:

这个页面其实是 Nginx 为我们生成的一个页面,根据目标服务器的配置不同在国密双向认证握手失败的情况下 Yakit MITM 可能会报错 tls: handshake failure表示与远程服务器的握手失败。这时我们只需要在 Yakit 的全局配置中添加国密双向认证证书对中的签名证书。

添加后即可正常访问目标站点:

这意味着Yakit的插件系统和WebFuzzer都可以在启用国密双向认证的目标上正常生效

将证书与指定Host 绑定

早些时候有用户反馈希望能提供证书和 Host 绑定的功能,场景大概如下图:

大概就是在服务器前级存在类似证书网关的中间件,他在要求客户端提供证书的时候。如图:

这里的限制可能比较宽泛,这时我们简单依靠算法和 CA 可能无法筛选出合适的证书,因此在这种场景下需要支持证书与 host 的绑定。

我们在 Yakit 的全局配置中,在添加完证书后点击证书左侧的齿轮即可将该证书与指定 host 绑定。

证书宽松匹配机制

最近也有用户反馈在使用证书后仍然无法正常访问启用了 mTLS 的目标服务器。首先,我们需要了解客户端在收到服务端的客户端证书验证请求后会如何处理。

这里其实最关键的是:

在 go 的源码中直接使用了 bytes.Equal 去判断证书的 RawIssuer 和 ca 是否相同。这里需要简单介绍一下 ASN.1(AbstractSyntaxNotation One,抽象语法标记法一**)**的相关概念: 这个术语。

我们可以将 ASN.1 理解为一种“数据结构的语言”:

1、抽象语法:它是一套国际标准,用于定义和描述各种复杂数据的结构,比如证书、密钥或数字签名等。它规定了数据“长什么样”,而与使用哪种编程语言或计算机系统无关。

2、编码规则(如 DER):ASN.1 本身只定义结构,要让数据能在网络上传输或存储,就需要编码规则将这些抽象结构序列化为具体的二进制字节流**。在 TLS 证书中,最常用的是 DER(Distinguished Encoding Rules,可辨别编码规则)

因此,上图中证书的 **RawIssuer原始字节,就是由 ASN.1 结构经过 DER 等编码规则转换而成的二进制数据。

这样其实会产生潜在问题,可能造成证书原始字节不一致的原因是多方面的,其中一个因素是:

字符集与编码差异: 证书中的 Subject 或 Issuer 字段值属于 ASN.1 中的字符串类型。在编码这些字符串值时,不同的 CA 或工具可能会选择使用不同的 ASN.1 字符串编码规则,例如:

  • 使用**PrintableString** 或 T61String(常用于 ASCII 或简单的 Latin 字符)。
  • 使用**UTF8String** 或 UniversalString(常用于包含 Unicode 字符,如中文)。

尽管两个证书的主题名称(Subject)在逻辑上是相同的,但如果它们的原始字节因为编码时使用了:

ASCII/PrintableStringUnicode/UTF8String这两种不同的 ASN.1 字符串类型进行封装,或者内部填充方式略有不同,严格的bytes.Equal比较就会立即返回失败

因此,这种原始字节的严格相等性判断,使得证书匹配过于脆弱,极易因为生成工具、操作系统或编码规则的细微差异而导致匹配失败,最终影响 Yakit MITM 功能的正常使用。在后续版本中我们已经对证书匹配逻辑进行了优化。


本文首发于 Yak Project 公众号,阅读原文