← All tools

SSL Certificate Toolkit

CSR, issued certificate, key match and deployment — the whole procedure on one guided page.

Why this exists

Issuing or renewing a TLS certificate is a standard operating procedure with a standard mistake waiting at every step: a CSR without SANs, a certificate issued for the wrong names, the wrong key file deployed beside it, a chain sent in the wrong order. Each step here verifies the artifact before the next step depends on it.

🔒 Runs in your browser — nothing is sent anywhere

Generate the key and CSR — then verify it

Create the private key and the certificate signing request on the machine that will keep the key. Before sending the CSR to the certificate authority, paste it below — a missing SAN or a 1024-bit key found now costs nothing; found after issuance it costs a reissue.

openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr \
  -subj "/CN=example.com" -addext "subjectAltName=DNS:example.com,DNS:www.example.com"
📖 Step-by-step guide
-newkey rsa:2048
Creates a fresh 2048-bit RSA key in the same command. 2048 is today's floor; 4096 is fine but slower on every handshake.
-nodes
No passphrase on the key. Server keys must be readable at boot without a human typing anything — protect the file with permissions instead: chmod 600 server.key.
-keyout / -out
Where the private key and the CSR land. The key never leaves this machine; only the .csr file goes to the CA.
-subj "/CN=…"
The Common Name. Kept for tradition — browsers ignore it and read only the SANs.
-addext "subjectAltName=…"
Every DNS name the certificate must cover, comma-separated. A name missing here will show a certificate error no matter what the CN says — the single most common CSR mistake.
Then verify
Paste the .csr below and confirm the key size and every SAN before submitting. A wrong CSR found now costs nothing; found after issuance it costs a reissue.

Decode what the CA sent back

When the certificate arrives, read it before installing it. Check the SANs cover every name that will serve traffic, the validity window, and — if the CA bundled intermediates — the chain order.

📖 Step-by-step guide
Where the certificate comes from
A commercial CA emails it or offers a portal download; certbot writes it to /etc/letsencrypt/live/<domain>/. You may get one file or a leaf plus intermediates.
Check the SANs
Every hostname that will serve traffic must be listed. A wildcard (*.example.com) covers one label only — it matches api.example.com but not v2.api.example.com.
Check the validity window
Not-before in the future means a clock problem; public certificates now live at most ~13 months, so note the expiry and plan the renewal.
Check the chain
If intermediates came bundled, paste everything at once — the order findings tell you whether it's leaf-first and whether the root was needlessly included.
From a live server
openssl s_client -connect host:443 -showcerts </dev/null prints exactly the chain a server sends; paste that output here to audit any deployment.

Prove the key and certificate belong together

The classic deploy failure is the right certificate next to the wrong key — nginx refuses to start with “key values mismatch”. Confirm the pair here first; the key never leaves the page.

📖 Step-by-step guide
Why pairs go wrong
A renewal generates a new key but the old one stays deployed; files get copied between servers; server.key and server.key.old get swapped. The filenames look right — the bytes aren't.
How the check works
The public half derived from your private key must equal the public key inside the certificate (or CSR). That comparison happens in your browser's WebCrypto; the key is never transmitted.
On a match
Deploy the pair with confidence — this is exactly the check nginx performs at startup.
On a mismatch
Find the key that made the CSR (openssl rsa -modulus on each candidate key and compare), or reissue the certificate from a CSR made with the key you actually have.
🔒 The private key never leaves this page. It is read by your browser’s own WebCrypto, compared, and discarded — there is no server involved and nothing is transmitted.

Deploy, then verify from outside

Servers send the chain leaf-first: your certificate, then each intermediate, and usually not the root — clients already have the roots. Concatenate in that order, point the server at the pair, and check the result the way a client would.

cat server.crt intermediate.crt > fullchain.pem
# nginx:   ssl_certificate fullchain.pem;  ssl_certificate_key server.key;
# apache:  SSLCertificateFile fullchain.pem  SSLCertificateKeyFile server.key
openssl s_client -connect example.com:443 -servername example.com </dev/null
📖 Step-by-step guide
Build the chain file
fullchain.pem = your certificate first, then each intermediate, no root — clients carry the roots themselves and sending one just wastes every handshake.
nginx
Point ssl_certificate at fullchain.pem and ssl_certificate_key at the key, then nginx -t && systemctl reload nginx. Reload, not restart — reload keeps existing connections alive.
Apache
SSLCertificateFile fullchain.pem, SSLCertificateKeyFile server.key, then apachectl configtest && systemctl reload apache2.
Verify like a client
In the s_client output look for 'Verify return code: 0 (ok)'. The error 'unable to get local issuer certificate' almost always means a missing intermediate — the chain-order trap.
Before you close the ticket
Note the expiry and automate the renewal (certbot's timer, or your CA's reminder). Every expired-certificate outage was a renewal nobody owned.

Paste the s_client output back into step 2 — it prints the chain exactly as your server sends it, so the chain-order findings there are the final check that the deployment is right.