私钥、CSR 与证书

OpenSSL 生成私钥:RSA、ECDSA、Ed25519、ML-DSA

作者: · 最后核实 实测 macOS · OpenSSL 3.6.1

OpenSSL 3.x 用 openssl genpkey 一个命令就能生成各种算法的私钥:RSA、ECDSA、Ed25519,以及 OpenSSL 3.5 新增的 ML-DSA、ML-KEM。下列命令还覆盖私钥的加密、查看和配对检查。输出示例来自 OpenSSL 3.6.1,标“3.5+”的命令在旧版本上会报错。

先用 openssl version 确认版本。macOS 自带的 openssl 其实是 LibreSSL,请改用 Homebrew 装的 $(brew --prefix openssl@3)/bin/openssl;CentOS 7 这类老系统自带 1.0.2,很多参数都不支持。

生成 RSA 私钥

用 genpkey 加 rsa_keygen_bits 指定位数:2048 是公网 CA 的下限,3072 更稳妥。

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out example.com.key
# 3072 或 4096 位
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out example.com.key
..+.+.....+...+....+..+...+++++++++++++++++++++++++++++++*..+...+......
...+.+............+...+..+++++++++++++++++++++++++++++++*...........+++

那一串点和加号是找素数时的进度提示,不是错误;较新的 3.x 可以加 -quiet 关掉。生成的文件以 -----BEGIN PRIVATE KEY----- 开头(PKCS#8)。4096 位生成慢、每次握手也更慢,相比 3072 位收益很小。

用 genrsa 生成 RSA 私钥

网上大多数老教程用的 genrsa 在 3.x 里照样能用,而且输出格式已经和 genpkey 一样。

openssl genrsa -out example.com.key 3072
head -1 example.com.key
-----BEGIN PRIVATE KEY-----

OpenSSL 3.0 之前 genrsa 输出的是 -----BEGIN RSA PRIVATE KEY-----(PKCS#1)。教程里写的头部和你看到的不一样,并不代表私钥坏了,只是外层格式变了。确实需要 PKCS#1 时,见下文“转换成传统格式”。

生成 ECDSA P-256 私钥

P-256(OpenSSL 里叫 prime256v1)是 TLS 的推荐默认:256 位的强度大致相当于 RSA 3072,签名速度快得多。

openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out example.com.key
# 老写法,输出 SEC1 格式 "BEGIN EC PRIVATE KEY"
openssl ecparam -name prime256v1 -genkey -noout -out example.com.key
-----BEGIN PRIVATE KEY-----

两种写法只是文件格式不同,Nginx、Apache、宝塔面板、阿里云和腾讯云的证书上传都能识别。有些老教程会加 -param_enc explicit,别加:显式曲线参数会被很多 TLS 实现拒绝。

生成 P-384 私钥并查看可用曲线

只有合规要求明确写了 P-384 时才需要它,一般网站用 P-256 就够。

openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -out example.com.key
openssl ecparam -list_curves | grep -E "prime256v1|secp384r1|secp521r1"
  secp384r1 : NIST/SECG curve over a 384 bit prime field
  secp521r1 : NIST/SECG curve over a 521 bit prime field
  prime256v1: X9.62/SECG curve over a 256 bit prime field

公网 CA 只接受 P-256、P-384、P-521 三条曲线。列表里还有 secp256k1、Brainpool 等,浏览器不认这些曲线的 TLS 证书。

生成 Ed25519 或 Ed448 私钥

Ed25519 私钥只有几十字节、速度很快,但目前只适合内部 PKI 和自家系统之间的 mTLS。

openssl genpkey -algorithm ED25519 -out device.key
cat device.key
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIPOx/N3NUcmxmTHOy4dbMZNVy+UQ0e5PhLTGx/frIr34
-----END PRIVATE KEY-----

Ed448 把参数换成 -algorithm ED448。截至 2026 年 11 月,CA/Browser Forum 的 Baseline Requirements 只允许 RSA 和 ECDSA,没有公网 CA 签发 Ed25519 服务器证书。Ed25519 没有可选参数,所以不要加 -pkeyopt。

给私钥加密码

写出私钥时加一个加密算法参数即可,常用 AES-256。

# 生成时直接加密
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 \
  -aes-256-cbc -out example.com.key
# 给已有私钥加密
openssl pkey -in example.com.key -aes256 -out example.com.enc.key
head -1 example.com.enc.key
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----BEGIN ENCRYPTED PRIVATE KEY-----

脚本里可以用 -pass pass:密码 或 -passout file:secret.txt 传密码。pass: 会出现在 shell 历史和进程列表里,生产环境优先用 file: 或 env:变量名。加了密码的私钥会让 Nginx 重启时卡在输入密码,要么配 ssl_password_file,要么部署前去掉密码。

去掉私钥密码

读取加密私钥、写出时不带加密参数,就得到无密码版本。

openssl pkey -in example.com.enc.key -out example.com.key
Enter pass phrase for example.com.enc.key:

如果报 Could not find private key ... UI_process:processing error,说明 OpenSSL 没法弹出密码输入(比如在非交互的脚本里),改用 -passin file:secret.txt。宝塔面板、阿里云/腾讯云控制台上传证书时都要求无密码私钥。去密码后记得 chmod 600,不要提交到 Git。

从私钥导出公钥

公钥由私钥推导出来,证书和 CSR 里装的就是它。

openssl pkey -in example.com.key -pubout -out example.com.pub
cat example.com.pub
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3ESOpoc5gEeieqQ2KT82rP89m/8d
pKpjwWt960B2G9vF7f/s1cTOn3iPwpPxflR8Z6cmTwDqBHraRNtAD7D8Xg==
-----END PUBLIC KEY-----

公钥可以公开。做公钥固定、生成 DANE TLSA 记录、核对证书到底是给哪把私钥签的,都要用到它。

查看和校验私钥

-text -noout 打印密钥参数而不输出 PEM,-check 做数学校验。

openssl pkey -in example.com.key -noout -text | head -12
openssl pkey -in example.com.key -noout -check
Private-Key: (256 bit)
priv:
    71:90:82:72:65:87:d7:a5:c1:76:04:09:c2:8f:00:
    ...
pub:
    04:dc:44:8e:a6:87:39:80:47:a2:7a:a4:36:29:3f:
    ...
ASN1 OID: prime256v1
NIST CURVE: P-256
Key is valid

看第一行就知道算法和长度:RSA 显示 Private-Key: (2048 bit, 2 primes),Ed25519 显示 ED25519 Private-Key:。这段输出里的 priv 就是私钥本身,不要贴到工单、群聊或论坛里求助。

检查私钥和证书、CSR 是否配对

分别从私钥、证书、CSR 取出公钥算 SHA-256,三个值一样就是一套。

openssl pkey -in example.com.key -pubout | openssl sha256
openssl x509 -in example.com.crt -noout -pubkey | openssl sha256
openssl req  -in example.com.csr -noout -pubkey | openssl sha256
SHA2-256(stdin)= 1b4738615ecd9017a0dfbb8fe1d9c7654f80c8ee25cac47d06f9f818c9879dd6
SHA2-256(stdin)= 1b4738615ecd9017a0dfbb8fe1d9c7654f80c8ee25cac47d06f9f818c9879dd6
SHA2-256(stdin)= 1b4738615ecd9017a0dfbb8fe1d9c7654f80c8ee25cac47d06f9f818c9879dd6

老教程里的 -modulus | openssl md5 只对 RSA 有效,比对公钥哈希对 RSA、EC、Ed25519、ML-DSA 都适用。在云厂商重新申请证书后忘了换私钥,最常见的后果就是 Nginx 报 key values mismatch。私钥匹配工具在浏览器本地做同样的比对。

转换成传统格式(PKCS#1 / SEC1)

部分老的 Java、PHP 程序和硬件设备只认 BEGIN RSA PRIVATE KEY 或 BEGIN EC PRIVATE KEY,用 -traditional 输出。

openssl pkey -in example.com.key -traditional -out example.com.pkcs1.key
head -1 example.com.pkcs1.key
# 再转回 PKCS#8
openssl pkey -in example.com.pkcs1.key -out example.com.key
-----BEGIN RSA PRIVATE KEY-----

PKCS#1(RSA)和 SEC1(EC)是“传统格式”;PKCS#8 用 OID 标明算法,什么密钥都能装,所以新工具都默认它。Ed25519、ML-DSA 只有 PKCS#8 一种格式。DER、PFX 等其他转换见格式转换命令和在线格式转换工具。

生成 ML-DSA 私钥(3.5+)

ML-DSA(FIPS 204)是 NIST 标准化的后量子签名算法,OpenSSL 3.5 起可以直接用 genpkey 生成。

openssl genpkey -algorithm ML-DSA-65 -out pq.key
openssl pkey -in pq.key -noout -text | head -4
ML-DSA-65 Private-Key:
seed:
    e0:ea:60:31:dd:5d:33:68:6b:ca:d0:c3:41:cb:3b:
    ...

可选参数集有 ML-DSA-44、ML-DSA-65、ML-DSA-87。ML-DSA-65 的公钥 1952 字节、签名 3309 字节,实测用它做的自签证书 DER 有 5520 字节,而 P-256 证书只有 468 字节。现阶段适合实验和内部 PKI,公网证书为什么还没跟上,见后量子 TLS。

生成 ML-KEM 私钥(3.5+)

ML-KEM(FIPS 203)是密钥封装算法而不是签名算法,所以它永远不会出现在 TLS 服务器证书里。

openssl genpkey -algorithm ML-KEM-768 -out kem.key
openssl list -kem-algorithms | grep -i mlkem
  { 2.16.840.1.101.3.4.4.2, id-alg-ml-kem-768, ML-KEM-768, MLKEM768 } @ default
  X25519MLKEM768 @ default
  X448MLKEM1024 @ default
  SecP256r1MLKEM768 @ default

TLS 里 ML-KEM 只用于临时密钥交换:OpenSSL 3.5 起客户端默认 keyshare 就包含混合组 X25519MLKEM768,这类密钥由握手自动生成,不需要手工创建。单独的 ML-KEM 私钥主要用于测试 HPKE 一类的加密或互通性。openssl list -signature-algorithms 和 openssl list -kem-algorithms 可以查看当前版本支持哪些算法;同理,genpkey -algorithm SM2 能生成国密 SM2 私钥,但 OpenSSL 不支持国密 TLS 套件。

常见问题

申请 SSL 证书用哪种私钥最好?

首选 ECDSA P-256:密钥小、握手快,所有公网 CA 都支持。只有要兼容很老的客户端或硬件时才用 RSA 2048/3072。不少站点同时部署一张 ECDSA 和一张 RSA 证书,Nginx 能按客户端能力自动选。

宝塔面板或云厂商控制台提示“私钥格式错误”怎么办?

先确认粘贴的是完整 PEM,包含 BEGIN/END 两行;如果是加了密码的 ENCRYPTED PRIVATE KEY,先用 openssl pkey 去掉密码;个别老面板只认 BEGIN RSA PRIVATE KEY,可用 openssl pkey -traditional 转换。

国密 SM2 私钥能用 OpenSSL 生成吗?

OpenSSL 3.x 可以用 genpkey -algorithm SM2 生成 SM2 密钥,但 OpenSSL 本身不支持 RFC 8998 定义的国密 TLS 套件。要部署国密 HTTPS,一般需要铜锁(Tongsuo)等支持国密的 TLS 库和国密浏览器。

资料来源

  1. OpenSSL 3.5 手册:openssl-genpkey
  2. OpenSSL 3.0 手册:openssl-pkey
  3. OpenSSL 3.0 迁移指南
  4. OpenSSL 3.5 手册:EVP_PKEY-ML-DSA
  5. RFC 5958:Asymmetric Key Packages(PKCS#8)