2. X.509标准详解:证书的八个核心字段
X.509标准,说白了就是数字证书的“身份证格式”。它规定了证书里必须写什么、怎么写。我最早接触X.509时,觉得它就是一堆ASN.1语法,枯燥得很。后来在项目中排查证书链验证失败,才发现——不懂字段细节,排错就是瞎猜。
今天咱们就把X.509 v3证书的八个核心字段拆开揉碎。每个字段我都结合实战踩过的坑来讲。
一句话总结:X.509 v3证书由8个主要字段组成,其中版本号、序列号、签名算法、颁发者、有效期、主体、公钥信息是必填项,扩展域是v3版本新增的可选但极其重要的部分。
2.1 版本号(Version)
证书有三个版本:v1、v2、v3。现在99%的证书都是v3。为什么?因为v1和v2不支持扩展域,而扩展域是现代PKI的基石。
我记得有一次,客户拿了个v1证书来问为什么TLS握手失败。我一看,证书没有Subject Alternative Name(SAN)扩展,浏览器直接报“证书名称不匹配”。这就是版本太老的问题。
| 版本 | 编码值 | 特点 |
|---|---|---|
| v1 | 0 | 最基础,无扩展域 |
| v2 | 1 | 增加了颁发者和主体唯一标识符,但很少用 |
| v3 | 2 | 支持扩展域,现代PKI标配 |
排错技巧:用 openssl x509 -in cert.pem -text -noout 查看证书时,第一行就是版本号。如果看到 v1 或 v2,基本可以判定证书过时了。
2.2 序列号(Serial Number)
序列号是CA给证书分配的唯一编号。它必须唯一,且不能重复。你想想看,如果两个证书序列号一样,CA怎么撤销其中一个?
我曾经遇到过一个诡异的问题:CRL(证书撤销列表)里明明有某个序列号,但客户端就是不认。后来发现,CA把序列号编码成了负数(ASN.1 INTEGER是有符号的),而客户端解析时用了无符号比较。嗯,这里要注意——序列号必须是正整数,RFC 5280明确要求。
避坑指南:我曾经见过某CA生成的序列号是0xFFFFFFFF这样的值,结果某些老旧系统解析时溢出,直接报错。建议序列号长度控制在8-16字节,且最高位不能为1。
2.3 签名算法(Signature Algorithm)
这个字段告诉你怎么验证证书的签名。它包含两个信息:哈希算法和公钥算法。比如 sha256WithRSAEncryption,就是先用SHA-256算摘要,再用RSA私钥加密。
我个人习惯,看到签名算法先检查两点:
- 哈希强度:SHA-1已经不安全了,2017年起主流CA不再签发SHA-1证书。如果看到SHA-1,直接打回。
- 算法匹配:签名算法必须和证书里的公钥算法一致。RSA公钥配ECDSA签名?那肯定不行。
# 查看签名算法
openssl x509 -in cert.pem -text -noout | grep "Signature Algorithm"
2.4 颁发者(Issuer)
颁发者就是签发这张证书的CA。它是一串DN(Distinguished Name),比如:
C=CN, O=MyCA, CN=My Root CA
这里有个坑:DN的编码顺序和大小写敏感。我记得有一次,上级CA的Subject是 CN=My Root CA,而下级证书的Issuer写成了 cn=my root ca。虽然人眼看一样,但机器比对时,某些实现会严格区分大小写,导致证书链验证失败。
排错技巧:用 openssl x509 -in cert.pem -issuer -noout 和 openssl x509 -in cacert.pem -subject -noout 对比,确保完全一致。包括空格、逗号后的空格都不能差。
2.5 有效期(Validity)
有效期包含两个时间点:notBefore 和 notAfter。证书只在两者之间有效。
你可能会想,这有什么好讲的?但实际排错中,时间问题占了很大比例。我遇到过三种典型情况:
- 客户端时间不准:手机时间调到了2015年,访问2024年的证书,直接报错。
- 时区问题:证书时间是UTC,系统本地时间没转对。
- 闰秒/时钟漂移:某些嵌入式设备时钟不准,导致证书“提前过期”。
避坑指南:我曾经在IoT项目里,设备没有RTC电池,每次重启时间都回到1970年。证书验证永远失败。解决方案是让设备先通过NTP同步时间,再去做证书校验。
2.6 主体(Subject)
主体就是证书持有者的身份。对于服务器证书,通常是域名或组织信息。比如:
C=CN, ST=Beijing, L=Beijing, O=Example Inc, CN=www.example.com
但注意,现代浏览器已经不靠Subject里的CN来验证域名了。它们看的是Subject Alternative Name(SAN)扩展。如果SAN里没有域名,浏览器会报“证书名称不匹配”。
我个人习惯,签发证书时一定把域名写进SAN,Subject里的CN反而可以省略。RFC 2818也建议这么做。
2.7 公钥信息(Subject Public Key Info)
这个字段包含两部分:公钥算法和公钥值。常见算法有RSA、ECDSA、Ed25519等。
我建议你关注两个点:
- 密钥长度:RSA 2048位是底线,4096位更安全。ECDSA推荐P-256或P-384。
- 算法兼容性:某些老旧系统不支持Ed25519,如果你签发Ed25519证书,客户端可能无法验证。
# 查看公钥信息
openssl x509 -in cert.pem -noout -pubkey
2.8 扩展域(Extensions)
扩展域是v3证书的灵魂。没有它,证书就是个“裸身份证”,什么权限都没有。常见的扩展有:
| 扩展名称 | OID | 作用 |
|---|---|---|
| Basic Constraints | 2.5.29.19 | 标记是否为CA证书,以及路径长度限制 |
| Key Usage | 2.5.29.15 | 指定密钥用途(数字签名、密钥加密等) |
| Extended Key Usage | 2.5.29.37 | 指定扩展用途(服务器认证、客户端认证等) |
| Subject Alternative Name | 2.5.29.17 | 列出证书适用的域名或IP |
| Authority Key Identifier | 2.5.29.35 | 标识签发此证书的CA公钥 |
| Subject Key Identifier | 2.5.29.14 | 标识此证书的公钥 |
这里我重点讲两个:
Basic Constraints:如果 CA:TRUE,说明这个证书可以签发下级证书。如果 CA:FALSE,它就是终端实体证书,不能当CA用。我曾经见过有人把终端证书的Basic Constraints设成 CA:TRUE,结果这个证书被用来签发其他证书,造成严重的安全事故。
Key Usage:这个扩展限制了公钥能干什么。比如,一个用于TLS服务器认证的证书,必须包含 digitalSignature 和 keyEncipherment(RSA场景)或 digitalSignature(ECDHE场景)。如果缺了,TLS握手会失败。
排错技巧:用 openssl x509 -in cert.pem -text -noout 查看扩展域。重点关注 X509v3 Basic Constraints 和 X509v3 Key Usage。如果发现CA证书没有 CA:TRUE,那证书链肯定建不起来。
知识体系总览
下面这张图把X.509证书的八个字段和它们的关系画出来了。你可以把它当作排错时的“地图”。
这张图把八个字段分成了两组:左边是必填字段(所有版本都有),右边是v3新增的扩展域。排错时,我习惯从左到右检查:先看版本号和有效期,再看颁发者和主体是否匹配,最后检查扩展域的权限设置。