# OpenSSL 生成 CSR：多域名 SAN、通配符、续期

> 用 OpenSSL 3.x 生成、查看、校验证书签名请求（CSR）：-addext 加 SAN、配置文件写多个域名、通配符、中文组织名、从旧证书生成续期 CSR。

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

CSR（证书签名请求）是一个带签名的小文件，里面装着你的公钥和想写进证书的域名，用 `openssl req` 生成后粘贴到 CA 或云厂商的申请页面。下列 OpenSSL 3.x 命令覆盖常见的 CSR 任务，从一行生成带 SAN 的请求，到用旧证书做续期 CSR。输出示例来自 OpenSSL 3.6.1。

私钥永远不交给 CA，`.key` 文件留在自己服务器上。用 certbot、acme.sh 这类 ACME 客户端申请免费证书时，CSR 由客户端自动生成，不需要手工做。

## 交互式生成 CSR

用已有私钥执行 `openssl req -new`，按提示逐项输入；回车跳过，输入 `.` 表示留空。

```bash
openssl req -new -key example.com.key -out example.com.csr
```

```text
You are about to be asked to enter information that will be incorporated
into your certificate request.
...
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:Beijing
Locality Name (eg, city) []:Beijing
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example Ltd
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:example.com
Email Address []:
A challenge password []:
An optional company name []:
```

部门、邮箱、challenge password、optional company name 都留空，公网 CA 用不到，有的还会因此拒单。交互模式加不了 SAN，除了临时测试，建议直接用下面的一行命令。

## 一行命令同时生成私钥和 CSR

`-newkey` 生成私钥，`-noenc` 不加密，`-subj` 填主题，`-addext` 加 SAN。

```bash
openssl req -new -newkey rsa:2048 -noenc \
  -keyout example.com.key -out example.com.csr \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=Example Ltd/CN=example.com" \
  -addext "subjectAltName=DNS:example.com,DNS:www.example.com"
```

```text
-----
```

成功时只打印一行 `-----`。`-noenc` 是 OpenSSL 3.0 新增的写法，取代意为“no DES”的 `-nodes`，后者还能用但已弃用。OpenSSL 1.1.0 及更早版本没有 `-addext`，只能用配置文件。

## 用 -addext 写多个域名

所有要覆盖的主机名都写进同一个 `subjectAltName`，用逗号分隔。

```bash
openssl req -new -key example.com.key -out example.com.csr \
  -subj "/CN=example.com" \
  -addext "subjectAltName=DNS:example.com,DNS:www.example.com,DNS:api.example.com"
openssl req -in example.com.csr -noout -text | grep -A1 "Alternative Name"
```

```text
                X509v3 Subject Alternative Name: 
                    DNS:example.com, DNS:www.example.com, DNS:api.example.com
```

