2. OpenSSL 实战:从安装到签发中间CA

说实话,OpenSSL 这个工具,又爱又恨。爱的是它免费、强大,几乎包办了所有证书操作;恨的是它的命令行参数设计得……嗯,有点反人类。我刚开始接触时,经常因为一个参数顺序不对,折腾半天。

但不管怎样,它是我们做证书生命周期自动化的基石。这一章,我们就把它彻底搞明白。

2.1 安装与基础配置

不同系统安装方式略有差异,但核心都一样。我个人习惯在 Linux 环境下操作,生产环境也建议如此。

操作系统 安装命令 备注
Ubuntu/Debian apt install openssl -y 版本通常较新
CentOS/RHEL 7/8 yum install openssl -y RHEL 8 默认自带 1.1.1
CentOS/RHEL 9+ dnf install openssl -y 默认 OpenSSL 3.x
macOS brew install openssl 系统自带版本较老,建议 brew
小提示:安装后务必检查版本:openssl version。我遇到过因为系统自带 1.0.2 版本,导致 TLS 1.3 握手失败的坑。建议至少 1.1.1 以上。

安装完,我们需要一个干净的目录结构。这是我的习惯做法:

mkdir -p ~/pki/{ca,intermediate,server,client,private,crl}
chmod 700 ~/pki/private
touch ~/pki/index.txt
echo 1000 > ~/pki/serial

为什么这么搞?index.txt 是 OpenSSL 的证书数据库,记录所有已签发证书的状态。serial 文件记录下一个证书的序列号。这两个文件,是后面所有自动化操作的基础。

2.2 生成自签名根 CA

根 CA 是整个信任链的起点。说白了,它就是「我是我」的证明。生成根 CA 私钥和证书,我建议用以下参数:

# 1. 生成根 CA 私钥(4096位,更安全)
openssl genrsa -out ~/pki/private/ca.key.pem 4096

# 2. 生成根 CA 证书(自签名,有效期10年)
openssl req -x509 -new -nodes \
  -key ~/pki/private/ca.key.pem \
  -sha256 -days 3650 \
  -out ~/pki/ca/ca.cert.pem \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/CN=My Root CA"
注意:根 CA 私钥必须严格保护。我曾经见过一个团队把根 CA 私钥放在 Git 仓库里……嗯,后果很严重。建议设置文件权限 400,并考虑硬件安全模块(HSM)保护。

这里有个细节:-nodes 参数表示不对私钥加密。为什么?因为自动化脚本里每次输入密码不现实。但如果你手动操作,可以去掉 -nodes,OpenSSL 会提示你输入密码。

2.3 签发中间 CA 证书

根 CA 一般不直接签发服务器证书,而是签发中间 CA。这样做的好处是:即使中间 CA 私钥泄露,我们只需要吊销它,根 CA 仍然安全。

流程分三步:

  1. 生成中间 CA 私钥和证书签名请求(CSR)
  2. 用根 CA 签发中间 CA 证书
  3. 验证证书链

先看第一步:

# 生成中间 CA 私钥
openssl genrsa -out ~/pki/private/intermediate.key.pem 4096

# 生成中间 CA 的 CSR
openssl req -new -sha256 \
  -key ~/pki/private/intermediate.key.pem \
  -out ~/pki/intermediate/intermediate.csr \
  -subj "/C=CN/ST=Beijing/O=MyOrg/CN=My Intermediate CA"

第二步,用根 CA 签发。这里需要创建一个扩展配置文件,指定中间 CA 的用途:

cat > ~/pki/intermediate/openssl_intermediate.cnf <<EOF
[v3_intermediate_ca]
basicConstraints = critical, CA:TRUE, pathlen:0
keyUsage = critical, digitalSignature, keyCertSign, cRLSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
EOF

openssl x509 -req -in ~/pki/intermediate/intermediate.csr \
  -CA ~/pki/ca/ca.cert.pem \
  -CAkey ~/pki/private/ca.key.pem \
  -CAcreateserial \
  -out ~/pki/intermediate/intermediate.cert.pem \
  -days 1825 -sha256 \
  -extfile ~/pki/intermediate/openssl_intermediate.cnf \
  -extensions v3_intermediate_ca
关键点:pathlen:0 表示这个中间 CA 不能再签发下级 CA。如果你需要多级 CA 链,可以设置 pathlen:1 或更大。但生产环境建议 pathlen:0,减少攻击面。

第三步,验证证书链:

openssl verify -CAfile ~/pki/ca/ca.cert.pem \
  ~/pki/intermediate/intermediate.cert.pem

如果输出 intermediate.cert.pem: OK,说明签发成功。

2.4 核心命令行操作

除了签发证书,OpenSSL 还有很多实用操作。我挑几个高频场景:

操作 命令示例 说明
查看证书 openssl x509 -in cert.pem -text -noout 查看证书所有字段
查看 CSR openssl req -in req.csr -text -noout 确认 CSR 内容是否正确
转换格式 openssl x509 -in cert.pem -outform der -out cert.der PEM 转 DER
提取公钥 openssl x509 -in cert.pem -pubkey -noout 获取证书中的公钥
计算指纹 openssl x509 -in cert.pem -fingerprint -sha256 用于校验证书一致性

举个例子,有一次我排查一个 TLS 握手失败问题,发现服务器证书和私钥不匹配。用以下命令快速定位:

# 比较证书公钥和私钥的模数
openssl x509 -in cert.pem -modulus -noout | openssl md5
openssl rsa -in key.pem -modulus -noout | openssl md5

如果两个 MD5 值一致,说明匹配。否则,就是证书和私钥对不上。

2.5 本章知识体系

下面这张图,概括了本章的核心流程和关系:

OpenSSL 证书签发流程 OpenSSL 证书签发流程 根 CA 自签名,有效期10年 中间 CA 由根 CA 签发,pathlen:0 服务器证书 由中间 CA 签发 客户端证书 由中间 CA 签发 签发 签发 签发 关键配置 • basicConstraints: CA:TRUE • keyUsage: keyCertSign • pathlen:0(中间CA不可再签发) 验证命令 openssl verify -CAfile ca.cert.pem intermediate.cert.pem

从图中可以看到,根 CA 是信任锚点,中间 CA 是桥梁,服务器和客户端证书是最终使用者。这个层级结构,就是 PKI 的核心思想。

2.6 避坑指南

最后,分享几个我踩过的坑:

  • 序列号重复: 如果 serial 文件没有正确递增,会导致证书序列号冲突。我曾经因为这个,导致一批证书在浏览器里被判定为重复,全部重新签发。建议用 openssl x509 -serial -noout 定期检查。
  • 时间不同步: 签发证书时,系统时间必须准确。有一次我测试环境时间差了 5 分钟,证书「提前生效」,结果客户端验证失败。NTP 同步是必须的。
  • 扩展属性遗漏: 签发服务器证书时,必须包含 subjectAltName(SAN)。否则 Chrome 会报错。我见过太多人只填了 CN,忘了 SAN。
我的习惯:每次签发完证书,立刻用 openssl x509 -in cert.pem -text -noout | grep -A 1 "Subject Alternative Name" 检查 SAN 字段。确保万无一失。

好了,这一章的内容就到这里。OpenSSL 的命令行操作,说白了就是「私钥生成 → CSR → 签发 → 验证」这个循环。把这个循环练熟了,后面的自动化脚本就是水到渠成的事。