[Bug] 动态签发的叶子证书 authorityKeyIdentifier 为空 SEQUENCE,导致 Python 3.13+ X509_STRICT 校验失败

Author: HWWX5Created Sep 24, 2026Updated Sep 27, 2026
LabelsBugGood first issueknown issues

Ⅰ. 说明操作系统及DS的版本号:

  1. 操作系统:Linux(Debian/Ubuntu 系),kernel 7.0.0-15-generic
  2. DS 版本号:v2.2.0(DevSidecar-2.2.0-linux-x86_64.deb)
  3. 相关环境: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

Ⅳ. 如何复现问题?

  1. 启动 dev-sidecar 并开启系统代理(默认 127.0.0.1:31181)
  2. 用 Python 3.13+ 访问任意被 dev-sidecar 拦截的域名:
python
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
  1. 不依赖 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 failed

error 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 + 实际 keyid

openssl 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:C7

2. 可能的根因代码路径(推测)

在 resources/app.asar 中可见如下逻辑:

javascript
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 属于长期存在、近期才被现代工具链暴露的问题。