Skip to content

Practical technology help for homes and small businesses, from someone who knows how to make it work. Based in Martinez, serving Contra Costa County.

Practical IT guides, cheatsheets, and references from Jeremiah Ellington, drawn from more than 20 years of IT work in public sector and higher education.

Contact Info
Location Martinez, CA (Serving Contra Costa County)
Contact Info
Location Martinez, CA (Serving Contra Costa County)

Cheatsheet

OpenSSL Certificate Inspection and s_client Cheatsheet

OpenSSL commands for reading certificates, requests, CRLs and PFX files, checking a chain, matching a key to its certificate, and testing a live TLS server with s_client, organised around four everyday jobs.

  • Applies to: OpenSSL 3.5 LTS on Linux or macOS. Checked against the OpenSSL 3.5 manual pages (docs.openssl.org/3.5) on 2026-10-27, not run against a live server; other 3.x releases may differ in detail
  • Last reviewed

Free, no email required.

Ask AI about this cheatsheetChatGPT, Claude, or any assistant

These open a new chat with a prompt that points the assistant to this cheatsheet. It only sees the cheatsheet if it can open the link. If it says it can't, use Copy cheatsheet for AI and paste the text into the chat.

Nothing goes to an AI service unless you choose one. AI answers can be wrong: check them against the cheatsheet before you change anything important.

Every example uses made-up people, names and addresses: www.example.com, mail.example.com, dc01.corp.example.com and the documentation address 192.0.2.10. Use files from your own test system, never someone else's server or key. Everything here was checked against the OpenSSL 3.5 manual pages, not lab-tested (October 27, 2026).

OpenSSL: four everyday jobs#

The next four sections each walk through one common job: renewing a certificate with a new key and request, finding out why a site shows the wrong certificate or an incomplete chain, packing a certificate into a PFX for a Windows server, and checking an LDAPS or mail server's certificate against the CA's CRL. Each one starts with a short situation, finds out what's going on with read-only commands, makes the change (each step labelled), confirms it worked, and lists what to try if it didn't. A quick-reference table closes each section.

Nearly every command only reads. The steps marked Writes files create a private key, a PFX or a certificate file; keep key and PFX files secret.

Renew a certificate: new key, new request#

The situation: www.example.com expires next month#

Ravi Shah runs the website for example.com. The certificate on www.example.com expires in four weeks, and the current one is saved as server.pem. The new one has to cover www.example.com and example.com.

Find out what's going on (read-only)#

BashRead-only
openssl x509 -in server.pem -noout -text                  # everything, in readable form
openssl x509 -in server.pem -noout -subject -issuer
openssl x509 -in server.pem -noout -dates                 # notBefore and notAfter
openssl x509 -in server.pem -noout -ext subjectAltName    # the names the certificate covers
openssl x509 -in server.pem -noout -ext extendedKeyUsage,keyUsage,basicConstraints
openssl x509 -in server.pem -noout -serial
openssl x509 -in server.pem -noout -fingerprint -sha256   # without -sha256, x509 uses SHA-1
openssl x509 -in server.cer -inform DER -noout -text      # a binary (DER) file
openssl x509 -in server.pem -noout -checkend 2592000 && echo "Still valid in 30 days"

-checkend takes seconds and exits nonzero if the certificate expires within that time, so it works in a monitoring script.

Plain textSample output (illustrative)
$ openssl x509 -in server.pem -noout -dates -ext subjectAltName
notBefore=Nov 25 00:00:00 2025 GMT
notAfter=Nov 24 23:59:59 2026 GMT
X509v3 Subject Alternative Name:
    DNS:www.example.com, DNS:example.com

Look for: the notAfter date, and every name the site answers to in the Subject Alternative Name.

Make the change#

Writes files: creates a private key. Keep the output files out of shared folders, tickets and version control.

BashWrites files: private keys
# New 2048-bit RSA key and a CSR with a SAN. The key is encrypted; you'll be asked for a passphrase.
openssl req -new -newkey rsa:2048 -keyout www.example.com.key -out www.example.com.csr \
  -subj "/CN=www.example.com" \
  -addext "subjectAltName = DNS:www.example.com,DNS:example.com"

Confirm it worked#

Check the request before you send it to the CA, then check the issued certificate matches the key.

BashRead-only
openssl req -in www.example.com.csr -noout -text -verify   # check the CSR's signature, then show it

Read-only, but these commands read the private key. Compare the hashes: if all three match, the key, the certificate and the request belong together.

