参会日期:2026-03-17 星期二
参会时间:14:00 - 15:30
参会地点:Grand Ballroom 2
会议领域:IRTF(互联网研究任务组)
会议名称:Measurement and Analysis for Protocols(MAPRG)
参会者 & 本文章撰写者:赵星涵
ISC 菁才计划

RFC3161 时间戳签名信息如下

国际化 X.509 证书

这是本次 MAPRG 会议当中的一小节。在本节会议中,来自中关村实验室和国内几所顶级大学的研究人员分析了当前国际化 X.509 证书(Unicert)的三个主要问题

  • 签发合规性:CA 是否按照复杂的 Unicode 相关标准正确签发了 Unicert?
  • 解析准确性:主流 TLS 实现是否能根据规范正确解析 Unicert?
  • 现实影响:不合规的签发和解析缺陷会带来什么安全风险?

Unicert 与 UA

Unicert 指包含国际化内容的 X.509 证书

而 UA(通用接受度)旨在倡议建立一个多语言包容的互联网,核心是确保系统能正确处理 ASCII 字符集之外的字符(Unicode)

多语言包容,使 X.509 证书的签发、解析和验证变得更加复杂,从而引发可能的安全风险,例如证书伪造、解析出错、缓冲区溢出等

混乱的文档

和 X.509 相关的文档高度耦合且不断演变,各种合规性规则散落在自然语言描述、ASN.1 定义和表格中

研究人员利用大语言模型(RFCGPT)进行整理,提取了 95 条规则,涵盖 36 个与 Unicode 相关的字段,其中 50 条在现有的合规性检查工具中缺失

签发合规性问题

根据 95 条规则,研究人员分析了 700 亿证书数据集中的 3480 万张 Unicert,发现了 24.9 万张不合规的 Unicert(占总数的 0.72%)

不合规问题:

  • T1. 字符检查不当(4.3 万张)
  • T2. 缺乏值规范化(3 张)
  • T3. 格式/结构无效(20.6 万张)

在不合规的证书中,65.3% 由公开信任的 CA 签发;21.1% 来自有限信任的 CA;13.6% 来自不受信任的 CA,涉及 505 个组织,甚至包括几大全球知名 CA

证书字段的复杂性

研究人员发现,证书中有高达 17 个字段在包含非 ASCII 文本时出现不一致现象和违规问题

例如 DNSNames 这个字段,有 2.7 万张包含畸形 IDN 的证书(无法转换为 Unicode,或 Punycode 解码后包含非法字符)

缺陷灰盒测试

研究人员构建了一种灰盒测试模型,构造包含特殊 Unicode 块和不同编码类型的测试证书,对 9 个主流 TLS 库的证书解析 API 进行了灰盒测试

测试结果为:全部 9 个 TLS 库都发现了证书字段解码或字符处理异常问题

安全风险

通用名(CN)伪造

编码与解码不匹配可能导致 CN 被伪造。例如,CA 用 BMPString 编码看似正常的域名,但用户的 TLS 库用 ASCII 解码,导致显示内容与实际验证内容不符

证书吊销列表(CRL)欺骗

控制字符的不当替换可能导致 CRL URI 被篡改。例如,http://ssl\u0001test.com 被替换显示为 http://ssl.test.com

属性嵌入

在 DNSName 中允许非 DNS 字符(如空格或特定分隔符),可能导致基于字符串的分析器将其误判为多个域名(如 a.com DNS:b.com 被解析为两个域)

安全现状

我认为,该问题的实际利用难度相当苛刻(毫不夸张的说,根本是不可能的),因为至少要达成以下几点条件

  • 恶意的 CA 精心构造假证书
  • 用户刚好信任该 CA
  • CT 刚好存在漏洞导致证书日志被遗漏
  • 用户的证书解析器刚好存在漏洞
  • 用户刚好被中间人攻击
  • 用户被中间人攻击的软件(例如网上银行)刚好风控算法不完善导致身份被伪造或信息泄露

截至目前,暂未发现任何在野利用证据(好比于:花数年时间研究密码学漏洞破解用户密码不如花数分钟把用户打一顿然后直接要密码)

漏洞复现

尽管这种攻击方式在实际中根本不可能发生,但出于学习的目的,我还是进行了复现测试

复现文章这里先挖个坑吧,以后再发公众号

对 Botan 密码学库的灰盒测试

原研究人员测试的 9 个密码学库中没有 Botan,所以我按原文提供的方法和工具对 Botan 密码学库页进行了测试

Botan 是使用 C++20 语法编写的 BSD 许可的加密和 TLS 库,和其他 TLS 库比起来,Botan 的社区很小,知名度也不高(Github 仅有 3.2k Star)

https://github.com/randombit/botan

测试工具

研究人员提供了现成的测试工具,Github 地址如下

https://github.com/Gjfnofalse/Unicert-tls_libs_difftest

项目目录下的 TestCertificates/input.json 即为测试证书集

测试代码编写

测试代码仅对数据集中的 IssuerSubject 进行了测试,其他的已跳过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include "json.hpp"
#include <botan/data_src.h>
#include <botan/x509cert.h>
#include <botan/x509path.h>
#include <fstream>
#include <string>
using json = nlohmann::ordered_json;

static std::string extract_dn(const Botan::X509_DN& dn) {
const auto& info = dn.dn_info();
assert(info.size() == 1);
return info[0].second.value();
}

int main() {
std::ifstream ifs("input.json");

json result = json::array();

std::string line;
while (std::getline(ifs, line)) {
json input = json::parse(line);

std::string_view focus_field = input["FocusField"];
std::string_view focus_field_value = input["FocusFieldValue"];

json output;

std::optional<Botan::X509_Certificate> cert;
try {
std::string_view pem = input["pem"];
Botan::DataSource_Memory data_src(pem);
cert = Botan::X509_Certificate(data_src);
}
catch (const Botan::Decoding_Error& e) {
output["decoding_error"] = e.what();
goto end;
}

if (focus_field.starts_with("Issuer")) {
output["decoded_value"] = extract_dn(cert->issuer_dn());

}
else if (focus_field.starts_with("Subject")) {
output["decoded_value"] = extract_dn(cert->subject_dn());
}
else {
continue;
}

output["expected_value"] = focus_field_value;
output["test_field"] = input["FocusField"];

end:
result.push_back(std::move(output));
}

std::ofstream ofs("output.json", std::ios_base::trunc);
ofs << result.dump(2, ' ', true);
}

运行后,打开 output.json 目视观察结果

如果该库的 X.509 证书解析器是完全符合标准的,那么应当是会解码失败。但事实上,只有非常少的几张证书出现了这种情况

TeletexString 类型的问题最多,甚至出现了错误的字符映射


https://crackme.net/articles/【IETF125参会记录】国际化 X.509 证书以及对 Botan 密码学库进行国际化证书灰盒安全测试/
作者
Brassinolide
发布于
2026年4月24日
许可协议