4. 自签名证书实战:从根证书到格式转换

这一章我们直接动手。说实话,自签名证书是理解 PKI 最直观的方式。你想想看,CA 机构签发的证书和自签名证书,底层原理其实一模一样——只是信任的源头不同。我自己带团队时,新人第一周的任务就是搭一套自签名证书体系,能跑通就算入门了。

本章核心脉络:创建根CA → 签发服务器证书 → 配置SAN和Key Usage → 格式转换。每一步都是生产环境里的基本功。

自签名证书实战流程 生成根CA密钥 openssl genrsa 自签根证书 openssl req -x509 生成服务器密钥 openssl genrsa 签发服务器证书 openssl ca 配置扩展属性 SAN / Key Usage 格式转换 PEM ↔ DER ↔ PFX 所有操作基于 OpenSSL 1.1.1+,建议使用 Linux 或 WSL 环境

4.1 创建自签名根证书

根证书是整个信任链的锚点。我习惯先建一个干净的目录结构,方便后续管理。

# 创建目录结构
mkdir -p ~/myca/{certs,crl,newcerts,private}
cd ~/myca
chmod 700 private
touch index.txt
echo 01 > serial

嗯,这里要注意:private 目录权限必须设为 700,根密钥一旦泄露,整个信任体系就崩了。我曾经见过有人把根密钥放在 755 的目录里,结果被 CI 脚本扫到并上传到了制品仓库……那场面,真是灾难。

接下来生成根密钥和自签名根证书:

# 生成根CA密钥(4096位,生产环境别用2048了)
openssl genrsa -out private/ca.key.pem 4096

# 自签名根证书,有效期10年
openssl req -x509 -new -nodes \
  -key private/ca.key.pem \
  -days 3650 \
  -out certs/ca.cert.pem \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=MyLab/CN=MyLab Root CA"

小技巧:-nodes 参数表示不对私钥加密。如果你需要在自动化脚本中使用,建议不加密码;如果是人工保管,可以去掉 -nodes 设置密码保护。

4.2 生成服务器证书(由根CA签发)

服务器证书不能直接用根证书,必须由根CA签发。这是 PKI 的基本规则——根证书只用来签中间证书或终端证书,不直接用于 TLS 握手。

先创建 OpenSSL 配置文件,这是最容易踩坑的地方:

# openssl.cnf
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no

[req_distinguished_name]
C = CN
ST = Beijing
L = Beijing
O = MyLab
CN = myserver.example.com

[v3_req]
keyUsage = keyEncipherment, digitalSignature
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = myserver.example.com
DNS.2 = www.myserver.example.com
IP.1 = 192.168.1.100

然后生成服务器密钥和证书签名请求(CSR):

# 生成服务器密钥
openssl genrsa -out private/server.key.pem 2048

# 生成CSR
openssl req -new \
  -key private/server.key.pem \
  -out server.csr \
  -config openssl.cnf

最后用根CA签发:

openssl ca -config openssl.cnf \
  -cert certs/ca.cert.pem \
  -keyfile private/ca.key.pem \
  -days 365 \
  -in server.csr \
  -out certs/server.cert.pem \
  -extensions v3_req

避坑指南:我曾经遇到过 Chrome 死活不认自签名证书的情况。折腾了半天,发现是 subjectAltName 没配。从 Chrome 58 开始,不配 SAN 的证书一律视为不安全。所以,SAN 不是可选项,是必选项。

4.3 配置证书扩展属性

扩展属性是证书的「能力声明」。两个最关键的扩展:Key Usage 和 Subject Alternative Name (SAN)。

扩展属性 作用 常见取值
Key Usage 声明密钥用途 digitalSignature, keyEncipherment, keyCertSign
Extended Key Usage 声明证书用途 serverAuth, clientAuth, codeSigning
SubjectAltName 指定证书适用的域名/IP DNS:example.com, IP:192.168.1.1
Basic Constraints 标记是否为CA证书 CA:TRUE, CA:FALSE

配置时记住一个原则:最小权限。服务器证书只需要 serverAuth 和 digitalSignature,不要给 keyCertSign——那是CA证书才用的。

4.4 证书格式转换

不同场景需要不同格式。我整理了一张对照表,方便你快速选择:

格式 特点 典型用途
PEM Base64 编码,可包含证书和私钥 Nginx、Apache、Linux 系统
DER 二进制编码,不可读 Java 环境、Windows 导入
PFX/P12 PKCS#12 容器,可加密打包 IIS、Windows 证书存储

转换命令如下:

# PEM → DER(证书)
openssl x509 -in certs/server.cert.pem -outform DER -out certs/server.cert.der

# PEM → PFX(证书+私钥打包,需要设置导出密码)
openssl pkcs12 -export \
  -in certs/server.cert.pem \
  -inkey private/server.key.pem \
  -out certs/server.pfx \
  -passout pass:YourPassword

# DER → PEM
openssl x509 -in certs/server.cert.der -inform DER -out certs/server.cert.pem

# 从PFX提取私钥
openssl pkcs12 -in certs/server.pfx -nocerts -out private/server-from-pfx.key.pem

个人习惯:我一般用 PEM 作为工作格式,只在需要导入 Windows 或 Java 时转成 DER 或 PFX。PEM 是文本格式,方便 diff 和版本控制,出问题也好排查。

4.5 验证证书链

签发完成后,一定要验证证书链是否完整:

# 验证服务器证书是否由根CA签发
openssl verify -CAfile certs/ca.cert.pem certs/server.cert.pem

# 查看证书详情(检查SAN和Key Usage)
openssl x509 -in certs/server.cert.pem -text -noout | grep -A 1 "Subject Alternative Name"

如果输出 server.cert.pem: OK,说明证书链没问题。如果报错,八成是 SAN 没配或者证书路径不对。

再啰嗦一句:自签名证书只适合开发测试环境。生产环境一定要用受信任的 CA 签发的证书,或者搭建内部 CA 并分发根证书到所有客户端。我见过有人把自签名证书直接扔到生产环境,结果第三方接口全部报 TLS 错误——那真是自己给自己挖坑。

好了,这一章的内容就是这些。从根证书创建到格式转换,每一步都是生产环境里反复用到的技能。你可以在自己的笔记本上跟着敲一遍,遇到问题很正常——我当年也是踩了无数坑才把这些命令刻进肌肉记忆的。