4. 数字证书结构:X.509标准与证书字段解析

数字证书这东西,说白了就是网络世界里的身份证。你想想看,我们怎么确认一个网站是真的?怎么确认一段代码确实是某个公司签的?靠的就是证书。而几乎所有证书,都遵循同一个标准——X.509。

我刚开始接触证书时,打开一个 .cer 文件,满屏的十六进制和字段名,说实话有点懵。后来做 PKI 项目,天天跟证书打交道,才慢慢摸清楚每个字段到底干嘛用的。今天咱们就把 X.509 证书从头到尾拆一遍。

4.1 X.509 标准概览

X.509 是国际电信联盟(ITU-T)定义的公钥证书格式标准。目前主流的是 X.509 v3 版本。它规定了证书里必须包含哪些信息、怎么编码、怎么扩展。

一个典型的 X.509 v3 证书,结构大致如下:

Certificate
├── TBSCertificate(待签名证书体)
│   ├── Version
│   ├── Serial Number
│   ├── Signature Algorithm
│   ├── Issuer
│   ├── Validity
│   │   ├── Not Before
│   │   └── Not After
│   ├── Subject
│   ├── Subject Public Key Info
│   │   ├── Algorithm
│   │   └── Public Key
│   └── Extensions(v3 新增)
├── Signature Algorithm
└── Signature Value

核心要点:证书体(TBSCertificate)是真正被签名的部分。CA 对这部分做哈希,然后用私钥签名,生成 Signature Value。验证时,用 CA 公钥解密签名,再对比哈希值。这就是证书防篡改的原理。

下面我用一张图把证书的签发和验证流程画清楚:

X.509 证书签发与验证流程 签发阶段(CA 侧) 证书体(TBS) 哈希运算 CA 私钥加密 签名值 最终证书 = TBS + 签名算法 + 签名值 验证阶段(验证者侧) 提取证书体 哈希运算 CA 公钥解密 提取签名值 对比两个哈希值 → 一致则证书有效 💡 关键:CA 公钥必须来自受信任的根证书,否则无法验证签名 证书体任何字段被篡改 → 哈希值不匹配 → 验证失败

4.2 核心字段详解

咱们一个一个字段过。每个字段我都会结合项目里的实际场景来讲。

4.2.1 版本号(Version)

目前主流是 v3(值为 2)。v1 和 v2 基本绝迹了。v3 最大的贡献是引入了扩展字段,让证书能承载更多信息。

4.2.2 序列号(Serial Number)

CA 给每个证书分配的唯一编号。我遇到过一个问题:某个 CA 的序列号生成器出 bug,重复了。结果证书吊销时,吊销列表里两条记录冲突,查了半天才发现是序列号重复。所以,序列号必须唯一,这是硬性要求。

4.2.3 签名算法(Signature Algorithm)

CA 对证书体签名时用的算法。常见的有:

算法标识符 哈希算法 签名算法 安全状态
sha256WithRSAEncryption SHA-256 RSA 安全(推荐)
sha384WithECDSA SHA-384 ECDSA 安全(推荐)
sha1WithRSAEncryption SHA-1 RSA 已废弃
md5WithRSAEncryption MD5 RSA 不安全

注意:我曾经在审计一个老系统时,发现它还在用 SHA-1 签名的证书。浏览器已经报红锁了。赶紧让客户换了。SHA-1 的碰撞攻击在 2017 年就已经被证实可行,千万别再用。

4.2.4 颁发者(Issuer)

签发这张证书的 CA 的身份信息。通常包含国家(C)、组织(O)、通用名称(CN)等。比如:

Issuer: C=CN, O=MyCA, CN=My Root CA

验证证书时,必须用颁发者对应的公钥来验签。所以,你得先信任这个颁发者。

4.2.5 有效期(Validity)

包含两个时间:

  • Not Before:证书生效时间
  • Not After:证书过期时间

我建议证书有效期别设太长。以前习惯设 5 年、10 年,现在业界趋势是 1 年甚至更短。为什么?密钥泄露风险、算法升级、合规要求。Google 已经在推动证书有效期缩短到 90 天。你想想看,如果私钥泄露,有效期越长,损失越大。

4.2.6 使用者(Subject)

证书持有者的身份信息。对于网站证书,Subject 的 CN 字段通常就是域名。比如:

Subject: CN=www.example.com, O=Example Inc, C=US

但要注意,现在浏览器更看重 Subject Alternative Name(SAN)扩展里的域名,而不是 CN。我遇到过客户只填了 CN,没填 SAN,结果浏览器报域名不匹配。嗯,这个坑踩过的人不少。

4.2.7 公钥信息(Subject Public Key Info)

这是证书最核心的部分——持有者的公钥。包含:

  • 算法:RSA、ECDSA、Ed25519 等
  • 公钥值:实际的密钥数据

