交叉签名 CA:密钥相同,AKI 绑定不同
叶证书可以用密钥标识符加上颁发者和序列号来钉住颁发者。把交叉签名链当作可互换之前,先核对这层绑定。
两张 CA 证书可以持有相同的公钥和相同的 subject,却仍然不能互换。一张是自签名,另一张由旧根交叉签名,它们的颁发者名称、序列号和签名都不同。叶证书可以通过自己的 Authority Key Identifier 只与其中一张绑定。这种绑定一直不可见,直到客户端选错链、握手在没有明显原因的情况下失败。
按顺序做完这五项检查:每一项排除一种不同的故障。
检查 1:先画出两条链,再动模板
把路径写下来。旧用户群信任旧根 A,A 签发了 CA B,B 签发了服务器叶证书。新用户群直接信任自签名的 CA B。两张 B 证书持有相同的密钥和 subject,正因为如此,读 subject 行的人看到一个 CA,而客户端看到两个。
交叉签名的副本也必须是真正的 CA 证书,具备允许签名的 basic constraints 和 key usage。
检查 2:比较 AKI 字段,而不只是密钥
叶证书的颁发者名称必须与 B 的 subject 匹配,这部分通常成立。两个副本分道扬镳的地方是 Authority Key Identifier,因为它可以携带密钥标识符、颁发者证书和序列号。密钥标识符与两个副本都匹配,因为密钥完全相同。颁发者和序列号字段只与其中一个精确匹配,所以针对自签名副本构建的链,可能在交叉签名副本上失败。
去检查叶证书,而不是猜测:openssl x509 -in server.pem -noout -ext authorityKeyIdentifier。
然后比较两个候选:openssl x509 -in b-cross.pem -noout -text | grep -A2 "Authority Key Identifier",并把同一条命令用在自签名副本上。
如果叶证书携带颁发者和序列号,就只有这些值所描述的那张证书能补全链,无论客户端加载了哪个副本。
检查 3:改签发策略,绝不改已签名的字节
扩展位于签名内部。在现有叶证书上增加、删除或修改扩展不是修复,而是另一张证书,需要重新签发。改动属于签发策略:authorityKeyIdentifier = keyid,issuer:always 让新叶证书携带证书专属的绑定,而只有 keyid 时保留更宽松的形式,适合有意为之的交叉签名过渡。在下一批签发之前,先决定你的 PKI 要哪一种。
修改共享模板也会改变未来每一张证书,而不只是你眼前的服务器。
检查 4:分别验证每个信任群体
把旧路径和新路径当作两次独立的运行来测试。openssl verify -purpose sslserver -verify_hostname vpn.example -CAfile old-root.pem -untrusted b-cross-signed.pem server.pem 走的是交叉签名路径,而使用新根、不带 untrusted 文件的同一条命令走的是自签名路径。把两个根混在一个 bundle 里会掩盖到底选了哪条链。之后再用真实客户端重复一遍,因为 VPN 后端可能以 openssl 不会采用的方式偏好某条链或钉住某张证书。
通过客户端真正读取的服务配置下发中间证书,而不是通过维基页面。
检查 5:给旧路径设一个退役时钟
交叉签名按设计让旧的信任路径继续存活,这既是目的也是风险。它不能补救已被攻破的旧根,也不能把那个根从任何客户端移除。写下旧路径何时不再授予信任,并确认那一天真的会到来。
如果所有客户端都能在叶证书迁移之前接受新根,那么普通的双重信任铺开比交叉签名更简单,也少了一件要退役的事。