4、自签名根证书:创建根CA、生成自签名SM2证书、根证书的存储与保护

好,咱们进入实战环节。前面聊了那么多密码学原理和证书链的宏观结构,现在终于要亲手搭建第一个基石了——自签名根证书。

说白了,根证书就是整个信任体系的“祖宗”。它自己证明自己,没有上级CA给它背书。你想想看,如果根证书被人篡改了,那下面签发的所有证书都不可信了。所以这一章,咱们不仅要学会怎么生成它,更要学会怎么把它“供”起来。

4.1 根CA的规划与准备

动手之前,先想清楚几个问题。我在项目中见过不少团队,上来就开干,结果证书体系混乱,最后不得不推倒重来。

根CA的定位:

  • 离线存储:根CA的私钥应该放在离线环境,甚至物理隔离的机器上。日常签发证书用二级CA或三级CA。
  • 长期有效:根证书的有效期通常设10-20年。我习惯设15年,既够长,又留了轮换的余地。
  • 单一职责:一个根CA只做一件事——签发下级CA证书。别用它直接签终端证书。
我的习惯: 我会准备一台不联网的Linux虚拟机,专门用来操作根CA。所有操作都在这台机器上完成,生成的私钥直接写入加密U盘。

4.2 生成SM2根密钥对

咱们用OpenSSL的国密版本来操作。如果你用的是标准OpenSSL 1.1.1以上版本,也支持SM2,但需要显式指定。

先创建目录结构,这是我个人比较喜欢的布局:

# 创建根CA的工作目录
mkdir -p ~/sm2-rootca/{private,certs,crl,newcerts,conf}
cd ~/sm2-rootca

# 初始化索引文件和序列号
touch index.txt
echo '01' > serial

# 生成SM2私钥(加密存储)
openssl ecparam -genkey -name SM2 -out private/ca.key.pem
# 设置私钥权限,只允许当前用户读写
chmod 400 private/ca.key.pem

这里有个关键点:chmod 400。我曾经在一个项目里,因为私钥权限没设好,被安全审计打了个严重漏洞。嗯,从那以后,我每次生成私钥后的第一件事就是改权限。

为什么要加密存储?因为私钥文件本身是明文的。我建议用AES-256-CBC再加密一层:

# 用SM4加密私钥(国密环境推荐)
openssl ec -aes-256-cbc -in private/ca.key.pem -out private/ca.key.enc.pem
# 然后删除明文私钥
shred -u private/ca.key.pem
注意: 加密密码必须足够复杂,并且要离线备份。我见过有人把密码写在便签纸上贴在显示器上……这比不加密还危险。

4.3 生成自签名SM2根证书

有了密钥对,接下来就是生成自签名证书。自签名意味着证书的签发者和使用者是同一个实体。

先准备配置文件。我习惯把配置写清楚,避免每次敲一堆命令行参数:

# conf/root-ca.conf
[ req ]
distinguished_name = req_dn
x509_extensions = v3_ca
prompt = no

[ req_dn ]
countryName = CN
stateOrProvinceName = Beijing
localityName = Haidian
organizationName = My Root CA
organizationalUnitName = Security
commonName = My SM2 Root CA

[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:TRUE, pathlen:1
keyUsage = critical, keyCertSign, cRLSign
extendedKeyUsage = serverAuth, clientAuth

然后生成自签名证书:

openssl req -new -x509 -days 5480 \
    -config conf/root-ca.conf \
    -key private/ca.key.enc.pem \
    -out certs/ca.cert.pem

# 查看证书内容
openssl x509 -in certs/ca.cert.pem -text -noout

-days 5480 大约是15年。为什么选这个数?因为SM2算法在合理安全强度下,15年是一个比较稳妥的周期。太短了要频繁轮换根证书,太长了万一算法被攻破就麻烦了。

关键参数说明:
  • basicConstraints=CA:TRUE:标记为CA证书,这是根证书的核心属性。
  • pathlen:1:限制下级CA只能再签一级证书,防止无限嵌套。
  • keyUsage=keyCertSign, cRLSign:只允许用于签发证书和CRL。

4.4 根证书的存储与保护

证书生成完了,怎么存?我见过最离谱的做法是直接把根证书和私钥放在Web服务器的某个目录下……

我的分级存储方案:

层级 存储介质 存储内容 访问频率
一级(热) 加密U盘(离线) 根私钥(加密)+ 根证书 几乎不访问
二级(温) 保险柜 + 纸质备份 私钥密码 + 证书指纹 紧急时使用
三级(冷) 银行保管箱 完整备份(加密) 灾难恢复

具体操作步骤:

  1. 导出证书的DER格式:openssl x509 -in certs/ca.cert.pem -outform DER -out certs/ca.cert.der
  2. 计算证书指纹:openssl x509 -in certs/ca.cert.pem -fingerprint -sha256,把指纹打印出来,和证书一起存放。
  3. 私钥分片备份:用Shamir秘密共享算法,把私钥拆成5片,任意3片可恢复。这样即使某个保管人出问题,也不至于丢失私钥。
  4. 定期验证:每年从备份中恢复一次,验证证书是否仍然有效,私钥是否损坏。
血的教训: 我曾经有一个客户,根证书私钥存在一个加密U盘里,结果U盘坏了。幸好他们有纸质备份的密码和分片,折腾了两天才恢复。从那以后,我坚持“3-2-1”备份原则:3份副本,2种介质,1份异地。

4.5 根证书的生命周期管理

证书不是生成完就完事了。你得考虑它整个生命周期:

  • 颁发:生成后立即计算指纹,并发布到公开的证书透明度日志(可选)。
  • 使用:根证书只用来签发下级CA证书。每次签发前,检查下级CA的CSR是否合规。
  • 轮换:在根证书到期前1-2年,生成新的根证书,并让所有下级CA信任新根。
  • 撤销:如果私钥泄露,立即生成CRL,并通知所有依赖方。

这里我画了一张根证书生命周期的流程图,帮你理清思路:

根证书生命周期管理流程 生成密钥对 自签名根证书 离线存储私钥 签发下级CA证书 私钥泄露? 到期前1年轮换 生成CRL并撤销 说明: 根证书一旦生成,私钥应 立即离线存储,永不联网。

4.6 验证根证书

最后一步,验证你生成的根证书是否合格。我每次都会跑一遍这个检查清单:

# 1. 检查证书是否自签名
openssl verify -CAfile certs/ca.cert.pem certs/ca.cert.pem
# 输出应为:ca.cert.pem: OK

# 2. 检查基本约束
openssl x509 -in certs/ca.cert.pem -text -noout | grep -A2 "X509v3 Basic Constraints"
# 应显示:CA:TRUE

# 3. 检查密钥用法
openssl x509 -in certs/ca.cert.pem -text -noout | grep -A2 "X509v3 Key Usage"
# 应显示:Certificate Sign, CRL Sign

# 4. 检查签名算法
openssl x509 -in certs/ca.cert.pem -text -noout | grep "Signature Algorithm"
# 应显示:SM2