3、证书签发自动化:使用OpenSSL或CFSSL自动签发证书、配置CA、生成CSR

证书签发这件事,说白了就是让机器替你干活。手动生成一个证书不难,但当你面对几百台服务器、几十个环境时,手敲命令就是灾难。我刚开始管证书那会儿,就吃过这个亏——半夜三点被叫起来签发一张紧急证书,手一抖把私钥权限搞错了,结果服务重启失败。嗯,从那以后我就铁了心要搞自动化。

这一章,我们聚焦两个工具:OpenSSL 和 CFSSL。前者是老兵,几乎每台Linux机器都自带;后者是CloudFlare出品的现代方案,适合批量操作。我会把CA配置、CSR生成、证书签发这三个环节拆开讲,每个环节都带上我踩过的坑。

核心目标:用脚本或配置文件,实现「一键签发」证书,不再手动输入任何交互信息。

证书签发自动化核心流程 证书签发自动化核心流程 配置CA 生成CSR 签发证书 关键说明: • 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以上版本。如果你的系统版本较老,建议升级或者改用配置文件方式。