BashRead-only (reads the private key, prints only public parts)
openssl x509 -in www.example.com.pem -noout -pubkey | openssl sha256
openssl pkey -in www.example.com.key -pubout | openssl sha256
openssl req -in www.example.com.csr -noout -pubkey | openssl sha256

If it didn't work#

  • The hashes don't match. The certificate was issued for a different request, or you're holding an old key. Find the key that made this CSR, or request again with the key you have.
  • You want to see inside the key. openssl pkey -in server.key -text prints the private key's components. Don't run it where the output could be logged or pasted into a ticket. -pubout limits the output to the public half.

Quick reference: renew a certificate#

CommandWhat it doesRead-only or changes something
openssl x509 -in server.pem -noout -textShows the whole certificateRead-only
openssl x509 -in server.pem -noout -datesShows notBefore and notAfterRead-only
openssl x509 -in server.pem -noout -ext subjectAltNameShows the names the certificate coversRead-only
openssl x509 -in server.pem -noout -fingerprint -sha256SHA-256 fingerprint (SHA-1 without -sha256)Read-only
openssl x509 -in server.pem -noout -checkend 2592000Exits nonzero if it expires within 30 daysRead-only
openssl req -new -newkey rsa:2048 -keyout ... -out ...Creates a private key and a CSRWrites files (private key)
openssl req -in request.csr -noout -text -verifyChecks the CSR's signature, then shows itRead-only
openssl pkey -in server.key -pubout | openssl sha256Hashes the key's public half, to match itRead-only (reads the private key)

Find out why a site shows the wrong certificate#

The situation: one server of two is wrong#

Ravi's new certificate is live, but some visitors to www.example.com still get a certificate warning. The site runs on two servers behind DNS, and one of them is at 192.0.2.10. Ravi needs to know which server is wrong, and how.

Find out what's going on (read-only)#

Read-only, but it connects over the network. Only test servers you run or are allowed to test.

BashRead-only (connects over the network)
openssl s_client -connect www.example.com:443 -servername www.example.com </dev/null
openssl s_client -connect www.example.com:443 -servername www.example.com -brief </dev/null
openssl s_client -connect www.example.com:443 -servername www.example.com -showcerts </dev/null
openssl s_client -connect 192.0.2.10:443 -servername www.example.com </dev/null   # one server by IP, correct SNI
openssl s_client -connect www.example.com:443 -servername www.example.com </dev/null 2>/dev/null |
  openssl x509 -noout -subject -issuer -dates -ext subjectAltName
openssl s_client -connect www.example.com:443 -servername www.example.com -tls1_2 </dev/null    # force TLS 1.2
Plain textSample output (illustrative, trimmed)
$ openssl s_client -connect 192.0.2.10:443 -servername www.example.com -showcerts </dev/null
CONNECTED(00000003)
---
Certificate chain
 0 s:CN=www.example.com
   i:CN=Example Intermediate CA
---
...
Verify return code: 20 (unable to get local issuer certificate)

Look for: whether the subject and dates are the new certificate's, and how many certificates the server sends under Certificate chain. A server certificate with no intermediate after it usually means the server isn't sending its chain. Check the Verify return code line.

What to know about s_client

  • SNI. s_client sends the name from -connect as the SNI if it looks like a DNS name. Connect by IP address and it sends no name, so a server hosting several sites may hand back the wrong certificate. Add -servername whenever you connect by IP, or just always.
  • -showcerts isn't a verified chain. The manual says it "only consists of certificates the server has sent" and is "not a verified chain". It's how you spot a missing intermediate, not proof that the chain is good.
  • Verification failures don't stop the connection unless you add -verify_return_error. Without it, look for the Verify return code line in the output.
  • Check the name too. Add -verify_hostname with the name you expect, so a valid certificate for the wrong name doesn't pass.
  • </dev/null closes the connection once the handshake is done. The manual warns that it "may close the connection prematurely, especially with TLS 1.3". That's fine for looking at the certificate. To test the application, run s_client interactively and type Q to quit.

Make the change#

The fix is on the web server: install the intermediate alongside the certificate, in the way your web server software expects. If the CA gave you the intermediate as a binary (DER) file, convert it first, then check the chain from files before you install it.

BashWrites files: certificates only
openssl x509 -in example-intermediate.cer -inform DER -out example-intermediate.pem   # DER to PEM

-CAfile is the trust anchor (the root). -untrusted holds intermediates that help build the chain but aren't trusted on their own.

