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

> OpenSSL 3.x 生成、加密、解密、查看、比对私钥的常用命令，覆盖 RSA、ECDSA、Ed25519 以及 3.5 新增的 ML-DSA、ML-KEM。

Source: https://ssl.sh/zh/cookbook/generate-keys/
Last updated: 2026-11-10

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 更稳妥。

```bash
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
```

```text
..+.+.....+...+....+..+...+++++++++++++++++++++++++++++++*..+...+......
...+.+............+...+..+++++++++++++++++++++++++++++++*...........+++
```

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

## 用 genrsa 生成 RSA 私钥

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

```bash
openssl genrsa -out example.com.key 3072
head -1 example.com.key
```

```text
-----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，签名速度快得多。

```bash
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
```

```text
-----BEGIN PRIVATE KEY-----
```

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

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

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

```bash
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -out example.com.key
openssl ecparam -list_curves | grep -E "prime256v1|secp384r1|secp521r1"
```

```text
  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。

```bash
openssl genpkey -algorithm ED25519 -out device.key
cat device.key
```

```text
-----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。

```bash
# 生成时直接加密
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
```

```text
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`，要么部署前去掉密码。

## 去掉私钥密码

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

```bash
openssl pkey -in example.com.enc.key -out example.com.key
```

```text
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 里装的就是它。

```bash
openssl pkey -in example.com.key -pubout -out example.com.pub
cat example.com.pub
```

```text
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3ESOpoc5gEeieqQ2KT82rP89m/8d
pKpjwWt960B2G9vF7f/s1cTOn3iPwpPxflR8Z6cmTwDqBHraRNtAD7D8Xg==
-----END PUBLIC KEY-----
```

公钥可以公开。做[公钥固定](https://ssl.sh/zh/glossary/certificate-pinning/)、生成 DANE [TLSA 记录](https://ssl.sh/zh/tools/tlsa-generator/)、核对证书到底是给哪把私钥签的，都要用到它。

## 查看和校验私钥

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

```bash
openssl pkey -in example.com.key -noout -text | head -12
openssl pkey -in example.com.key -noout -check
```

```text
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，三个值一样就是一套。

```bash
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
```

```text
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](https://ssl.sh/zh/errors/nginx-key-values-mismatch/)。[私钥匹配工具](https://ssl.sh/zh/tools/key-matcher/)在浏览器本地做同样的比对。

## 转换成传统格式（PKCS#1 / SEC1）

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

```bash
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
```

```text
-----BEGIN RSA PRIVATE KEY-----
```

PKCS#1（RSA）和 SEC1（EC）是“传统格式”；PKCS#8 用 OID 标明算法，什么密钥都能装，所以新工具都默认它。Ed25519、ML-DSA 只有 PKCS#8 一种格式。DER、PFX 等其他转换见[格式转换命令](https://ssl.sh/zh/cookbook/convert-formats/)和[在线格式转换工具](https://ssl.sh/zh/tools/ssl-converter/)。

## 生成 ML-DSA 私钥（3.5+）

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

```bash
openssl genpkey -algorithm ML-DSA-65 -out pq.key
openssl pkey -in pq.key -noout -text | head -4
```

```text
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](https://ssl.sh/zh/learn/post-quantum-tls/)。

## 生成 ML-KEM 私钥（3.5+）

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

```bash
openssl genpkey -algorithm ML-KEM-768 -out kem.key
openssl list -kem-algorithms | grep -i mlkem
```

```text
  { 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 套件。
