3. X.509 证书标准:你手里的那张“身份证”到底长什么样

X.509 证书标准,说白了,就是 TLS 握手里用来交换身份的那张“电子身份证”。我这些年排查过不少双向认证接口的坑,十次里有七八次都能追到证书格式、证书链或者签名算法上。所以这一节,咱们把它拆开看明白。

3.1 证书格式:字段虽多,核心就这些

一个 X.509 证书,不只是一个公钥加个签名那么简单。它有一套完整的结构,定义在 ASN.1 里,但我们程序员更常用 openssl 命令或者代码库去解析。我个人习惯用 openssl x509 -text -in cert.pem 来查看,输出里能看到所有字段。

下面这张表格列出了最关键的那些字段,也是你调试接口时最常碰到的:

字段说明我踩过的坑
版本号 (Version)目前主流是 v3 (值为2),v1/v2 已淘汰有些老设备只认 v1,但 v3 的扩展功能用不了
序列号 (Serial Number)CA 为该证书分配的唯一整数我曾经遇到序列号重复的测试证书,浏览器直接报错
签名算法 (Signature Algorithm)CA 签发时用的算法组合,如 sha256WithRSAEncryption别把签名算法和证书公钥算法搞混,这俩不是一回事
颁发者 (Issuer)签发该证书的 CA 的 DN链验证时,Issuer 必须和上一级证书的 Subject 完全一致,大小写空格都不能差
有效期 (Validity)notBefore 和 notAfter时区问题!我曾见过证书写的是 UTC 时间,但服务器时区配错导致“未生效”
主体 (Subject)证书持有者的 DN双向认证中,服务端会用 Subject 里的信息做鉴权,比如 CN 字段
公钥 (Subject Public Key Info)算法标识 + 公钥值RSA 和 ECDSA 的公钥结构不同,解析库要选对
扩展 (Extensions)v3 引入,如密钥用法、基础约束、SAN 等基础约束 (CA:TRUE/FALSE) 是链验证的关键,很多人漏配
技巧: 调试接口时,优先检查有效期和基础约束扩展。这两个最容易出问题,也最容易修复。

3.2 证书链:信任不是孤立的,是一根链子

单张证书无法自证清白。你拿到的服务器证书,需要由上级 CA 签发,那个 CA 又可能由更上层的根 CA 签发,这样就形成了一条 证书链。验证时,接收方会从服务器证书开始,一直追溯到本机信任的根证书(信任锚)。

我早年做支付接口对接时,就因为证书链不完整,被对方网关拒绝了三次。对方只发了服务器证书,没有附带中间 CA 证书,对方验证到一半就断了——因为对方信任存储里只有根 CA,没有中间 CA。

下面这张图展示了典型的证书链结构:

证书链信任传递 根 CA 证书 Subject: /CN=Root CA CA: TRUE (自签名) 颁发 中间 CA 证书 Subject: /CN=Intermediate CA Issuer: /CN=Root CA 颁发 服务端/客户端证书 Subject: /CN=api.example.com Issuer: /CN=Intermediate CA 信任锚(Trust Anchor) 预装在操作系统中 验证 逐级向上校验签名

图中从上到下是颁发关系。实际验证时方向相反:从叶子证书开始,用中间 CA 的公钥验证叶子证书的签名,再用根 CA 的公钥验证中间 CA 的签名,直到碰到一个本机信任的根证书(自签名证书)。

注意: 交付服务器证书时,必须把中间 CA 证书也一并附上(组合成链文件)。只送叶子证书是双向认证接口最常见的“证书链不完整”错误。我曾经就因为漏了中间证书,线上排查了一整个下午。

3.3 签名算法:证书防伪的“印泥”

证书的签名算法,说的是 CA 用什么算法给证书内容“按手印”。它通常由两部分组成:摘要算法(比如 SHA-256) + 签名算法(比如 RSA 或 ECDSA)。你想想看,如果摘要算法太弱(比如 SHA-1),别人就能伪造证书。

目前最常用的是 sha256WithRSAEncryption 或 ecdsa-with-SHA256。2019 年之后主流 CA 已经停止签发 SHA-1 签名的证书,但我在一些老旧系统里还见过,那真是定时炸弹。

算法 OID摘要公钥算法安全强度
1.2.840.113549.1.1.11sha256RSA强(推荐)
1.2.840.10045.4.3.2sha256ECDSA强(密钥短,性能好)
1.2.840.113549.1.1.5sha1RSA弱(已不推荐)
1.2.840.113549.1.1.13sha384RSA强(更高安全)

有个细节容易被忽略:证书里的签名算法字段和 SubjectPublicKeyInfo 里的公钥算法是两码事。签名算法只决定 CA 怎么签这个证书,公钥算法决定证书持有者怎么用私钥做 TLS 握手。例如,证书可以用 RSA 公钥,但 CA 可以用 ECDSA 签名来签发它,完全 OK。

实战经验: 做双向认证时,我习惯用 openssl verify -CAfile root.pem -untrusted intermediate.pem server.pem 来测试证书链和签名。如果提示“unable to get local issuer certificate”,99% 是中间证书没传。

3.4 避坑指南:证书链的“常见死法”

  • 链不完整:服务端没发中间 CA,客户端只有根 CA,验证失败。我曾经帮一个客户排查,他们把中间 CA 证书拼在了根 CA 后面,顺序反了,也验证不过。
  • Subject 和 Issuer 不匹配:叶子证书的 Issuer 必须等于上一级证书的 Subject,字符串完全一致(包括空格和大小写)。
  • 基础约束缺失:中间 CA 证书的扩展里没有 CA:TRUE,验证器会认为它不能签发下级证书。
  • 签名算法过弱:SHA-1 签名的证书在主流浏览器和 OpenSSL 1.1.1+ 中已被拒绝。
我的自查口诀: “链要全,名要对,期要长,签名要强,扩展不能没。”