BashRead-only
openssl verify -CAfile example-root.pem -untrusted example-intermediate.pem server.pem
openssl verify -CAfile example-root.pem -untrusted example-intermediate.pem \
  -purpose sslserver -verify_hostname www.example.com -show_chain server.pem

Confirm it worked#

BashRead-only (connects over the network)
openssl s_client -connect 192.0.2.10:443 -servername www.example.com \
  -verify_return_error -verify_hostname www.example.com </dev/null

With -verify_return_error, a bad chain or a wrong name stops the handshake. Run the same check against the other server by its IP address too.

If it didn't work#

  • The IP address gives a different certificate from the name. You probably left off -servername, so the server picked its default certificate. Add it and run the check again.
  • -showcerts now lists the intermediate, but verification still fails. -showcerts only shows what the server sent. Check the chain from files with openssl verify ... -show_chain to see which link is missing or out of order.

Quick reference: s_client and chains#

CommandWhat it doesRead-only or changes something
openssl s_client -connect host:443 -servername host </dev/nullConnects with SNI and shows the handshakeRead-only (connects over the network)
... -briefShorter summary of the connectionRead-only (connects over the network)
... -showcertsShows every certificate the server sent (not a verified chain)Read-only (connects over the network)
... -verify_return_error -verify_hostname hostFails on a bad chain or a wrong nameRead-only (connects over the network)
... -tls1_2Forces TLS 1.2Read-only (connects over the network)
s_client ... | openssl x509 -noout -subject -issuer -datesReads the first certificate from s_client's outputRead-only (connects over the network)
openssl verify -CAfile root.pem -untrusted inter.pem server.pemChecks a chain from filesRead-only
openssl x509 -in file.cer -inform DER -out file.pemConverts DER to PEMWrites files (certificate only)

Pack a certificate into a PFX for Windows#

The situation: the same certificate on IIS#

Example.com is adding a Windows server running IIS behind www.example.com. Jo Martin, who looks after it, asks Ravi for the certificate as a PFX. Jo also has an old PFX from a previous project and wants to know what's in it.

Find out what's going on (read-only)#

pkcs12 -info -noout checks and describes the file without writing out any key.

BashRead-only
openssl pkcs12 -in old-server.pfx -info -noout         # structure and algorithms; outputs no keys
openssl x509 -in www.example.com.pem -noout -pubkey | openssl sha256
openssl pkey -in www.example.com.key -pubout | openssl sha256

Look for: matching hashes from the last two lines (the key belongs to the certificate), and the algorithms -info reports for the old file.

Make the change#

Writes files: the PFX holds the private key. Keep it out of shared folders, tickets and version control, and send its password by a different route from the file.

BashWrites files: private keys
# Bundle key, certificate and chain into a PFX (for Windows or IIS). The PFX holds the private key.
openssl pkcs12 -export -inkey www.example.com.key -in www.example.com.pem \
  -certfile example-chain.pem -out www.example.com.pfx

If someone only needs the certificates from a PFX, take them out without the key:

BashWrites files: certificates only
openssl pkcs12 -in server.pfx -nokeys -out server-certs.pem   # certificates out of a PFX, no key

Confirm it worked#

BashRead-only
openssl pkcs12 -in www.example.com.pfx -info -noout

You'll be asked for the PFX password. Then Jo imports it on the Windows server (see Move a certificate to a new server on the Windows sheet).

If it didn't work#

  • The old PFX won't open. In OpenSSL 3.5 the default PFX encryption is AES-256-CBC with PBKDF2. An old PFX that uses older algorithms such as RC2-40 needs -legacy to open.
  • A Windows server rejects a PFX password you know is right. Verify on your system: the OpenSSL manual doesn't say which Windows releases read its AES-256 default, so try the import on the target server before the change window. If it fails, export again with -legacy added to the pkcs12 -export command; the manual says that in legacy mode "the default algorithm for private key encryption is 3DES_CBC".
  • -info lists only one certificate. The intermediate wasn't in example-chain.pem. Rebuild the PFX with the full chain in -certfile.

Quick reference: PFX files#

CommandWhat it doesRead-only or changes something
openssl pkcs12 -in server.pfx -info -nooutDescribes the PFX; outputs no keysRead-only
openssl pkcs12 -export -inkey key -in cert -certfile chain -out file.pfxBundles key, certificate and chainWrites files (private key)
openssl pkcs12 -in server.pfx -nokeys -out certs.pemTakes the certificates out, no keyWrites files (certificates only)
openssl pkcs12 -in old.pfx -info -noout -legacyOpens a PFX that uses older algorithmsRead-only

