数字证书基础:X.509证书结构、字段解析
说到PKI,最核心的物料就是数字证书。没有证书,一切信任模型都是空中楼阁。而X.509 v3是目前最通用的证书标准,几乎所有的HTTPS、代码签名、文档签名都在用这个格式。我早年刚接触PKI时,对着证书那一串十六进制字符串完全懵圈——后来才明白,证书就是个结构化的数据包,每个字段都有明确含义。
2.1 X.509 v3证书的整体结构
证书底层用ASN.1描述,编码成DER或PEM格式。你直接用浏览器打开一个HTTPS证书,看到的友好表单其实就是解析后的字段。但你用openssl x509 -text看到的才是原始结构。我习惯把证书拆成三层看:
- tbsCertificate(待签名证书体)——包含证书持有者公钥、身份、有效期、扩展等核心信息。
- signatureAlgorithm(签名算法标识)——CA对tbsCertificate签名时用的算法,比如sha256WithRSAEncryption。
- signatureValue(签名值)——CA的私钥对tbsCertificate做数字签名后的结果,用来防篡改。
说白了,你拿着链上的CA公钥,验一下签名值,就能确认tbsCertificate没被改过。我在项目中遇到过几次证书被中间人替换的情况,验签名那步一跑就现形了。
2.2 核心字段逐项解析
我把X.509 v3里最重要的字段列成一个表格,方便你对照。每个字段我都会提一句实际使用中的坑。
| 字段 | OID / 位置 | 作用 | 常见坑点 |
|---|---|---|---|
| Version | 整型 (0,1,2) | 版本号,v3=2 | 很多旧设备不支持v3扩展,造成握手失败 |
| Serial Number | 证书序列号 | 唯一标识一张证书,CA签发时递增 | 我曾见过序列号重复的证书链——CA侧CRL生成就全乱了 |
| Signature Algorithm | 算法OID | 声明CA签名用的算法(与外面一致) | 两个地方都要写一致,否则openssl会报错 |
| Issuer | DN字符串 | 签发者的Distinguished Name | CA的DN必须和subject一致,否则链断掉 |
| Validity | notBefore / notAfter | 有效期起止时间 | 时钟不同步会导致证书“提前过期”或“还未生效” |
| Subject | DN字符串 | 证书持有者身份,可以填域名、邮箱等 | 通配符证书的CN乱填会引发浏览器警告 |
| Subject Public Key Info | 算法+公钥值 | 持有者的公钥 | RSA/ECC密钥长度不足时,HTTPS握手协商失败 |
| Extensions (v3) | 扩展列表 | 限用途、密钥用法、CA标志等 | 漏配Basic Constraints会导致CA证书被误当EE证书用 |
2.3 扩展字段——v3的“灵魂”
X.509 v3引入扩展机制,让证书不只是一个公钥容器。扩展分为关键(critical)和非关键(non-critical)。关键扩展如果证书接收方不认识,必须拒绝整张证书。非关键的可以忽略。我设计PKI时,习惯把Basic Constraints和Key Usage设为关键,其他大部分设为非关键——你想想看,如果客户端不认识某个自定义扩展就直接拒掉,那通信就没法玩了。
常见的v3扩展有这些:
- Basic Constraints (2.5.29.19)——标记是否CA证书,ca: TRUE/FALSE。根CA必须是TRUE,终端证书必须是FALSE。
- Key Usage (2.5.29.15)——指定密钥用途,比如digitalSignature, keyEncipherment, keyCertSign等。CA证书必须带keyCertSign。
- Extended Key Usage (2.5.29.37)——更细化的用途,比如serverAuth(TLS服务端)、clientAuth(TLS客户端)、codeSigning(代码签名)。
- Subject Alternative Name (2.5.29.17)——存放证书关联的域名、IP、邮箱等,浏览器主要看这个,CN反而次要了。
- Authority Key Identifier (2.5.29.35)——指向签发者证书的公钥标识,方便构建证书链。
- Subject Key Identifier (2.5.29.14)——本证书的公钥标识,通常由公钥哈希得到。
2.4 证书的编码与格式
证书常见两种编码形式:DER是二进制,PEM是Base64加头尾标记。我平时开发调试更喜欢用PEM,因为能直接粘贴到网页工具里看。但生产环境下发证书链时,必须小心PEM的顺序——先放终端证书,再放中间CA,最后放根CA。服务端配置时如果顺序弄反,客户端会找不到签发者。
举个例子,你看下面这个PEM文件的结构:
-----BEGIN CERTIFICATE-----
(终端证书的Base64)
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
(中间CA证书的Base64)
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
(根CA证书的Base64)
-----END CERTIFICATE-----
注意:根CA其实可以不放,如果客户端信任列表里已有它。但为了兼容性,我建议还是带上完整的链。
2.5 一个真实的X.509证书字段快照
你可以在终端执行 openssl x509 -in google.pem -text -noout 看一张真实证书。输出大致是:
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 04:5a:...:f8
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, O = Google Trust Services, CN = GTS CA 1O1
Validity
Not Before: Mar 1 00:00:00 2023 GMT
Not After : Feb 28 23:59:59 2024 GMT
Subject: CN = *.google.com
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub: 04:d5:...
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:*.google.com, DNS:*.appengine.google.com, ...
X509v3 Key Usage: critical
Digital Signature
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
Signature Algorithm: sha256WithRSAEncryption
Signature Value: 55:3b:...
看到了吧?Issuer和Subject的DN不一定相同,因为这张是终端证书,由中间CA签发。SAN里列了一大堆域名,都是为了覆盖google所有子域名。
openssl verify -CAfile root.pem -untrusted intermediate.pem cert.pem。遇到报错就逐级检查Issuer和Subject是否对齐。
2.6 本章知识图谱
下面这张SVG图帮你把X.509证书的结构和字段层级理清楚。你可以把它当作复习索引,每次看证书时心里默念这几个模块:
2.7 证书生命周期中的字段变化
证书签发后,大部分字段是静态的,但有两个地方会动态更新:
- 签名值会随tbsCertificate内容变化重新计算,比如续签后有效期变了,签名就跟着变。
- 扩展字段可以在CA策略下调整,比如吊销后重新签发时,可以加一个Authority Information Access扩展指向OCSP响应器。
嗯,证书字段的解析就到这里。下回拿到一张陌生证书,你可以按这个步骤快速解读:一看版本v3、二看颁发者与主体、三看SAN域名、四看密钥用法、五验签名。手熟了之后,一眼就能判断证书配置有没有问题。