举个例子,RSA 2048 位的公钥,长度是 256 字节。ECDSA 用 P-256 曲线,公钥是 65 字节(含前缀)。

个人经验:我在做移动端证书认证时,发现 ECDSA 公钥比 RSA 小很多,握手速度也快。如果你的设备性能有限,优先考虑 ECDSA。

4.3 X.509 v3 扩展字段

v3 扩展是证书的「可编程区域」。CA 可以在扩展里放各种信息,浏览器和操作系统会根据这些扩展做不同的校验。

常见的扩展字段:

扩展名称 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 证书吊销列表下载地址
Authority Information Access 1.3.6.1.5.5.7.1.1 CA 证书下载地址、OCSP 地址

我重点说几个容易出问题的:

4.3.1 Basic Constraints

这个扩展决定证书能不能当 CA 用。如果 CA:TRUE,说明这张证书可以签发其他证书。如果 CA:FALSE,它就是终端实体证书,不能签发。

我曾经见过一个测试环境,不小心把终端证书的 Basic Constraints 设成了 CA:TRUE。结果有人拿它签了一堆子证书,整个信任链乱套了。嗯,这个字段一定要严格管控。

4.3.2 Key Usage 和 Extended Key Usage

这两个字段限制了证书能干什么。比如:

  • digitalSignature:用于数字签名
  • keyEncipherment:用于密钥加密(如 TLS 握手)
  • serverAuth:用于服务器身份认证
  • codeSigning:用于代码签名

我建议严格按需设置。不要给一个代码签名证书加上 serverAuth,也不要给服务器证书加上 codeSigning。否则一旦私钥泄露,攻击者能做的事情就多了。

4.3.3 Subject Alternative Name(SAN)

SAN 是现代证书的标配。一个证书可以包含多个域名:

DNS Name: example.com
DNS Name: www.example.com
DNS Name: api.example.com

浏览器现在优先看 SAN,不看 CN。我建议你签发证书时,把所有需要用到的域名都列在 SAN 里。漏一个,那个域名就会报安全警告。

4.4 证书编码格式

证书有两种常见的编码方式:

格式 编码 文件后缀 特点
DER 二进制 .cer, .crt, .der 纯二进制,不可读
PEM Base64 .pem, .crt, .cer 文本格式,可读,带头尾标记

PEM 格式长这样:

-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIU...
(中间是 Base64 编码的 DER 数据)
-----END CERTIFICATE-----

我个人习惯用 PEM 格式。为什么?因为它可以用文本编辑器打开,方便调试。而且 PEM 可以一个文件里放多个证书(比如证书链)。DER 是纯二进制,只能用工具解析。

小技巧:用 OpenSSL 可以轻松转换格式:

# DER → PEM
openssl x509 -in cert.der -inform DER -out cert.pem -outform PEM

# PEM → DER
openssl x509 -in cert.pem -inform PEM -out cert.der -outform DER

4.5 实战:用 OpenSSL 解析证书

光说不练假把式。咱们拿一个真实的证书来拆解一下。假设你有一个 PEM 格式的证书文件 server.pem:

openssl x509 -in server.pem -text -noout

输出结果会包含所有字段。我截取一段典型的输出:

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 = Let's Encrypt, CN = R3
        Validity
            Not Before: Jan 15 00:00:00 2024 GMT
            Not After : Apr 14 23:59:59 2024 GMT
        Subject: CN = www.example.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:b6:17:2d:...
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Alternative Name:
                DNS:www.example.com, DNS:example.com
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        6a:3f:1c:...(签名值,256 字节)

你看,所有字段一目了然。我建议你拿到任何证书,第一件事就是用 openssl x509 -text 看看它的完整结构。这比看文档直观多了。

避坑指南:我曾经在生产环境遇到一个证书,看起来一切正常,但浏览器就是报错。后来用 OpenSSL 一查,发现它的 Key Usage 里没有 digitalSignature。TLS 握手需要这个标记,没有它,浏览器直接拒绝连接。所以,签发证书时一定要检查 Key Usage 和 Extended Key Usage 是否匹配实际用途。

4.6 小结

X.509 证书的结构其实不复杂。核心就三块:证书体(包含身份和公钥)、签名算法、CA 的签名值。验证时,用 CA 公钥解密签名,对比证书体的哈希值,一致就通过。

实际工作中,最容易出问题的往往是扩展字段——Key Usage 设错了、SAN 漏了域名、Basic Constraints 没设对。嗯,这些坑我都踩过。你只要记住:签发证书前,用 OpenSSL 仔细检查一遍;验证证书时,别只看有效期,扩展字段也得过一遍。

证书结构搞清楚了,下一件大事就是证书链的验证。不过那是后面章节的内容了。今天先把证书本身吃透,后面的事就好办了。