Check an LDAPS or mail certificate against the CRL#

The situation: was dc01's certificate revoked?#

The CA team at corp.example.com revoked some certificates after a server was decommissioned. Sam Okafor, who runs the Linux LDAP clients, needs to confirm that the certificate dc01.corp.example.com serves for LDAPS isn't on the revocation list, and that the mail server's certificate is fine too.

Find out what's going on#

Writes files: certificate only. First, save the certificate the server sends, so the checks below have a file to read. A certificate is public, so this file needs no special protection.

BashWrites files: certificate only
openssl s_client -connect dc01.corp.example.com:636 -servername dc01.corp.example.com </dev/null 2>/dev/null |
  openssl x509 -out dc01.pem

Everything else in this step only reads.

BashRead-only (connects over the network)
openssl s_client -connect dc01.corp.example.com:636 -servername dc01.corp.example.com </dev/null   # LDAPS (3269 for the global catalog)
openssl s_client -connect mail.example.com:25 -starttls smtp -servername mail.example.com </dev/null
openssl s_client -connect www.example.com:443 -servername www.example.com -status </dev/null    # ask for a stapled OCSP response

-starttls supports smtp, pop3, imap, ftp, xmpp, xmpp-server, irc, postgres, mysql, lmtp, nntp, sieve and ldap.

BashRead-only
openssl x509 -in dc01.pem -noout -serial
openssl x509 -in dc01.pem -noout -ext crlDistributionPoints,authorityInfoAccess
openssl x509 -in dc01.pem -noout -ocsp_uri              # empty if the certificate has no OCSP address
openssl crl -in example-ca.crl -inform DER -noout -issuer -lastupdate -nextupdate
openssl crl -in example-ca.crl -inform DER -noout -text
Plain textSample output (illustrative, invented serial)
$ openssl x509 -in dc01.pem -noout -serial
serial=0123456789ABCDEF

Look for: where the certificate says its CRL lives (download that file as example-ca.crl), whether the CRL's nextUpdate is still in the future, and whether the certificate's serial appears in the CRL's revoked list.

Make the change#

It depends on what the CRL says.

  • The serial is on the CRL. The certificate dc01 serves has been revoked, and the fix is on the server, not on Sam's clients: dc01 needs a new certificate. On Windows, follow Renew a server certificate, then save the new certificate as dc01.pem again, as in the first step.
  • The serial isn't on the CRL. There's nothing to change on the client. Go on to the check below to confirm the chain and the CRL together.

Confirm it worked#

BashRead-only
openssl verify -CAfile example-root.pem -untrusted example-intermediate.pem \
  -crl_check -CRLfile example-ca.crl dc01.pem     # also check the CA's CRL

An OK means the chain is good and the CRL doesn't list the certificate. If the certificate was replaced, OK on the newly saved dc01.pem confirms the replacement.

If it didn't work#

  • crl can't read the file. A binary (DER) CRL needs -inform DER, as above. Leave it off for a PEM file.
  • The check passes on port 636 but a client using the global catalog fails. The global catalog listens on 3269. Run the same s_client check on that port.

Quick reference: revocation, LDAPS and mail#

CommandWhat it doesRead-only or changes something
openssl s_client -connect dc01...:636 -servername dc01... </dev/nullConnects to LDAPS (3269 for the global catalog)Read-only (connects over the network)
openssl s_client -connect mail...:25 -starttls smtp ... </dev/nullUpgrades an SMTP connection to TLS and shows the certificateRead-only (connects over the network)
openssl s_client ... -status </dev/nullAsks for a stapled OCSP responseRead-only (connects over the network)
openssl x509 -in cert.pem -noout -serialShows the serial numberRead-only
openssl x509 -in cert.pem -noout -ocsp_uriShows the OCSP address (empty if none)Read-only
openssl crl -in ca.crl -inform DER -noout -textShows a CRL, including revoked serialsRead-only
openssl verify ... -crl_check -CRLfile ca.crl cert.pemChecks the chain and the CA's CRLRead-only
s_client ... | openssl x509 -out cert.pemSaves the certificate a server sendsWrites files (certificate only)

Sources and official documentation

Review history

  • Technical review: Jeremiah Ellington,
  • Editorial review: Jeremiah Ellington,
  • Next scheduled review:

Spotted something out of date or wrong? Email a correction and I'll fix it. See how these guides are made.