3、证书签发自动化:使用OpenSSL或CFSSL自动签发证书、配置CA、生成CSR
证书签发这件事,说白了就是让机器替你干活。手动生成一个证书不难,但当你面对几百台服务器、几十个环境时,手敲命令就是灾难。我刚开始管证书那会儿,就吃过这个亏——半夜三点被叫起来签发一张紧急证书,手一抖把私钥权限搞错了,结果服务重启失败。嗯,从那以后我就铁了心要搞自动化。
这一章,我们聚焦两个工具:OpenSSL 和 CFSSL。前者是老兵,几乎每台Linux机器都自带;后者是CloudFlare出品的现代方案,适合批量操作。我会把CA配置、CSR生成、证书签发这三个环节拆开讲,每个环节都带上我踩过的坑。
核心目标:用脚本或配置文件,实现「一键签发」证书,不再手动输入任何交互信息。
3.1 配置CA:你的信任锚点
CA(证书颁发机构)是整个体系的信任根。你签发的每一张证书,最终都要追溯到CA的根证书。配置CA说白了就是生成一对密钥和自签名证书,然后把它锁好。
我个人习惯用OpenSSL来搭CA,因为它的配置文件机制非常灵活。下面是我常用的CA配置文件模板:
# ca.cnf - CA配置文件
[ req ]
default_bits = 4096
default_keyfile = ca-key.pem
distinguished_name = req_distinguished_name
x509_extensions = v3_ca
prompt = no
[ req_distinguished_name ]
C = CN
ST = Beijing
L = Beijing
O = MyCompany
OU = Security
CN = MyCompany Root CA
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:TRUE, pathlen:0
keyUsage = critical, digitalSignature, keyCertSign, cRLSign
有了这个文件,生成CA就一行命令:
openssl req -x509 -new -nodes -config ca.cnf \
-keyout ca-key.pem -out ca-cert.pem -days 3650
我的经验:CA的私钥一定要加密存储,最好放在离线机器或HSM里。我曾经见过有人把CA私钥直接放在Git仓库里——那等于把整个信任体系拱手送人。
如果你更喜欢CFSSL,它的CA配置更简洁。一个JSON文件搞定:
{
"CN": "MyCompany Root CA",
"key": { "algo": "rsa", "size": 4096 },
"names": [
{ "C": "CN", "ST": "Beijing", "L": "Beijing", "O": "MyCompany" }
],
"ca": { "expiry": "87600h" }
}
然后用 cfssl gencert -initca ca.json | cfssljson -bare ca 生成。注意CFSSL默认输出三个文件:ca.pem(证书)、ca-key.pem(私钥)、ca.csr(CSR,CA的CSR其实用不上,但CFSSL会生成)。
3.2 生成CSR:告别交互式输入
CSR(证书签名请求)是申请证书的「申请书」。手动生成CSR时,OpenSSL会弹出一堆问题让你填——国家、省份、组织名……你想想看,如果每张证书都这么填,不仅慢,还容易出错。
自动化的核心就是用配置文件代替交互。下面是我常用的CSR配置文件:
# server.cnf - 服务器证书CSR配置
[ req ]
default_bits = 2048
default_keyfile = server-key.pem
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[ req_distinguished_name ]
C = CN
ST = Beijing
L = Beijing
O = MyCompany
OU = IT
CN = *.example.com
[ v3_req ]
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = example.com
DNS.2 = *.example.com
DNS.3 = api.example.com
生成CSR的命令:
openssl req -new -config server.cnf -keyout server-key.pem -out server.csr
注意这里 prompt = no 是关键——它告诉OpenSSL不要问任何问题,直接从配置文件里读。我刚开始用的时候漏了这一行,结果脚本跑起来卡在交互输入上,气得我差点砸键盘。
避坑指南:我曾经在生成CSR时忘记加 subjectAltName,结果证书签发后浏览器报「证书名称不匹配」。现在我的CSR配置里永远带着SAN字段,哪怕只有一个域名也要写进去。
CFSSL生成CSR的方式更直接。你只需要一个JSON文件:
{
"CN": "*.example.com",
"hosts": [
"example.com",
"*.example.com",
"api.example.com"
],
"key": { "algo": "rsa", "size": 2048 },
"names": [
{ "C": "CN", "ST": "Beijing", "L": "Beijing", "O": "MyCompany" }
]
}
然后执行:
cfssl genkey csr.json | cfssljson -bare server
CFSSL会自动生成私钥和CSR,一步到位。我个人觉得CFSSL在批量场景下更顺手,尤其是配合模板文件使用时。
3.3 签发证书:让CA为你盖章
有了CA和CSR,签发证书就是最后一步。OpenSSL的命令如下:
openssl x509 -req -in server.csr \
-CA ca-cert.pem -CAkey ca-key.pem \
-CAcreateserial -out server-cert.pem -days 365 \
-extfile server.cnf -extensions v3_req
这里有几个参数需要留意:
- -CAcreateserial:自动创建序列号文件。如果CA已经签发过证书,用
-CAserial ca.srl指定已有序列号文件。 - -extfile 和 -extensions:把CSR配置里的扩展信息(比如SAN)带到证书里。忘了加这个,证书就没有SAN字段。
- -days:有效期。我建议不要超过1年,短生命周期证书更安全。
我的习惯:我会把签发命令封装成一个shell脚本,传入域名和有效期两个参数。这样团队里其他人也能用,不用每次都翻文档。
CFSSL的签发命令更简洁:
cfssl sign -ca ca.pem -ca-key ca-key.pem server.csr | cfssljson -bare server
CFSSL默认会从CSR里读取SAN信息,不需要额外指定扩展文件。但如果你想自定义有效期或策略,可以写一个签名策略文件:
{
"signing": {
"default": { "expiry": "8760h" },
"profiles": {
"server": { "expiry": "8760h", "usages": ["server auth"] },
"client": { "expiry": "8760h", "usages": ["client auth"] }
}
}
}
然后签发时指定profile:
cfssl sign -ca ca.pem -ca-key ca-key.pem -config signing.json -profile server server.csr | cfssljson -bare server
3.4 两种工具的对比
我把OpenSSL和CFSSL的核心差异整理成了一张表,方便你选型时参考:
| 维度 | OpenSSL | CFSSL |
|---|---|---|
| 配置方式 | INI格式配置文件 | JSON格式配置文件 |
| 交互式输入 | 默认交互,需加 prompt=no |
默认非交互 |
| 批量签发 | 需自己写脚本封装 | 原生支持API和批量操作 |
| SAN支持 | 需在配置文件中手动定义 | 自动从CSR的hosts字段读取 |
| 签名策略 | 通过命令行参数控制 | 支持profile机制,灵活度高 |
| 学习曲线 | 中等,参数较多 | 较低,结构清晰 |
我个人建议:如果你只需要偶尔签发几张证书,OpenSSL完全够用。如果你要管理成百上千张证书,或者需要集成到自动化平台里,CFSSL是更好的选择。
3.5 自动化脚本示例
最后,分享一个我实际在用的自动化脚本片段。它用OpenSSL实现「一键签发」,支持通配符域名:
#!/bin/bash
# auto-sign.sh - 自动签发证书
DOMAIN=$1
DAYS=${2:-365}
# 生成私钥和CSR
openssl req -new -newkey rsa:2048 -nodes \
-keyout "${DOMAIN}-key.pem" \
-out "${DOMAIN}.csr" \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/OU=IT/CN=${DOMAIN}" \
-addext "subjectAltName=DNS:${DOMAIN},DNS:*.${DOMAIN}"
# 签发证书
openssl x509 -req -in "${DOMAIN}.csr" \
-CA ca-cert.pem -CAkey ca-key.pem \
-CAserial ca.srl -out "${DOMAIN}-cert.pem" \
-days "${DAYS}" \
-extfile <(echo "subjectAltName=DNS:${DOMAIN},DNS:*.${DOMAIN}")
echo "证书签发完成:${DOMAIN}-cert.pem"
注意:这个脚本里用了 -addext 参数,它需要OpenSSL 1.1.1以上版本。如果你的系统版本较老,建议升级或者改用配置文件方式。