Chrome 从 58 版（2017 年）起就不再看 CN，RFC 9525 也把 CN 匹配从 TLS 身份规则里删掉了。域名只写在 CN 里的证书会报 [NET::ERR_CERT_COMMON_NAME_INVALID](https://ssl.sh/zh/errors/net-err-cert-common-name-invalid/)，国内的 360、QQ 浏览器等 Chromium 内核浏览器同样如此。IP 地址写成 `IP:203.0.113.10`，但只有部分公网 CA 签 IP 证书。

## 用配置文件写大量 SAN

域名多、要反复使用，或者服务器上还是 OpenSSL 1.1.0 时，配置文件更清楚。

```ini
# san.cnf
[ req ]
prompt             = no
default_md         = sha256
distinguished_name = dn
req_extensions     = v3_req

[ dn ]
C  = CN
O  = Example Ltd
CN = example.com

[ v3_req ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = example.com
DNS.2 = www.example.com
DNS.3 = api.example.com
IP.1  = 203.0.113.10
```

```bash
openssl req -new -newkey rsa:2048 -noenc -keyout example.com.key \
  -out example.com.csr -config san.cnf
openssl req -in example.com.csr -noout -text | grep -A1 "Alternative Name"
```

```text
                X509v3 Subject Alternative Name: 
                    DNS:example.com, DNS:www.example.com, DNS:api.example.com, IP Address:203.0.113.10
```

`prompt = no` 让 OpenSSL 不再提问，直接取 `[ dn ]` 里的值。`req_extensions` 指定写进 CSR 的扩展段；常见错误是写成 `x509_extensions`，那个只在 `req -x509` 直接生成自签证书时生效，CSR 里就会没有 SAN。

## 生成通配符证书的 CSR

SAN 里写 `*.example.com`，同时把根域名也写上，因为通配符不包含根域名。

```bash
openssl req -new -key example.com.key -out wildcard.csr \
  -subj "/CN=*.example.com" \
  -addext "subjectAltName=DNS:*.example.com,DNS:example.com"
openssl req -in wildcard.csr -noout -subject
```

```text
subject=CN=*.example.com
```

通配符只匹配一级：`*.example.com` 能覆盖 `www.example.com`，覆盖不了 `example.com` 和 `a.b.example.com`。公网 CA 只能通过 DNS 验证通配符，ACME 里就是 [dns-01](https://ssl.sh/zh/glossary/dns-01/)，域名托管在 DNSPod、阿里云解析时可以用对应的 API 自动添加 TXT 记录。命令里的主题要加引号，否则 shell 会展开 `*`。

## 用已有私钥生成 CSR

想保留现有私钥（比如 CA 要求重新提交 CSR）时，把 `-newkey` 换成 `-key`。

```bash
openssl req -new -key example.com.key -out example.com.csr \
  -subj "/CN=example.com" -addext "subjectAltName=DNS:example.com,DNS:www.example.com"
```

```text
（成功时没有输出）
```

私钥加了密码会提示输入，脚本里用 `-passin file:secret.txt`。复用私钥是允许的，但只要怀疑泄露过（比如被打包进了网站源码压缩包），就必须换新私钥。

## 生成 ECDSA 的 CSR

CSR 的算法由私钥决定：ECDSA 私钥生成 ECDSA CSR，签出来的也是 ECDSA 证书。

```bash
openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:P-256 -noenc \
  -keyout example.com.key -out example.com.csr \
  -subj "/CN=example.com" -addext "subjectAltName=DNS:example.com,DNS:*.example.com"
openssl req -in example.com.csr -noout -text | grep -E "Public Key Algorithm|Signature Algorithm"
```

```text
            Public Key Algorithm: id-ecPublicKey
    Signature Algorithm: ecdsa-with-SHA256
```

老教程常写 `-newkey ec:params.pem`，需要先生成参数文件；`-newkey ec -pkeyopt ec_paramgen_curve:P-256` 一步到位。想同时部署 RSA 和 ECDSA 双证书，就生成两把私钥、两个 CSR，买两张证书。

## 查看 CSR 内容

`-text -noout` 以可读形式打印主题、公钥和申请的扩展。

```bash
openssl req -in example.com.csr -noout -text
```

```text
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: C=CN, ST=Beijing, L=Beijing, O=Example Ltd, CN=example.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                ...
        Attributes:
            Requested Extensions:
                X509v3 Subject Alternative Name: 
                    DNS:example.com, DNS:www.example.com
    Signature Algorithm: sha256WithRSAEncryption
```

`Version: 1 (0x0)` 是正常的，PKCS#10 只有这一个版本。只看主题用 `-subject`；提交前也可以把 CSR 贴进 [CSR 解析工具](https://ssl.sh/zh/tools/csr-decoder/)确认域名没写错。

## 校验 CSR 并确认对应的私钥

`-verify` 校验 CSR 的签名，比对公钥哈希可以确认它属于哪把私钥。

```bash
openssl req -in example.com.csr -noout -verify
openssl req -in example.com.csr -noout -pubkey | openssl sha256
openssl pkey -in example.com.key -pubout | openssl sha256
```

```text
Certificate request self-signature verify OK
SHA2-256(stdin)= 8c6044a61e57fc591b9842bbdb3dbde4c074e8257ba907889023a000ed166588
SHA2-256(stdin)= 8c6044a61e57fc591b9842bbdb3dbde4c074e8257ba907889023a000ed166588
```

复制粘贴时少了行、混进全角字符或多余空格，会报 `unable to load X509 request` 或 ASN.1 `wrong tag`。确认 `BEGIN CERTIFICATE REQUEST` 和 `END CERTIFICATE REQUEST` 两行都在，从微信、QQ 里转发过的 CSR 尤其要检查。

## 从旧证书生成续期 CSR

`openssl x509 -x509toreq` 能从现有证书复制主题，必须加 `-copy_extensions copy`，否则 SAN 会丢。

```bash
openssl x509 -x509toreq -in example.com.crt -key example.com.key \
  -copy_extensions copy -out renew.csr
openssl req -in renew.csr -noout -text | grep -A1 "Alternative Name"
```

```text
                X509v3 Subject Alternative Name: 
                    DNS:example.com, DNS:www.example.com, IP Address:203.0.113.10
```

`-copy_extensions` 从 OpenSSL 3.0 才有。不加它，新 CSR 只有主题没有 SAN，签出来的证书可能悄悄少了 `www`。`-key` 是 3.x 对老参数 `-signkey` 的新名字。1.1.1 上请改用配置文件重新生成。

## 使用中文组织名

加 `-utf8` 让 OpenSSL 按 UTF-8 读取主题，否则中文公司名会变成乱码。

```bash
openssl req -new -key example.com.key -utf8 \
  -subj "/C=CN/O=示例科技有限公司/CN=example.com" -out zh.csr
openssl req -in zh.csr -noout -subject
```

```text
subject=C=CN, O=示例科技有限公司, CN=example.com
```

不加 `-utf8` 时字节会被当成 Latin-1 存进去，签发后显示成乱码。只有 OV、EV 证书会显示公司名，CA 最终会用它核验过的工商注册名称（通常与营业执照一致）。CN 和 SAN 保持英文；中文域名要先转成 `xn--` 开头的 Punycode 再写入。

## 选择签名哈希

OpenSSL 3.x 默认用 SHA-256 给 CSR 签名，所有 CA 都接受，一般不用改。

```bash
openssl req -new -key example.com.key -sha384 -subj "/CN=example.com" -out example.com.csr
openssl req -in example.com.csr -noout -text | grep "Signature Algorithm"
```

```text
    Signature Algorithm: sha384WithRSAEncryption
```

CSR 自身的哈希只用来证明你持有私钥，证书用什么签名算法由 CA 决定。没有理由改默认值，SHA-1 签名的 CSR 会被公网 CA 拒绝。也可以用 [CSR 生成工具](https://ssl.sh/zh/tools/csr-generator/)在浏览器里生成；拿到证书后，用[证书查看命令](https://ssl.sh/zh/cookbook/inspect-certificates/)核对域名和有效期。
