How-to guide · Create keys and CSRs
How to create a key and a CSR with OpenSSL (with SANs)
In one sentence
You will have a new private key that never leaves your server, and a verified certificate signing request that lists every name your certificate needs.
- Platforms
- Linux
- macOS
- Windows
- Level
- Beginner · 6 steps
On this page
Before you start
- OpenSSL 1.1.1 or later for the one-line method (
-addext). Older versions need the configuration file in step 4. - Every name the certificate must cover: for example
shop.example.comandwww.shop.example.com. Ask the people who run the DNS and the load balancer. - The machine that will use the certificate, or a secure one close to it. The key is created here and should stay here.
A certificate signing request (CSR) is what you send to a CA: your public key and the names you want, signed with your private key. The CA never needs your private key. If anyone asks you for it, the answer is no.
Steps
1. Choose a key type
- ECDSA P-256: small, fast and supported by every modern client. A good default.
- RSA 3072 (or 2048): works with very old clients and appliances.
Public CAs accept RSA keys of at least 2048 bits and ECDSA keys on the P-256, P-384 and P-521 curves (Baseline Requirements, section 6.1.5). Generate a fresh key for every new certificate, including renewals.
2. Create the private key
Create the key in its own step, so that its file permissions are right from the start:
# ECDSA P-256
(umask 077; openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out shop.key)
# or RSA 3072
(umask 077; openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out shop.key)
ls -l shop.key
head -1 shop.key
-rw------- 1 user user 2484 Oct 20 04:07 shop.key
-----BEGIN PRIVATE KEY-----
umask 077 makes the file readable only by you (on Linux and macOS; Windows file permissions work differently). The key is not encrypted, which servers usually need to start unattended. To protect it with a pass phrase instead, add -aes-256-cbc; then every service start needs that pass phrase. On OpenSSL 3, -quiet hides the progress dots that RSA key generation prints.
3. Create the CSR with its names on one line
openssl req -new -key shop.key \
-subj "/C=BE/O=Example Shop/CN=shop.example.com" \
-addext "subjectAltName=DNS:shop.example.com,DNS:*.shop.example.com,IP:192.0.2.10" \
-out shop.csr
-subjsets the subject. Put your main name inCN, but know that clients ignore it for host names.-addext "subjectAltName=…"sets the Subject Alternative Name list, which is what clients check. List every name, including the one inCN.- A wildcard such as
*.shop.example.comcovers exactly one label (api.shop.example.com), never deeper names and nevershop.example.comitself. Add that name separately. IP:adds an IP address. Public CAs issue those only for public addresses; internal CAs can use them for private ones.
4. Or describe many names in a configuration file
For more than a few names, a file is easier to review and to keep in version control. It contains no secrets. Save it as req.cnf:
[req]
prompt = no
distinguished_name = dn
req_extensions = ext
[dn]
C = BE
O = Example Shop
CN = shop.example.com
[ext]
subjectAltName = @alt
[alt]
DNS.1 = shop.example.com
DNS.2 = www.shop.example.com
DNS.3 = api.example.com
IP.1 = 192.0.2.10
openssl req -new -key shop.key -config req.cnf -out shop.csr
This also works on OpenSSL 1.0.2, which has no -addext. Use the file or -addext for the names, not both: OpenSSL 3 refuses a second SAN list with Error adding extensions defined via -addext (OpenSSL 3.2: Error adding request extensions defined via -addext) and duplicate attribute.
5. Read the CSR back before you send it
openssl req -in shop.csr -noout -text -verify
Certificate request self-signature verify OK
Certificate Request:
Data:
Version: 1 (0x0)
Subject: C=BE, O=Example Shop, CN=shop.example.com
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
...
ASN1 OID: prime256v1
NIST CURVE: P-256
Attributes:
Requested Extensions:
X509v3 Subject Alternative Name:
DNS:shop.example.com, DNS:*.shop.example.com, IP Address:192.0.2.10
Signature Algorithm: ecdsa-with-SHA256
Check three things: self-signature verify OK, every name under Requested Extensions, and the key type and size you chose. The file starts with -----BEGIN CERTIFICATE REQUEST-----.
6. Send the CSR, keep the key
Send shop.csr to the CA: paste it into their web form, upload it, or hand it to your internal PKI team. A CSR is public information. Keep shop.key on the server, back it up the way your organisation backs up secrets, and never email it.
When the certificate comes back:
- Check that it matches your key: How to check that a private key matches a certificate.
- Check that the CA kept all your names: How to inspect a certificate with OpenSSL.
- Build the file your server needs: How to build the full chain file, or a PFX for Windows with How to convert PEM to PFX.
Check it worked
Paste the CSR (never the key) into the CSR decoder. It runs in your browser and shows the subject, the requested names and whether the self-signature verifies:
Then confirm that the CSR carries the public key of the key you will keep. These two values must be equal:
openssl pkey -in shop.key -pubout -outform DER | openssl sha256
openssl req -in shop.csr -noout -pubkey | openssl pkey -pubin -outform DER | openssl sha256
If something goes wrong
subject name is expected to be in the format …in Git Bash: see the warning in step 3.- OpenSSL rejects
-addextas an unknown option: your OpenSSL is older than 1.1.1. Use the configuration file in step 4, or a newer OpenSSL. Error adding extensions defined via -addext(orError adding request extensions …) …duplicate attribute: the configuration file already setssubjectAltName. Set the names in one place only.- The CA rejects the CSR: the key may be too small or of a type the CA does not accept, or a name fails the CA’s checks. Read the CA’s message and step 1 again.
- The certificate arrives without one of your names, and browsers show
NET::ERR_CERT_COMMON_NAME_INVALIDfor it: the name was missing from the SAN list, or the CA could not validate it. Fix the CSR and request again. - Java says
No subject alternative names matching IP address … found: clients connect by IP address, so the IP must be in the SAN list asIP:, notDNS:.
Why this works
The CSR format is PKCS #10, RFC 2986 (PKCS #10). It packs your subject, your public key and the extensions you request into one structure, then signs it with your private key. That signature proves to the CA that you hold the key, without revealing it. The CA validates the names, copies what it accepts into a certificate and signs that with its own key. Asking for a certificate explains the whole exchange, Names and jobs explains SANs and wildcards, and Key pairs explains why the private key never needs to travel.
Go deeper
- OpenSSL
reqmanual:-subj,-addext,-configand the configuration file format. - OpenSSL
genpkeymanual: key types and options. - OpenSSL
x509v3_config: the syntax forsubjectAltNameand other extensions. - RFC 2986: the PKCS #10 certificate request syntax.
- RFC 9525: how clients match host names against the SAN.
- CA/Browser Forum Baseline Requirements: section 6.1.5 on key sizes for public certificates.
Early edition. Checked against the standards; an independent expert review is in progress. Found a mistake? Tell us.