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.

Task
Create keys and CSRs
Platforms
  • Linux
  • macOS
  • Windows
Who it is for
Level
Beginner · 6 steps
On this page
  1. Before you start
  2. Steps
  3. 1. Choose a key type
  4. 2. Create the private key
  5. 3. Create the CSR with its names on one line
  6. 4. Or describe many names in a configuration file
  7. 5. Read the CSR back before you send it
  8. 6. Send the CSR, keep the key
  9. Check it worked
  10. If something goes wrong
  11. Why this works
  12. Go deeper

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.com and www.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
  • -subj sets the subject. Put your main name in CN, 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 in CN.
  • A wildcard such as *.shop.example.com covers exactly one label (api.shop.example.com), never deeper names and never shop.example.com itself. 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:

  1. Check that it matches your key: How to check that a private key matches a certificate.
  2. Check that the CA kept all your names: How to inspect a certificate with OpenSSL.
  3. 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:

Try it: CSR decoderOpen the full tool

Runs in your browser. Nothing you paste is sent anywhere. A CSR is public; never paste the private key that goes with it.

PEM text, or a .csr file dropped on the box.

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 -addext as 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 (or Error adding request extensions …) … duplicate attribute: the configuration file already sets subjectAltName. 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_INVALID for 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 as IP:, not DNS:.

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

Early edition. Checked against the standards; an independent expert review is in progress. Found a mistake? Tell us.

Last reviewed Edition 0.9