SSL Certificate Toolkit
CSR, issued certificate, key match and deployment — the whole procedure on one guided page.
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.
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.
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.