2. X.509证书标准:证书结构、版本号、序列号、签名算法、颁发者与使用者、有效期、扩展域
聊到PKI,X.509证书就是那个绕不开的核心。说白了,它就是数字世界里的身份证。我刚开始接触证书时,觉得这东西就是个文件,后来踩过坑才明白——证书里的每一个字段,都有它存在的道理。
今天咱们就把X.509证书从头到脚拆一遍。你以后拿到一张证书,至少能看懂它到底在说什么。
2.1 证书的整体结构
一张X.509证书,不是简单的“公钥+签名”。它有一套标准化的结构。我习惯把它想象成一个信封:信封外面写着谁发的、发给谁、什么时候过期;信封里面装着真正的宝贝——公钥。
证书的基本结构可以分成三大部分:
- 证书主体(TBSCertificate):包含版本、序列号、签名算法、颁发者、有效期、使用者、公钥、扩展域等。这部分是要被签名的原始数据。
- 签名算法标识:告诉你怎么验证这个签名。
- 签名值:CA对证书主体做的数字签名,防止篡改。
嗯,这里要注意:签名算法标识出现了两次。一次在证书主体里,一次在外面。为什么?我后面会讲。
核心原则:证书的信任基础,就是CA对TBSCertificate的签名。只要签名有效,证书里的所有信息就不可抵赖。
2.2 版本号
X.509证书有三个版本。你想想看,现在谁还用v1?基本绝迹了。
| 版本 | 特点 | 现状 |
|---|---|---|
| v1 (1988) | 最原始的结构,没有扩展域 | 基本淘汰,仅兼容遗留系统 |
| v2 (1993) | 增加了颁发者和使用者唯一标识符 | 很少使用,被v3取代 |
| v3 (1996) | 引入扩展域,支持自定义属性 | 当前唯一标准 |
我个人习惯,在任何新项目中都只用v3。为什么?因为扩展域太重要了。没有扩展域,你就没法做SAN(主题备用名称)、没法做密钥用法限制、没法做CA标志位。说白了,v3之前的证书就是个“裸奔”的公钥。
小技巧:用OpenSSL查看证书版本时,v3证书显示的是2(从0开始计数)。别被这个数字搞懵了。
2.3 序列号
序列号是CA给每张证书分配的唯一编号。它必须唯一,不能重复。我曾经遇到过一个问题:某个CA的序列号生成器出了bug,重复了序列号。结果呢?证书吊销列表(CRL)里出现了两条相同序列号的记录,客户端直接报错。
序列号有几个关键点:
- 唯一性:同一CA下,序列号必须唯一。
- 不可预测性:RFC 5280建议序列号至少20位随机数,防止攻击者猜测。
- 编码:ASN.1中是一个整数,但通常用十六进制表示。
避坑指南:我曾经见过有人把序列号设成固定值“1”,结果所有证书序列号都一样。这种证书在吊销时根本没法区分。序列号一定要随机生成,别偷懒。
2.4 签名算法
签名算法字段告诉验证者:这个签名是用什么算法算出来的。常见的组合有:
| 算法OID | 哈希算法 | 签名算法 | 推荐度 |
|---|---|---|---|
| sha256WithRSAEncryption | SHA-256 | RSA | ⭐⭐⭐⭐⭐ |
| ecdsa-with-SHA256 | SHA-256 | ECDSA | ⭐⭐⭐⭐⭐ |
| sha1WithRSAEncryption | SHA-1 | RSA | ❌ 已废弃 |
| md5WithRSAEncryption | MD5 | RSA | ❌ 不安全 |
为什么签名算法要出现两次?我解释一下:证书主体里的签名算法是给签名者用的,告诉CA“请用这个算法签名”;外层的签名算法是给验证者用的,告诉客户端“请用这个算法验证”。虽然大多数情况下两者一致,但标准允许它们不同。嗯,实际中我很少见到不一致的情况,但设计上确实留了这个口子。
2.5 颁发者与使用者
这两个字段是证书的“发件人”和“收件人”。它们用DN(Distinguished Name,可分辨名称)来表示。
一个典型的DN长这样:
CN = www.example.com
O = Example Inc.
OU = IT Department
C = US
ST = California
L = San Francisco
各字段含义:
- CN (Common Name):通用名称。对于服务器证书,通常是域名。
- O (Organization):组织名称。
- OU (Organizational Unit):部门名称。
- C (Country):国家代码,两位字母。
- ST (State/Province):州或省。
- L (Locality):城市或地点。
颁发者(Issuer)是签发这张证书的CA。使用者(Subject)是这张证书的持有者。对于自签名证书,Issuer和Subject是一样的。
重要提醒:浏览器现在不再依赖CN来匹配域名了。CN字段已经被SAN(主题备用名称)扩展取代。你配置证书时,域名一定要写在SAN里,光写CN没用。
2.6 有效期
有效期由两个时间戳组成:
- Not Before:证书生效时间。
- Not After:证书失效时间。
我建议你注意以下几点:
- 时间同步:证书验证依赖系统时间。如果服务器时间不准,证书可能被误判为过期或未生效。我遇到过一台NTP服务挂了的设备,所有证书验证都失败,排查了半天才发现是时间问题。
- 有效期长度:CA/Browser Forum现在要求公开信任的SSL证书最长398天。内部PKI我建议设1-2年,太长有安全风险,太短运维成本高。
- UTC时间格式:证书里的时间用UTC表示,格式是YYMMDDHHMMSSZ。注意,2050年之后这个格式会出问题(年份只有两位)。新标准已经改用GeneralizedTime了。
避坑指南:我曾经在凌晨2点做证书轮换,结果新证书的Not Before设成了当前时间,但客户端时间比服务器慢了5分钟。所有客户端都报“证书尚未生效”。从那以后,我习惯把Not Before设为当前时间的前10分钟,留个缓冲。
2.7 扩展域
扩展域是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地址等 | 非关键 |
| CRL Distribution Points | 2.5.29.31 | 指定CRL下载地址 | 非关键 |
| Authority Information Access | 1.3.6.1.5.5.7.1.1 | 指定CA证书下载地址和OCSP响应器地址 | 非关键 |
每个扩展都有一个“关键”(Critical)标志。如果某个扩展标记为关键,但客户端不认识这个扩展,客户端必须拒绝这张证书。如果标记为非关键,客户端可以忽略它。
我个人的经验:Basic Constraints和Key Usage一定要标记为关键。这两个扩展直接决定了证书的安全边界。其他扩展可以设为非关键,兼容性更好。
实战建议:生成CA证书时,Basic Constraints要设CA=True,Key Usage要包含keyCertSign和cRLSign。生成终端实体证书时,CA必须为False,Key Usage根据用途设置。这个搞反了,整个PKI就乱套了。
2.8 一个完整的证书示例
用OpenSSL查看一张实际证书,你能看到所有字段:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
04:00:00:00:00:01:23:45:67:89:ab:cd:ef
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, O = Example CA, CN = Example Root CA
Validity
Not Before: Jan 1 00:00:00 2024 GMT
Not After : Jan 1 00:00:00 2025 GMT
Subject: C = US, O = Example Inc., CN = www.example.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication
X509v3 Subject Alternative Name:
DNS:www.example.com, DNS:example.com
Signature Algorithm: sha256WithRSAEncryption
Signature Value: ab:cd:ef:01:23:45:67:89... (签名值)
你看,从版本到签名值,每一段都有明确的意义。你拿到一张证书,按这个结构去读,就不会被一堆十六进制搞晕。
好了,X.509证书的结构就这些。理解了这个基础,后面讲证书链验证、CRL、OCSP时,你就能知道每个字段在验证过程中扮演什么角色了。