Keys, CSRs and certificates

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

By · Last verified Tested macOS · OpenSSL 3.6.1

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.

openssl req -new -key example.com.key -out example.com.csr
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.

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

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.

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"
                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. 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.

# 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
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"
                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.

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

openssl req -new -key example.com.key -out example.com.csr \
  -subj "/CN=example.com" -addext "subjectAltName=DNS:example.com,DNS:www.example.com"
(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.

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"
            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.

openssl req -in example.com.csr -noout -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 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.

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

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"
                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.

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

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"
    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 or the inspection recipes to check what you got back.

FAQ

Do I still need to fill in country, state and organization?

For DV certificates no: the CA only validates the domain names and drops or ignores the other subject fields. For OV and EV certificates the CA verifies the organization details separately, and the CSR fields are usually overwritten with the validated data. The OU field has been forbidden in public TLS certificates since September 2022.

Should the CSR's Common Name include www?

It does not matter for browsers, which only read the SAN list. Put your main hostname in CN for readability and list every hostname, with and without www, as DNS SAN entries.

Can I reuse the same CSR for every renewal?

Technically yes, because the CSR only carries the public key and names. Generating a new key and CSR at each renewal is better practice, and ACME clients such as certbot do this by default.

Sources

  1. OpenSSL manual: openssl-req
  2. OpenSSL 3.0 manual: x509v3_config
  3. OpenSSL 3.0 manual: openssl-x509
  4. RFC 2986: PKCS #10 Certification Request Syntax
  5. RFC 9525: Service Identity in TLS