X.509证书体系:证书结构、证书链、自签名证书与CA签发证书
说到TLS,绕不开的就是X.509证书。这东西说白了就是数字世界的身份证。我早期做网关接入时,曾被证书链不完整的问题坑过——客户端死活连不上,抓包一看,服务器只发了自己那张证书,中间CA没带。嗯,从那以后我对证书结构就特别上心。
证书结构长什么样?
X.509 v3证书,你可以把它理解成一个带签名的数据结构。它包含两个大部分:tbsCertificate(待签名主体)和签名值。
我们直接看关键字段。我个人习惯把它们分成三组:
| 分组 | 字段 | 说明 |
|---|---|---|
| 身份信息 | Subject (主体) | 证书持有者,比如 example.com |
| 身份信息 | Issuer (签发者) | 签发该证书的CA名称 |
| 身份信息 | Subject Key Identifier | 主体的公钥哈希,用于找证书 |
| 公钥与算法 | Subject Public Key Info | 算法 + 公钥值 (RSA/ECDSA) |
| 公钥与算法 | Signature Algorithm | 签名用的算法 (如 sha256WithRSAEncryption) |
| 有效期与用途 | Validity (Not Before / Not After) | 生效时间和到期时间 |
| 有效期与用途 | Extended Key Usage (EKU) | 用途:ServerAuth, ClientAuth 等 |
| 扩展 | Basic Constraints | 标记是否为CA证书 (CA:TRUE/FALSE) |
核心原则:客户端验证证书时,实际上就是检查 Issuer 和 Subject 是否匹配,签名是否由上一级私钥签发,有没有过期,用途对不对。你想想看,这就像查身份证真伪,得看盖章对不对得上。
证书链——信任的传递
证书不会凭空信任。我们信任谁?信任根CA。根CA把小型CA的证书签了,小型CA再签你的服务器证书。这样就形成了一条链。
我曾经在给银行客户做网关时,需要部署双向认证。对方的根CA证书是我们自己生成的,但中间CA证书我忘了配到信任库。结果TLS握手报错certificate unknown。查了半天才发现链断了。链断了的后果是什么呢?客户端会直接拒绝连接。
注意:服务端发送证书时,必须把完整的证书链发过去(根证书可省略,但中间CA必须带)。很多开发者只发叶证书,导致校验失败。
典型的证书链结构:
- 根CA证书(自签名)—— 信任锚
- 中间CA证书(由根CA签发)—— 可有多级
- 终端实体证书(由中间CA签发)—— 用于实际TLS通信
自签名证书 vs CA签发证书
做开发环境或内部测试时,很多人会直接自己签个证书,也就是自签名。生产环境肯定得用CA签发的证书,否则浏览器会弹大红警告。
| 对比维度 | 自签名证书 | CA签发证书 |
|---|---|---|
| 信任来源 | 需要手动将证书导入信任库 | 操作系统/浏览器内置CA信任 |
| 成本 | 免费,简单生成 | 需购买或使用免费CA (如Let's Encrypt) |
| 使用场景 | 开发、内网、设备证书 | 对外服务、HTTPS网站 |
| 吊销机制 | 无(或手工) | CRL/OCSP 支持 |
| 安全隐患 | 中间人攻击风险大(无第三方验证) | 有权威CA背书,抗伪造 |
我建议:即使是测试环境,也尽量模拟真实证书链——自己搭个小型CA,给服务器签发证书。这样能提前发现链配置问题。
如何生成自签名证书?(快速参考)
下面这个命令我用了很多年,生成一个带SAN(主题备用名称)的自签名证书,适合开发调试:
# 生成私钥
openssl genrsa -out server.key 2048
# 生成自签名证书(有效期365天,带 SAN)
openssl req -new -x509 -key server.key -out server.crt -days 365 \
-subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,DNS:example.com,IP:127.0.0.1"
注意那个-addext参数,早期OpenSSL版本不支持,可以用配置文件代替。如果你用的是旧版,可能得手动写openssl.cnf。
小技巧:检查证书内容的命令:openssl x509 -in server.crt -text -noout。看看Issuer和Subject是否一致(自签名证书二者相同),扩展字段是否带CA标志。
证书链的完整流程图
下面这张图展示了从根CA到终端证书的层级关系,以及握手时客户端如何逐级验证。
生产环境中的避坑指南
讲到最后,分享几个我踩过的坑:
- 证书链顺序:服务端发送证书顺序必须是“终端 -> 中间CA1 -> 中间CA2 -> ...”根证书一般不发送。我见过有人把根证书放最前面,结果客户端解析出错。
- Wildcard证书的SAN限制:通配符
*.example.com只匹配单级子域,不能匹配sub.extra.example.com。你想想看,如果用了*.example.com却放到了二级域名上,HTTPS 直接报警。 - 证书过期监控:我建议在运维系统里写上每日检查脚本,提前30天发告警。我亲眼见过某云的证书过期导致全球页面打不开,那场面……
- 吊销清单:生产环境一定要配CRL分发点或OCSP。如果私钥泄露,不吊销就等于把大门敞开了。
好了,X.509证书这块其实细节很多,但核心就三个字:信任链。你理解了什么叫“签名传递信任”,剩下的就是背字段名和调OpenSSL参数了。