第3章 密钥对与证书生成:RSA/ECDSA密钥对、CSR与签名
说实话,TLS 配置里最绕不开的一步,就是生成密钥对和证书。我见过不少运维同事在这环节栽跟头——密钥权限设错了、CSR 信息填反了、签名扩展忘了加……每个坑都够你折腾半天。这一章咱们就把这些基础操作捋清楚,我保证,看完你就能自己动手走完一遍。
3.1 选 RSA 还是 ECDSA?先看这张表
生成密钥对之前,得先定算法。RSA 和 ECDSA 是目前主流的两条路。我个人习惯:如果兼容性排第一,选 RSA;如果性能敏感、终端设备新,选 ECDSA。
| 对比维度 | RSA | ECDSA |
|---|---|---|
| 安全性基础 | 大整数分解 | 椭圆曲线离散对数 |
| 推荐密钥长度 | 2048–4096 位 | 256–521 位 |
| 签名性能 | 较慢 | 快 3–5 倍 |
| 验证性能 | 较快 | 略慢于 RSA |
| 证书体积 | 较大 | 小 30%–40% |
| 兼容性 | 最广泛,覆盖旧设备 | 现代浏览器/系统均支持 |
我的建议:新项目直接上 ECDSA 曲线 prime256v1(也叫 secp256r1),性能和体积都占优。如果客户环境有 Windows Server 2012 或更老的系统,才考虑 RSA 2048。
3.2 生成 RSA 密钥对
RSA 密钥生成,说白了就是让 OpenSSL 帮你找两个大素数。命令很简单,但参数别写错。
# 生成 RSA 私钥(2048 位,带 AES-256 加密保护)
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \
-aes-256-cbc -out server.key
# 从私钥提取公钥
openssl pkey -in server.key -pubout -out server.pub
注意私钥文件生成时会提示你输入密码。有人觉得麻烦直接不加 -aes-256-cbc,我强烈建议别图省事——私钥裸奔,万一服务器被拖库,证书直接就废了。
chmod 600 server.key,确保只有 root 或运行 TLS 服务的用户能读。我曾经在客户现场发现私钥是 644 权限,运维 team 任何人都能 cat 出来…… 嗯,当场就改了。
3.3 生成 ECDSA 密钥对
ECDSA 的密钥比 RSA 短得多,生成速度也更快。关键是要选对曲线。
# 推荐曲线:prime256v1(等效 secp256r1)
openssl genpkey -algorithm EC \
-pkeyopt ec_paramgen_curve:prime256v1 \
-aes-256-cbc -out server.key
# 查看曲线列表(挑你需要的)
openssl ecparam -list_curves | grep -E 'prime|secp'
# 提取公钥(和 RSA 命令一样)
openssl pkey -in server.key -pubout -out server.pub
曲线选型上我踩过坑。以前图新鲜试过 secp521r1,结果某些 CDN 边缘节点不支持,折腾了两天才定位到问题。现在老老实实用 prime256v1,兼容性最好。
· prime256v1 —— 最通用,IoT、CDN、浏览器全覆盖
· secp384r1 —— 合规要求高的场景(如政务、金融)
· secp521r1 —— 除非明确要求,否则别碰
3.4 创建 CSR(证书签名请求)
CSR 就是你去 CA 那里办证书的"申请表"。里面写了你的域名、公司信息、公钥,还有签名。
# 交互式(OpenSSL 会问你一堆问题)
openssl req -new -key server.key -out server.csr
# 非交互式(我推荐这种方式,避免手误)
openssl req -new -key server.key -out server.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=Example Inc./OU=IT/CN=www.example.com"
Subject 字段里最重要的就是 CN(Common Name),必须填域名。不过现在浏览器已经不怎么看 CN 了,真正起作用的是 SAN。所以——
核心要点:生成 CSR 时顺手写好 SAN 扩展。没有 SAN 的证书,Chrome 会直接挂红锁。我见过有团队上线前才发现 SAN 忘加,又重新走了一遍签发流程,耽误了两天。
SAN 扩展通常用一个 ext 文件指定:
# san.ext
subjectAltName = DNS:example.com, DNS:www.example.com, DNS:api.example.com
然后在签名时用 -extfile san.ext 带进去。后面 3.5 节会展示完整命令。
3.5 签名证书:自签名 vs CA 签名
CSR 拿到手,下一步就是签名。自签名适合测试和内网,CA 签名用于生产环境。
自签名证书(测试用)
openssl x509 -req -in server.csr -signkey server.key \
-out server.crt -days 365 \
-extfile san.ext
自签名证书浏览器会报警,因为根证书不在受信任列表里。你需要在客户端手动导入信任。
CA 签名(生产环境)
如果你们公司有自己的内部 CA,或者你从 Let's Encrypt 等外部 CA 申请证书,流程是这样的:
# 用 CA 私钥签署 CSR
openssl x509 -req -in server.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out server.crt -days 365 \
-extfile san.ext
这里 -CAcreateserial 会自动生成序列号文件,避免序列号冲突。如果你把序列号弄重复了,CA 签发的证书可能会出问题,我遇到过类似情况,排查起来很头疼。
| 签名方式 | 适用场景 | 浏览器信任 | 有效期管理 |
|---|---|---|---|
| 自签名 | 开发 / 内网测试 | 否(需手动导入) | 自行控制 |
| 内部 CA 签名 | 企业内部应用 | 是(需部署 CA 根证书) | CA 统一管理 |
| 公共 CA 签名 | 生产环境公网服务 | 是(全球信任) | 通常 90 天–1 年 |
3.6 避坑指南:我替你试过的错
- 私钥加密别省:生成时加 -aes-256-cbc,虽然每次重启服务要输密码,但安全等级高一个档次。可以用 openssl rsa -in server.key -out server.nopass.key 解密后给自动化工具用,注意用完删临时文件。
- CSR 的 CN 和 SAN 必须匹配:Chrome 从 2017 年起就只认 SAN 了。CN 填错了还能改,SAN 漏了证书就废了。我建议生成 CSR 前就把 san.ext 准备好。
- 密钥长度不是越长越好:RSA 4096 比 2048 安全冗余高,但握手性能下降 30%–50%。对于绝大多数场景,RSA 2048 或 ECDSA prime256v1 足够。
- 证书链别漏:CA 签名后拿到的 .crt 往往只包含终端证书,你需要手动拼接中间证书和根证书。少了中间证书,某些客户端会报"证书链不完整"。
- 别忘了备份:私钥和 CSR 文件丢了,证书到期续签时会很麻烦。我用一个加密的 USB 盘做冷备份,每季度更新一次。
如果怀疑私钥被非授权人员访问过,立即吊销证书并重新生成。不要抱有侥幸心理,我曾经处理过一个案例:私钥在 Git 仓库里躺了三个月,直到被安全扫描工具发现才紧急更换,期间所有加密流量理论上都可被解密。
好啦,这一章的内容就是这些。从密钥对生成到 CSR 再到签名,你只要按流程走,每一步盯着参数别写错,基本不会出大问题。下回你遇到同事在终端前犹豫不决,可以直接把这章的步骤丢给他——省得他再踩一遍我踩过的坑。