4、CA二级证书:生成CA证书请求、使用根证书签发二级CA、验证证书链

上一章我们搭好了根CA,但说实话,根CA的私钥太金贵了。我一般把它锁在离线机器里,平时签发证书根本不动它。那日常签发工作谁来做?二级CA。

二级CA,也叫中间CA。它由根CA签发,拿着它去签发终端设备证书。这样就算二级CA私钥泄露,吊销它就行,根CA还是安全的。说白了,就是分层管理,风险隔离。

核心思路: 根CA只签二级CA,二级CA签终端证书。证书链就是「根CA → 二级CA → 设备证书」。

4.1 生成二级CA的私钥和证书请求

先给二级CA生成一对密钥。我个人习惯用ECC,性能好、密钥短。当然RSA也行,看你们公司的安全策略。

# 生成二级CA的私钥(ECC P-256)
openssl ecparam -genkey -name prime256v1 -out intermediate.key

# 查看私钥信息
openssl ec -in intermediate.key -text -noout

私钥生成后,权限一定要锁死。我见过有人把中间CA的私钥权限设成644,结果被运维脚本误删了……嗯,血的教训。

接下来生成证书请求(CSR)。这里要注意,二级CA的CN(Common Name)最好和根CA区分开,比如用「Company Intermediate CA v1」。

# 生成二级CA的证书请求
openssl req -new \
  -key intermediate.key \
  -out intermediate.csr \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/CN=MyCompany Intermediate CA v1"
小技巧: 二级CA的CSR里,Organization(O)必须和根CA一致,否则证书链验证会报错。我踩过这个坑,查了半天才发现是O字段拼写不一样。

4.2 使用根证书签发二级CA

现在拿着根CA的私钥和证书,来签这个二级CA的请求。签发时有个关键参数——pathlen:0。什么意思?就是限制二级CA不能再签下一级CA,只能签终端证书。这样能防止证书链无限延伸。

# 使用根CA签发二级CA证书(有效期10年)
openssl x509 -req \
  -in intermediate.csr \
  -CA root.crt \
  -CAkey root.key \
  -CAcreateserial \
  -out intermediate.crt \
  -days 3650 \
  -sha256 \
  -extfile <(echo "
    basicConstraints = critical, CA:true, pathlen:0
    keyUsage = critical, keyCertSign, cRLSign
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid:always,issuer
  ")

签发完成后,你会得到一个 intermediate.crt。这就是二级CA证书。你可以用下面命令看看它的内容:

openssl x509 -in intermediate.crt -text -noout | grep -E "CA:|Path"

输出里应该能看到 CA:TRUE 和 Path Length Constraint:0。这就对了。

注意: 二级CA的证书有效期不要超过根CA。我习惯设成根CA的一半时长,比如根CA20年,二级CA就10年。到期前重新签发,避免证书链断裂。

4.3 验证证书链

证书签完了,链能不能串起来?必须验证。验证分两步:一是看二级CA是不是由根CA签发,二是看整个链是否可信。

4.3.1 验证二级CA的签发者

# 验证二级CA证书的签发者
openssl verify -CAfile root.crt intermediate.crt

如果输出 intermediate.crt: OK,说明根CA认这个二级CA。如果报错,八成是签发时扩展字段写错了,或者证书链文件没拼对。

4.3.2 构建完整证书链文件

实际部署时,客户端需要拿到完整的证书链,才能验证到根。所以我们要把根证书和二级CA证书拼在一起。

# 构建证书链文件(根证书在前,二级CA在后)
cat root.crt intermediate.crt > chain.crt

# 验证证书链
openssl verify -CAfile chain.crt intermediate.crt

这里有个细节:chain.crt 的顺序是「根证书 → 二级CA证书」。有些设备要求反过来,你部署前最好查一下文档。

验证结果解读:
  • OK — 证书链完整,信任关系建立
  • unable to get local issuer certificate — 缺少中间CA或根证书
  • certificate has expired — 证书过期,检查有效期
  • self-signed certificate in chain — 根证书自签名正常,但链可能不完整

4.4 证书链结构图

下面这张图展示了根CA、二级CA和设备证书之间的层级关系。我画图时习惯把根CA放在最上面,二级CA在中间,设备证书在最下面。箭头表示「签发」关系。

CA二级证书链结构图 CA二级证书链结构 根CA(Root CA) 自签名 · 离线存储 · 最高信任 签发 二级CA(Intermediate CA) pathlen:0 · 仅签终端证书 签发 设备证书(Device Cert) 终端实体 · 用于TLS/签名 证书链验证路径: 设备证书 → 二级CA证书 → 根CA证书(信任锚)

你看,从设备证书往上追溯,每一级都由上一级签发,最终停在自签名的根CA。这就是信任链的传递。客户端验证时,只要信任根CA,就能信任整条链上的所有证书。

4.5 避坑指南

做二级CA签发,有几个坑我替你们踩过了:

  • 扩展字段遗漏: 二级CA必须带 CA:TRUE 和 pathlen:0。漏了其中一个,客户端会认为它不是合法CA,拒绝信任。
  • 证书链顺序搞反: 有些服务器(比如Nginx)要求证书链文件里「服务器证书在前,中间证书在后」。但OpenSSL的 -CAfile 要求「根证书在前」。不同场景顺序不同,务必看文档。
  • 私钥权限太松: 二级CA私钥至少设成 400(仅所有者可读)。我曾经见过一个测试环境,中间CA私钥是644,被CI脚本意外覆盖了……嗯,那天下午我都在重签证书。
  • 有效期超过根CA: 二级CA过期了,根CA还在。但所有由二级CA签发的设备证书都会失效。所以二级CA的有效期一定要小于根CA。
我的习惯: 每次签发二级CA后,我会把 intermediate.crt 和 chain.crt 一起归档,并记录签发日期、有效期、用途。方便后续审计和续期。