4、证书签发实战:使用根CA签发子CA、签发服务器证书、签发客户端证书
理论讲完了,咱们得真刀真枪干一场。
这一章,我会带你走完三条核心签发路径:根CA签子CA、根CA签服务器证书、根CA签客户端证书。说白了,就是把上一章生成的根CA密钥对用起来,让它真正干活。
我个人习惯把这三步叫做「证书家族三代人」——爷爷(根CA)生爸爸(子CA),爸爸给员工(服务器)和访客(客户端)发身份证。你想想看,如果爷爷亲自给每个员工发证,那爷爷得累死,而且风险极高。所以实际生产里,我们几乎都是用子CA来签发终端证书。
核心原则:根CA只做一件事——签发子CA。根CA的私钥签完子CA后,就应该离线冷存储,甚至锁进保险柜。服务器和客户端证书,全部由子CA签发。
4.1 使用根CA签发子CA证书
先来第一步:让根CA给子CA签个「出生证明」。
子CA本质上也是一个X.509证书,只不过它的CA标志位是TRUE,并且Path Length限制了它还能往下签几层。我一般设pathlen:0,意思是子CA只能签终端证书,不能再签下一级CA。
操作步骤:
- 生成子CA的私钥和证书签名请求(CSR)
# 生成子CA私钥(4096位,RSA)
openssl genrsa -out subca.key 4096
# 生成子CA的CSR
openssl req -new \
-key subca.key \
-out subca.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/OU=CA/CN=MyOrg SubCA 2025"
- 使用根CA签发子CA证书
openssl x509 -req \
-in subca.csr \
-CA rootca.crt -CAkey rootca.key \
-CAcreateserial \
-out subca.crt \
-days 3650 \
-extfile <(echo "
basicConstraints=critical,CA:TRUE,pathlen:0
keyUsage=critical,keyCertSign,cRLSign
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
")
我的习惯:子CA有效期我设10年(3650天),根CA设20年。这样在子CA生命周期内,根CA只需要签一次。我曾经见过一个项目,子CA有效期只设了1年,结果每年都要把根CA从保险柜里请出来续签,运维成本极高。
- 验证子CA证书
openssl verify -CAfile rootca.crt subca.crt
如果输出subca.crt: OK,说明子CA证书链已经建立。你可以用下面命令看看子CA的细节:
openssl x509 -in subca.crt -text -noout | grep -A2 "CA:"
你会看到CA:TRUE, pathlen:0,嗯,这就对了。
4.2 签发服务器证书
服务器证书,就是你的Nginx、Apache、Tomcat等Web服务器用的SSL证书。它的核心要求是:Common Name (CN) 或 Subject Alternative Name (SAN) 必须包含服务器的域名或IP。
我强烈建议你只用SAN,别依赖CN。为什么?因为从Chrome 58开始,浏览器已经不再看CN字段了,只看SAN。我踩过这个坑——当年给一个内部系统签证书,只填了CN,结果所有用户访问时都报「证书名称不匹配」。后来老老实实加了SAN,问题才解决。
操作步骤:
- 生成服务器私钥和CSR
openssl genrsa -out server.key 2048
openssl req -new \
-key server.key \
-out server.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/OU=IT/CN=www.example.com"
- 创建SAN扩展配置文件
cat > server_ext.cnf <<EOF
[v3_req]
basicConstraints = CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = www.example.com
DNS.2 = example.com
DNS.3 = api.example.com
IP.1 = 192.168.1.100
EOF
- 使用子CA签发服务器证书
openssl x509 -req \
-in server.csr \
-CA subca.crt -CAkey subca.key \
-CAcreateserial \
-out server.crt \
-days 365 \
-extfile server_ext.cnf \
-extensions v3_req
注意:服务器证书有效期通常不超过1年(365天)。现在业界趋势是90天,比如Let's Encrypt。如果你做内部PKI,建议最长设398天(约13个月),这是主流浏览器允许的最大值。超过这个天数,Chrome会报安全警告。
- 验证服务器证书链
openssl verify -CAfile <(cat rootca.crt subca.crt) server.crt
这里需要把根CA和子CA的证书拼接在一起,形成完整的信任链。输出server.crt: OK才算通过。
4.3 签发客户端证书
客户端证书,说白了就是「你是谁」的证明。它常用于双向TLS认证(mTLS)、VPN登录、代码签名等场景。
客户端证书和服务器证书最大的区别在于extendedKeyUsage:服务器证书是serverAuth,客户端证书是clientAuth。如果你搞混了,客户端拿着服务器证书去认证,服务端会直接拒绝握手。
操作步骤:
- 生成客户端私钥和CSR
openssl genrsa -out client.key 2048
openssl req -new \
-key client.key \
-out client.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/OU=Dev/CN=zhangsan@example.com"
- 创建客户端证书扩展配置
cat > client_ext.cnf <<EOF
[v3_req]
basicConstraints = CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = clientAuth
subjectAltName = @alt_names
[alt_names]
email.1 = zhangsan@example.com
EOF
- 使用子CA签发客户端证书
openssl x509 -req \
-in client.csr \
-CA subca.crt -CAkey subca.key \
-CAcreateserial \
-out client.crt \
-days 365 \
-extfile client_ext.cnf \
-extensions v3_req
- 打包为PKCS12格式(可选,用于导入浏览器或系统)
openssl pkcs12 -export \
-in client.crt \
-inkey client.key \
-out client.p12 \
-name "MyOrg Client Cert" \
-passout pass:changeit
避坑指南:我曾经给一个团队签发客户端证书,忘了加clientAuth扩展。结果所有客户端TLS握手都失败,报错certificate verify error。排查了整整半天才发现是EKU配错了。所以签发完一定要用下面命令检查:
openssl x509 -in client.crt -text -noout | grep -A1 "Extended Key Usage"
看到TLS Web Client Authentication才算正确。
4.4 三种证书对比一览
| 对比维度 | 根CA证书 | 子CA证书 | 服务器证书 | 客户端证书 |
|---|---|---|---|---|
| CA标志 | TRUE | TRUE | FALSE | FALSE |
| Path Length | ≥1 | 0 | 无 | 无 |
| Key Usage | keyCertSign, cRLSign | keyCertSign, cRLSign | digitalSignature, keyEncipherment | digitalSignature, keyEncipherment |
| Extended Key Usage | 无 | 无 | serverAuth | clientAuth |
| 典型有效期 | 10-20年 | 5-10年 | 90-398天 | 1-3年 |
| 私钥存储 | 离线冷存储 | 在线HSM或加密 | 服务器本地 | 用户本地/浏览器 |
4.5 签发流程总览
下面这张图,把整个签发链路串起来了。你可以把它当作操作手册的封面图。
4.6 批量签发小技巧
如果你需要一次性签发几十上百张证书,逐条敲命令肯定不现实。我一般写个简单的Shell脚本,把CSR生成和签发流程自动化。
核心思路就三步:
- 准备CSR清单:一个CSV文件,每行包含域名、组织、OU等信息
- 循环生成CSR:用
openssl req -new批量生成 - 批量签发:用
openssl x509 -req配合-CA参数统一签发
重要提醒:批量签发时,一定要给每个证书生成独立的私钥。千万别复用私钥!我曾经见过一个团队,为了省事,所有服务器都用同一个私钥。结果一台服务器被入侵,所有服务器的TLS连接都得重新签发。私钥复用 = 安全归零。
嗯,这一章的内容就到这里。你现在应该能独立完成根CA签子CA、签服务器证书、签客户端证书这三条链路了。记住:根CA离线、子CA在线、终端证书按需签发——这个架构能陪你走很久。