[Bug] 动态签发的叶子证书 authorityKeyIdentifier 为空 SEQUENCE,导致 Python 3.13+ X509_STRICT 校验失败
Ⅰ. 说明操作系统及DS的版本号:
- 操作系统:Linux(Debian/Ubuntu 系),kernel 7.0.0-15-generic
- DS 版本号:v2.2.0(DevSidecar-2.2.0-linux-x86_64.deb)
- 相关环境:Python 3.14.4
根证书已正常安装到系统信任库(复制到 /usr/local/share/ca-certificates/ 并执行 update-ca-certificates,openssl verify 通过)。
Ⅱ. 问题描述:
dev-sidecar 代理动态签发的叶子证书中,authorityKeyIdentifier(OID 2.5.29.35)是一个空的 SEQUENCE(DER 为 30 00),不符合 RFC 5280 §4.2.1.1 —— 该节要求 keyIdentifier 必须存在。
开启严格模式校验的客户端会直接拒绝该证书。Python 3.13 起 ssl.create_default_context() 默认启用 VERIFY_X509_STRICT(本报告在 Python 3.14.4 上实测复现),因此所有走 dev-sidecar 代理的 Python 程序(requests / pip / urllib 等)在访问被拦截的域名时都会 CERTIFICATE_VERIFY_FAILED。
注意:这与根证书是否安装无关 —— 根证书安装完全正确时问题依然存在,重装根证书无法解决。
实测受影响的域名:github.com、api.github.com、raw.githubusercontent.com、www.google.com、registry.npmjs.org
(codeload.github.com 另表现为 TLS 握手超时,未深究是否同源)
对照:pypi.org / files.pythonhosted.org 正常 —— 因为它们未被 dev-sidecar 拦截,走的是真实证书。
补充:dev-sidecar 自身日志中没有报错,因为失败发生在客户端证书校验环节,代理侧看不到异常。
Ⅲ. 期望的结果:
动态签发的叶子证书中,authorityKeyIdentifier 应填入签发 CA 的 subjectKeyIdentifier 字节
(本机 CA 的 SKI 为 99:68:0C:BF:3D:59:31:C5:2F:9F:71:81:A3:D8:90:D1:99:38:A8:8F),
而不是留下一个空 SEQUENCE。
若使用 node-forge,对应写法是传入 keyIdentifier: true,使其从签发者证书派生。
修复后可用以下命令验证:
$ openssl verify -x509_strict -CAfile dev-sidecar.ca.crt leaf.pem
leaf.pem: OK # 应返回 OKⅣ. 如何复现问题?
- 启动 dev-sidecar 并开启系统代理(默认
127.0.0.1:31181) - 用 Python 3.13+ 访问任意被 dev-sidecar 拦截的域名:
import ssl, socket
ctx = ssl.create_default_context() # 3.13+ 默认含 VERIFY_X509_STRICT
s = socket.create_connection(('127.0.0.1', 31181), timeout=15)
s.sendall(b'CONNECT github.com:443 HTTP/1.1\r\nHost: github.com:443\r\n\r\n')
s.recv(200)
ctx.wrap_socket(s, server_hostname='github.com')结果:
SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED]
certificate verify failed: Missing Authority Key Identifier- 不依赖 Python,用 openssl 同样可复现:
$ openssl verify -CAfile dev-sidecar.ca.crt leaf.pem
leaf.pem: OK
$ openssl verify -x509_strict -CAfile dev-sidecar.ca.crt leaf.pem
CN=*.github.com, C=CN, ST=GuangDong, L=ShenZhen, O=dev-sidecar, ...
error 85 at 0 depth lookup: Missing Authority Key Identifier
error /tmp/leaf.pem: verification failederror 85 与 Python 报的是同一个错误,可确认问题在证书本身而非 Python 配置。
Ⅴ. 请提供相关的错误日志,尽可能的详细:
dev-sidecar 自身日志无报错,以下为证书本身的证据。
1. 原始 DER:AKI 是空 SEQUENCE
dev-sidecar 签发的叶子证书:
06 03 55 1d 23 OID 2.5.29.35 (authorityKeyIdentifier)
04 02 OCTET STRING, 长度 2
30 00 SEQUENCE, 长度 0 ← 空正常证书(同一台机器抓到的 PyPI 真实证书)作为对照:
06 03 55 1d 23 OID 2.5.29.35
04 18 OCTET STRING, 长度 24
30 16 SEQUENCE, 长度 22
80 14 b0 43 9e… keyIdentifier, 长度 20 + 实际 keyidopenssl x509 -ext authorityKeyIdentifier 输出:
# dev-sidecar 叶子证书
X509v3 Authority Key Identifier:
0. ← 无法解析
# PyPI 真实证书
X509v3 Authority Key Identifier:
B0:43:9E:30:70:F6:DE:FD:54:1E:12:3A:1D:E0:F7:61:CB:FB:20:C72. 可能的根因代码路径(推测)
在 resources/app.asar 中可见如下逻辑:
else if ("authorityKeyIdentifier" === e.name && t.cert) {
e.value = a.create(UNIVERSAL, SEQUENCE, true, []); // 先建空 SEQUENCE
l = e.value.value;
if (e.keyIdentifier) { // 仅当传入才填充
// push keyId ...
}
}即:AKI 扩展先被初始化为空 SEQUENCE,只有在 keyIdentifier 有值时才写入内容。
若当前签发路径未提供 keyIdentifier,产出的正是上述 30 00。
(说明:从打包产物中只能看到该填充逻辑,无法确认调用方传入了什么参数, 因此本节为推测,供定位参考。)
3. 已排除的因素
- 叶子证书为运行时内存生成,磁盘上无证书缓存(
~/.dev-sidecar/下仅有 CA 的 key/crt) - 连续两次连接签发的证书字节一致,AKI 均为
30 00,行为确定,非偶发 - 根证书安装正确:不加 strict 时
openssl verify返回 OK,CA 的 SKI 正常 - 与 #667 提到的「根证书无法正常安装」是不同问题:本问题在根证书安装正确的前提下依然复现
4. 影响范围
- 受影响:Python ≥ 3.13,以及任何显式启用
X509_STRICT的客户端 - 不受影响:curl、Node.js、Python ≤ 3.12(它们不做该项检查)
该 bug 属于长期存在、近期才被现代工具链暴露的问题。
Source: docmirror/dev-sidecar