4、中间证书颁发机构(CA):生成中间CA私钥、使用根证书签发中间CA证书、证书链的层级概念
好,我们继续往下走。上一章我们把根CA建好了,它就像一把藏在保险柜里的万能钥匙。但你想,如果每次签发服务器证书都用这把万能钥匙,万一哪天它被偷了,整个信任体系就全完了。
所以,我们需要一个“中间人”。这就是中间CA。
4.1 为什么非要中间CA不可?
说白了,就是风险隔离。根CA的私钥应该永远处于离线状态,甚至物理隔离。日常签发证书的工作,交给中间CA去做。
我见过一个真实的案例。某大厂早期没做层级隔离,根证书私钥直接放在在线服务器上。结果被拖库,私钥泄露,不得不紧急吊销所有已签发的证书,重新铺信任链。那场面,简直是灾难。
所以,记住这个原则:根CA只签中间CA,中间CA才签终端证书。
核心原则:根CA私钥离线存储,中间CA私钥在线使用。一旦中间CA出问题,可以用根CA吊销它,而根CA本身不受影响。
4.2 生成中间CA的私钥
这一步和生成根CA私钥几乎一样。但有个细节:中间CA的密钥长度,我个人习惯和根CA保持一致,都用4096位。虽然2048位在安全上目前也够用,但既然根CA已经用了4096,中间CA就别降级了,保持统一标准。
# 生成中间CA私钥,带AES-256加密
openssl genrsa -aes256 -out intermediate/private/intermediate.key.pem 4096
# 设置权限,只允许文件所有者读取
chmod 400 intermediate/private/intermediate.key.pem
这里你会被要求输入一个密码。嗯,这个密码一定要记牢。我建议你把它存在密码管理器里,或者写进物理保险柜的文档里。别指望靠脑子记,三个月后你肯定忘。
小技巧:中间CA私钥的密码可以和根CA不同。我个人习惯用不同的密码,这样即使中间CA的密码泄露,根CA依然是安全的。
4.3 生成中间CA的证书签名请求(CSR)
有了私钥,下一步就是生成CSR。这里要注意,中间CA的DN(Distinguished Name)必须和根CA不同。尤其是Common Name,要能清晰区分层级。
# 创建中间CA的配置文件 intermediate/openssl.cnf
# 这里只列出关键部分,完整配置请参考课程附件
[ req ]
prompt = no
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
C = CN
ST = Beijing
L = Beijing
O = My Company Intermediate CA
OU = Certificate Authority
CN = My Company Intermediate CA 2024
# 生成CSR
openssl req -new \
-key intermediate/private/intermediate.key.pem \
-out intermediate/csr/intermediate.csr.pem \
-config intermediate/openssl.cnf
你看,这里的O我写的是“My Company Intermediate CA”,而不是根CA的“My Company Root CA”。这样一眼就能看出层级关系。
4.4 使用根CA签发中间CA证书
这一步是关键。我们要用根CA的私钥去签中间CA的CSR。注意,这里要加一个扩展参数 -extensions v3_ca,告诉OpenSSL:这是一个CA证书,不是终端证书。
# 切换到根CA的工作目录
cd /root/ca
# 使用根CA签发中间CA证书
openssl ca \
-config openssl.cnf \
-extensions v3_intermediate_ca \
-days 3650 \
-notext \
-md sha256 \
-in intermediate/csr/intermediate.csr.pem \
-out intermediate/certs/intermediate.cert.pem
# 设置权限
chmod 444 intermediate/certs/intermediate.cert.pem
这里有个坑,我踩过。如果你在根CA的配置文件中没有定义 v3_intermediate_ca 这个扩展段,OpenSSL会报错。所以,一定要在根CA的 openssl.cnf 里加上这一段:
[ v3_intermediate_ca ]
# 标记为CA证书,关键路径长度为0(表示不能再往下签)
basicConstraints = critical, CA:TRUE, pathlen:0
# 密钥用途
keyUsage = critical, digitalSignature, keyCertSign, cRLSign
# 证书策略(可选)
certificatePolicies = 2.5.29.32.0
注意 pathlen:0。这个参数的意思是:这个中间CA不能再签发下一级CA了。如果你希望中间CA还能再签下级CA,可以设成 pathlen:1。但一般情况下,两层就够了,根CA→中间CA→终端证书。
警告:千万不要把 pathlen 设得太大,或者干脆不设。否则中间CA被攻破后,攻击者可以无限签发下级CA,整个信任链就失控了。我曾经见过一个案例,就是因为没设 pathlen,导致一个被攻破的中间CA签出了几十个伪造的二级CA。
4.5 验证中间CA证书
签发完成后,一定要验证一下。看看证书内容对不对,是不是由根CA签发的。
# 查看中间CA证书内容
openssl x509 -noout -text -in intermediate/certs/intermediate.cert.pem
# 验证证书链:用根CA证书验证中间CA证书
openssl verify -CAfile certs/ca.cert.pem \
intermediate/certs/intermediate.cert.pem
如果输出 intermediate.cert.pem: OK,那就说明验证通过了。如果报错,多半是扩展字段配置有问题,或者根CA的证书路径没写对。
4.6 证书链的层级概念
好,现在我们来聊聊证书链到底是个什么东西。
你想想看,浏览器访问一个HTTPS网站时,它拿到的是服务器证书。但这个证书是谁签的?中间CA。那浏览器怎么信任中间CA?因为中间CA的证书是根CA签的。而根CA的证书,是预装在操作系统或浏览器里的。
这就形成了一条链:
- 根证书(自签名,预装在信任库中)
- ↓ 签发
- 中间证书(由根CA签发)
- ↓ 签发
- 服务器证书(由中间CA签发)
浏览器验证时,会从服务器证书开始,逐级向上找签发者,直到找到一个它信任的根证书。这个过程叫“证书链验证”或“路径构建”。
我画了一张图,帮你理解这个层级关系:
你看,这个结构就像一棵倒挂的树。根在最上面,中间CA在中间,服务器证书在最下面。每一级都信任上一级,直到根。
4.7 创建证书链文件
在实际部署中,服务器需要把中间CA证书和服务器证书一起发给客户端。因为客户端(比如浏览器)只有根证书,没有中间CA证书。如果不发中间CA证书,客户端就找不到验证路径。
所以,我们需要创建一个证书链文件:
# 将服务器证书和中间CA证书合并
cat intermediate/certs/intermediate.cert.pem \
certs/ca.cert.pem \
> intermediate/certs/ca-chain.cert.pem
# 查看合并后的文件
cat intermediate/certs/ca-chain.cert.pem
这个 ca-chain.cert.pem 文件,就是服务器需要配置的证书链。顺序是:先服务器证书,再中间CA证书,最后根CA证书。但注意,有些服务器配置要求顺序反过来,具体看文档。
经验之谈:我建议你在Nginx或Apache上配置时,把证书链文件和服务器私钥分开存放。证书链可以公开,但私钥必须严格保护。我曾经见过有人把私钥和证书链放在同一个文件里,然后不小心提交到了Git仓库……嗯,后果你懂的。
4.8 本章小结
好,我们来快速回顾一下这章的核心内容:
| 概念 | 说明 | 关键点 |
|---|---|---|
| 根CA | 信任链的起点,自签名 | 离线存储,只签中间CA |
| 中间CA | 由根CA签发,用于签发终端证书 | pathlen=0,防止无限签发 |
| 服务器证书 | 由中间CA签发,用于实际服务 | 部署时需附带中间CA证书 |
| 证书链文件 | 服务器证书 + 中间CA证书 + 根CA证书 | 顺序因服务器而异 |