第2章 证书体系:X.509 证书结构、CA 信任链、自签名证书与 CA 签名证书
2.1 X.509 证书到底长什么样?
很多人把证书当成一个“加密文件”,其实它更像一张数字身份证。我刚开始接触 TLS 时,总以为证书里就只有公钥,后来被生产环境的一个证书链问题折腾了半夜——
打开一个 PEM 格式的证书,用 openssl x509 -text -noout 看,你会看到这些字段(按 ASN.1 顺序):
- 版本号:通常是 v3(值 2)。别问我 v1/v2 哪去了,你基本遇不到。
- 序列号:CA 给的唯一编号。企业内我见过有人用随机 128 位,也有懒人直接 timestamp。
- 签名算法:比如 sha256WithRSAEncryption。我个人习惯看一眼这个字段,因为有些老旧系统还用 sha1,该换了。
- 颁发者(Issuer):签这个证书的实体 DN。你想想看,这其实就是“谁给你签的名”。
- 有效期:notBefore 和 notAfter。我遇到过证书过期但没告警导致中断的事——
openssl x509 -checkend 86400定期跑一下很重要。 - 主体(Subject):证书持有者的 DN。对于服务器证书,CN 字段过去用域名,现在主流用 SAN 扩展。
- 公钥信息:算法 + 公钥值。RSA 2048 位以上或 ECDSA P-256 都常见。
- 扩展(Extensions):v3 的精华。Basic Constraints(标记 CA 能力)、Key Usage、Extended Key Usage、Subject Alternative Name(SAN,重点中的重点)。
- 签名值(Signature):CA 用私钥对整个证书做签名。嗯,这里要注意:签名覆盖的是 tbsCertificate 部分,不包括签名本身。
2.2 CA 信任链——为什么浏览器会“认识”你的证书?
说白了,信任链就是个“签名接力”。你拿到一张服务器证书,浏览器不会直接信它,而是会去看是谁签了它,然后再看是谁签了那个签发者……一直追到浏览器内置的信任锚(根证书)。
我参与过某大厂的内部 PKI 改造,当时有三十多台中间 CA,链验证的代码写错顺序直接导致全站 TLS 握手失败。所以理解这个结构非常关键:
- 根 CA(Root CA):自己签自己,也叫自签名根证书。所有信任的起点。通常离线保存,极少签发实体证书。
- 中间 CA(Intermediate CA):由根 CA 签发,Basic Constraints 中 CA:TRUE。它负责下签服务器或客户端证书。好处是根可以锁起来,中间泄露也能吊销而不影响根。
- 叶证书(Leaf Certificate):即你的服务器证书,CA: FALSE。它不能被用来签发其他证书。
验证路径时,接收方必须收到完整的证书链(根除外,因为客户端已有信任锚)。我曾经遇到客户只发了叶证书,结果安卓设备全跪——链不完整。
2.3 自签名证书 vs CA 签名证书
自签名证书就是自己当自己的 CA——证书里的 Issuer 和 Subject 相同,签名用的也是自己的私钥。CA 签名证书则是由一个独立的 CA 实体签发,Issuer 和 Subject 不同。
你可能会问:为什么要有自签名?其实很多内部环境、开发测试甚至一些 IoT 设备都用它。但生产环境对外服务,千万别偷懒——用户浏览器会给你一个大红叉。
| 对比项 | 自签名证书 | CA 签名证书 |
|---|---|---|
| 信任来源 | 需要手动导入信任锚 | 浏览器/OS 内置信任根 |
| 浏览器信任 | 不信任(除非本地安装根) | 默认信任(公共 CA) |
| 部署成本 | 零成本,一条 openssl 命令搞定 | 需购买或搭建内部 CA,流程较长 |
| 适用场景 | 开发/测试、内网安全、物联网 | 生产环境、对外服务、公众访问 |
| 维护复杂度 | 低,到期自己续就行 | 需要管理 CRL/OCSP,吊销策略 |
2.4 核心逻辑一览(SVG 结构图)
下面这张图把本章三个知识点串在了一起——你可以看到证书链的层级关系,以及自签名证书在这个体系中的不同位置。
2.5 实操:用 OpenSSL 生成证书看看
光说不练假把式。下面几条命令你可以跟练一遍,马上就能感受到自签名和 CA 签名的区别:
openssl req -x509 -newkey rsa:2048 -keyout self.key -out self.crt \
-days 365 -nodes \
-subj "/CN=myserver.local" \
-addext "subjectAltName = DNS:myserver.local,DNS:localhost,IP:127.0.0.1"
-addext 需要 OpenSSL 1.1.1+,旧版本要用配置文件。我曾在 CentOS 7 上踩过这个坑。
查看证书细节:
openssl x509 -in self.crt -text -noout | head -30
你会看到 Issuer 和 Subject 完全相同,Basic Constraints: CA:FALSE(除非你加 -extensions v3_ca)。
要搭建一个迷你 CA 链,需要先自签根:
openssl req -x509 -newkey rsa:2048 -keyout ca.key -out ca.crt \
-days 3650 -nodes -subj "/CN=My Test Root CA" -extensions v3_ca
然后用根签中间:
# 生成中间 CSR
openssl req -newkey rsa:2048 -keyout inter.key -out inter.csr \
-nodes -subj "/CN=My Test Intermediate CA"
# 用根签发中间(加 CA 扩展)
openssl x509 -req -in inter.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out inter.crt -days 1825 \
-extfile <(echo "basicConstraints=critical,CA:TRUE")
最后用中间签服务器:
openssl x509 -req -in server.csr -CA inter.crt -CAkey inter.key \
-CAcreateserial -out server.crt -days 365 \
-extfile <(echo "subjectAltName=DNS:example.com")
openssl verify -CAfile ca.crt -untrusted inter.crt server.crt如果输出
server.crt: OK,说明信任链没问题。
你可能会发现,自签名证书直接 openssl verify self.crt 会报错——因为没指定信任锚。加 -CAfile self.crt 就好了,这恰好证明了自签名的信任要靠显式导入。
好了,到此你已经掌握了 X.509 证书的核心字段、CA 信任链的层级结构,以及自签名与 CA 签名证书的根本区别。这些知识是后续排查 TLS 握手失败、证书链不完整、浏览器不信任等问题的基础。记住:证书不是加密的,它只负责绑定公钥和身份,真正的安全要靠 TLS 握手协议来落实。