# OpenSSL CSR commands: SANs, wildcards, config files, renewal

> OpenSSL 3.x commands to create, inspect and verify certificate signing requests with subjectAltName, wildcard names, config files and UTF-8 subjects.

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

A certificate signing request (CSR) is a signed file that carries your public key and the names you want on the certificate; create it with `openssl req` and paste it into the CA's order form. The OpenSSL 3.x commands below cover every common CSR task, from a one-line request with SANs to a renewal CSR. Outputs are from OpenSSL 3.6.1.

The private key never goes to the CA. Keep the `.key` file on the server, and if you use an ACME client such as certbot, it builds the CSR for you.

## Create a CSR interactively

Run `openssl req -new` with an existing key and answer the prompts; press Enter to skip a field or type `.` to leave it empty.

```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 []:
```

Leave Organizational Unit, Email, challenge password and optional company name empty: public CAs ignore or reject them. The interactive mode cannot add SANs, so for anything but a quick test use the one-line form below.

## Create a key and CSR in one line

`-newkey` generates the key, `-noenc` leaves it unencrypted, `-subj` fills the subject and `-addext` adds the SANs.

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

The `-----` line is all you see on success. `-noenc` was added in OpenSSL 3.0 and replaces `-nodes` ("no DES"), which still works but is marked deprecated. On OpenSSL 1.1.0 or older `-addext` does not exist; use a config file instead.

## Add SANs with -addext

Every hostname the certificate must cover goes into one `subjectAltName` value, separated by commas.

```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 has ignored the Common Name since Chrome 58 (2017), and RFC 9525 removed CN matching from the TLS identity rules, so a certificate whose name is only in CN fails with [NET::ERR_CERT_COMMON_NAME_INVALID](https://ssl.sh/errors/net-err-cert-common-name-invalid/). Use `IP:203.0.113.10` for an IP address; only some public CAs issue IP certificates.

## Use a config file for many SANs

A config file is easier to review and reuse when you have many names or need OpenSSL 1.1.0 compatibility.

```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` stops the questions and takes the subject from `[ dn ]`. `req_extensions` is the section copied into the CSR; a common mistake is writing `x509_extensions` there, which only applies when `req -x509` makes a self-signed certificate.

## Create a wildcard CSR

Put `*.example.com` in the SAN list, and add the bare domain too because a wildcard does not cover it.

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

A wildcard matches exactly one label: `*.example.com` covers `www.example.com` but neither `example.com` nor `a.b.example.com`. Public CAs validate wildcard names only through DNS, which for ACME means [dns-01](https://ssl.sh/glossary/dns-01/). Quote the subject in the shell so `*` is not expanded.

## Create a CSR from an existing private key

Use `-key` instead of `-newkey` when you want to keep the current key, for example when a CA asks you to resubmit.

```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
(no output on success)
```

If the key is encrypted you are asked for its passphrase; use `-passin file:secret.txt` in scripts. Reusing a key is allowed, but rotate it if it may have leaked, and never reuse a key that a CA has listed as compromised.

## Create an ECDSA CSR

The CSR's algorithm comes from the key, so an ECDSA key gives an ECDSA CSR and certificate.

```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` is the old way; `-newkey ec -pkeyopt ec_paramgen_curve:P-256` avoids the extra file. To serve RSA and ECDSA side by side, create two keys and two CSRs and order two certificates.

## View the contents of a CSR

`-text -noout` prints the subject, public key and requested extensions in readable form.

```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)` is normal: PKCS#10 only defines version 1. Use `-subject` alone for the name only, or paste the CSR into the [CSR decoder](https://ssl.sh/tools/csr-decoder/) to check it before ordering.

## Verify a CSR and match it to a key

`-verify` checks the CSR's signature; comparing public-key hashes proves which key it belongs to.

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

A CSR damaged by copy and paste (missing lines, Windows line endings in odd places) fails with `unable to load X509 request` or an ASN.1 `wrong tag` error. Make sure the `BEGIN CERTIFICATE REQUEST` and `END CERTIFICATE REQUEST` lines are included.

## Build a renewal CSR from an existing certificate

`openssl x509 -x509toreq` copies the subject from a certificate; add `-copy_extensions copy` or the SANs are lost.

```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` exists from OpenSSL 3.0; without it the new CSR has the subject but no SANs, and the next certificate silently drops `www`. `-key` is the 3.x name for the older `-signkey`. With 1.1.1, rebuild the CSR from a config file instead.

## Use a non-ASCII subject

Add `-utf8` so OpenSSL reads the subject as UTF-8 instead of ASCII, for example a Chinese organization name.

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

Without `-utf8` the bytes are stored as if they were Latin-1 and appear garbled after issuance. Only OV and EV certificates show the organization name, and the CA will use the exact registered name it validated. Keep the CN and SANs in ASCII; internationalized domain names go in as their `xn--` punycode form.

## Choose a signature hash

OpenSSL 3.x signs CSRs with SHA-256 by default, which is what every CA expects.

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

The CSR's own hash only proves you hold the key; the CA chooses the certificate's signature algorithm. There is no reason to change the default, and SHA-1 CSRs are rejected by public CAs. For the full path from CSR to an installed certificate, use the [CSR generator](https://ssl.sh/tools/csr-generator/) or the [inspection recipes](https://ssl.sh/cookbook/inspect-certificates/) to check what you got back.
