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 之后,我建议你养成一个习惯:先验证,再提交。验证主要看三件事:

  1. 签名是否有效——确保 CSR 没有被篡改。
  2. 公钥是否匹配私钥——防止用错了密钥对。
  3. 身份信息是否正确——特别是 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 生成与验证流程 CSR 生成与验证流程 选择密钥算法 RSA 2048 / 4096 ECC prime256v1 / secp384r1 生成密钥对(私钥) 创建 CSR(含公钥 + 身份信息) 验证 CSR(签名 + 密钥匹配 + 信息) 验证通过 → 提交 CA 验证失败 → 排查重来 算法选择 密钥生成 CSR 创建 验证环节

整个流程其实不复杂,但每一步都有细节。密钥算法选错了,后面全白搭;CSR 里的 SAN 没填,证书下来就是废的;验证步骤跳过了,上线时才发现问题。嗯,这些坑我都踩过,所以现在做批量证书管理时,我会把每一步都写成自动化脚本,人只负责审核结果。

你想想看,如果每次申请证书都要手动敲命令、肉眼核对信息,那 30 张证书还能忍,300 张呢?3000 张呢?所以这一章的内容,本质上是在为后面的批量自动化打基础。把单张 CSR 的生成和验证流程搞扎实了,后面上批量脚本时才不会翻车。