3、证书请求(CSR):生成RSA/ECC密钥对、创建CSR、CSR内容解析与验证
证书请求,也就是 CSR(Certificate Signing Request),是申请证书的「敲门砖」。说白了,它就是你把公钥和一些身份信息打包好,交给 CA 去签名的那个请求文件。
我见过不少运维同学,直接一条 openssl 命令把证书撸出来,从来不看 CSR 里到底写了啥。结果证书下来发现组织名写错了,或者密钥算法不对,又得重新走流程。嗯,这章我们就把它掰开揉碎了讲清楚。
3.1 密钥对生成:RSA 与 ECC 的选择
生成 CSR 之前,你得先有一对密钥。我个人习惯把密钥生成和 CSR 生成分开做,这样密钥可以复用,也方便管理。
3.1.1 RSA 密钥对
RSA 是目前最通用的算法。它的优点是兼容性好,几乎所有 CA 和服务器都支持。缺点是密钥越长,性能越差。
生产建议:现在业界普遍推荐 RSA 2048 位。1024 位已经被证实不安全,4096 位虽然更安全,但握手时 CPU 开销大,日常业务用 2048 位完全够用。
生成 RSA 私钥的命令很简单:
# 生成 RSA 2048 位私钥
openssl genrsa -out server.key 2048
# 查看私钥信息
openssl rsa -in server.key -text -noout
这里有个细节:genrsa 默认不加密私钥文件。如果你需要加密存储,可以加 -aes256 参数。不过在生产环境里,我建议私钥文件本身通过文件权限和 HSM 来保护,而不是依赖密码——密码反而会在自动化部署时带来麻烦。
3.1.2 ECC 密钥对
ECC(椭圆曲线密码学)是 RSA 的替代方案。同样的安全强度下,ECC 的密钥更短、计算更快。举个例子,RSA 2048 位的安全强度,ECC 用 256 位就能达到。
我曾经帮一个客户优化 API 网关的性能,把 RSA 换成 ECC 后,TLS 握手时间直接降了 40%。如果你的业务对延迟敏感,ECC 值得考虑。
# 生成 ECC 私钥(使用 prime256v1 曲线)
openssl ecparam -genkey -name prime256v1 -out server-ecc.key
# 查看 ECC 私钥信息
openssl ec -in server-ecc.key -text -noout
曲线选择:主流曲线是 prime256v1(也叫 secp256r1)和 secp384r1。前者兼容性最好,后者安全强度更高。不建议用 secp521r1,很多老旧客户端不支持。
3.1.3 RSA vs ECC 对比
| 对比维度 | RSA 2048 | ECC 256 (prime256v1) |
|---|---|---|
| 密钥长度 | 2048 位 | 256 位 |
| 安全强度 | 112 位 | 128 位 |
| 签名速度 | 较慢 | 快约 3-5 倍 |
| 验证速度 | 较快 | 稍慢 |
| 兼容性 | 最好 | 主流浏览器/系统均支持 |
| 推荐场景 | 通用场景、老旧系统 | 高性能场景、IoT 设备 |
3.2 创建 CSR
有了密钥对,下一步就是生成 CSR。CSR 里包含你的公钥和身份信息,比如通用名(CN)、组织(O)、国家(C)等。
3.2.1 交互式生成 CSR
最直接的方式是让 openssl 提示你输入信息:
openssl req -new -key server.key -out server.csr
执行后会依次问你国家、省份、城市、组织名、通用名等。这里有个坑:通用名(CN)必须填写你的域名,不能写 IP(除非是内网自签名)。我遇到过有人把公司全名写在 CN 里,结果证书下来浏览器报域名不匹配。
3.2.2 非交互式生成 CSR(推荐)
批量管理时,交互式输入显然不现实。我习惯用配置文件来生成 CSR:
# 创建配置文件 csr.conf
[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
req_extensions = req_ext
prompt = no
[ req_distinguished_name ]
countryName = CN
stateOrProvinceName = Beijing
localityName = Beijing
organizationName = Example Inc.
organizationalUnitName = IT Dept
commonName = www.example.com
emailAddress = admin@example.com
[ req_ext ]
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = www.example.com
DNS.2 = example.com
DNS.3 = api.example.com
然后一行命令生成 CSR:
openssl req -new -key server.key -out server.csr -config csr.conf
重要:从 2020 年起,主流浏览器开始要求证书必须包含 SAN(Subject Alternative Name)。如果你的 CSR 里没有 SAN,CA 签出来的证书可能被 Chrome 标记为「不安全」。所以配置文件里的 subjectAltName 一定要填。
3.2.3 生成 ECC 算法的 CSR
流程完全一样,只是密钥文件换成 ECC 的:
openssl req -new -key server-ecc.key -out server-ecc.csr -config csr.conf
3.3 CSR 内容解析
CSR 不是加密的,它是 Base64 编码的 DER 格式数据。你可以直接用文本编辑器打开看看:
-----BEGIN CERTIFICATE REQUEST-----
MIICzTCCAbUCAQAwgY...
-----END CERTIFICATE REQUEST-----
要解析里面的内容,用这条命令:
openssl req -in server.csr -text -noout
输出会包含以下几部分:
- Subject:申请者的身份信息,就是你在配置文件里填的那些字段。
- Subject Public Key Info:公钥算法和公钥值。这里可以确认是 RSA 还是 ECC。
- Attributes:可选属性,比如挑战密码(challenge password),一般不用。
- Signature Algorithm:CSR 本身的签名算法,用于验证 CSR 的完整性。
举个例子,解析结果大概长这样:
Certificate Request:
Data:
Version: 1 (0x0)
Subject: C = CN, ST = Beijing, L = Beijing, O = Example Inc., OU = IT Dept, CN = www.example.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:b6:7f:2a:...
Exponent: 65537 (0x10001)
Attributes:
(none)
Requested Extensions:
X509v3 Subject Alternative Name:
DNS:www.example.com, DNS:example.com, DNS:api.example.com
Signature Algorithm: sha256WithRSAEncryption
6f:8d:3a:1b:...
3.4 CSR 验证
生成 CSR 之后,我建议你养成一个习惯:先验证,再提交。验证主要看三件事:
- 签名是否有效——确保 CSR 没有被篡改。
- 公钥是否匹配私钥——防止用错了密钥对。
- 身份信息是否正确——特别是 CN 和 SAN。
3.4.1 验证 CSR 签名
openssl req -in server.csr -verify -noout
如果输出 verify OK,说明签名没问题。如果报错,那这个 CSR 就是坏的,别提交。
3.4.2 验证 CSR 与私钥是否匹配
这个步骤很多人会忽略。我曾经遇到过开发同学把 A 服务器的 CSR 和 B 服务器的私钥一起提交,结果证书下来装不上,排查了半天才发现是密钥不匹配。
验证方法很简单,分别提取 CSR 和私钥的 modulus(RSA)或公钥(ECC),对比是否一致:
# RSA:对比 modulus
openssl req -in server.csr -noout -modulus
openssl rsa -in server.key -noout -modulus
# ECC:对比公钥
openssl ec -in server-ecc.key -pubout
openssl req -in server-ecc.csr -noout -pubkey
两个输出一致,就说明 CSR 和私钥是「一对」的。
3.4.3 验证身份信息
用 -text 查看 CSR 内容,人工核对 CN、SAN、组织名等。这一步在批量生成时尤其重要——我曾经因为脚本里的 CN 变量没替换干净,一次生成了 50 个域名全是 example.com 的 CSR,全部作废重来。
自动化验证脚本:在批量流程里,我会写一个简单的 shell 脚本,自动提取 CSR 的 CN 和 SAN,跟预期值做比对。不一致的直接告警,不进入下一步。
3.5 本章知识体系
下面这张图把 CSR 的完整生命周期串起来了:
整个流程其实不复杂,但每一步都有细节。密钥算法选错了,后面全白搭;CSR 里的 SAN 没填,证书下来就是废的;验证步骤跳过了,上线时才发现问题。嗯,这些坑我都踩过,所以现在做批量证书管理时,我会把每一步都写成自动化脚本,人只负责审核结果。
你想想看,如果每次申请证书都要手动敲命令、肉眼核对信息,那 30 张证书还能忍,300 张呢?3000 张呢?所以这一章的内容,本质上是在为后面的批量自动化打基础。把单张 CSR 的生成和验证流程搞扎实了,后面上批量脚本时才不会翻车。