Intro
This practical guide shows how to inspect, validate, troubleshoot, and safely convert TLS certificates with OpenSSL 3.x. It distinguishes four different checks that are often confused:
- Parsing a certificate file (structure and fields)
- Validating a certificate chain (trust anchor, intermediates, and purpose)
- Verifying a hostname (SAN-based identity check)
- Completing a TCP/TLS connection (handshake success)
You will find copyable commands, what each one proves, what it does not prove, and safety notes to avoid losing keys or exposing secrets.
1) Identify your OpenSSL and environment
Options and defaults vary by build and version. OpenSSL 1.1.1 is end-of-life; prefer OpenSSL 3.x. Record your environment before running diagnostics:
openssl version -a
Expected evidence: OpenSSL version, build flags, OPENSSLDIR, providers, and default trust locations. These affect verification behavior and available options like -verify_hostname.
Table: Version and environment inventory
| What to record | Example evidence | Why it matters |
|---|---|---|
| OpenSSL version | OpenSSL 3.0.13 | Determines command options and verification behavior |
| Build config | Options with FIPS provider | Explains provider availability and algorithms allowed |
| OPENSSLDIR | OPENSSLDIR: \/etc\/ssl | Location for default CAfile and CApath |
| Default trust | CAfile and CApath values | Affects s_client and verify trust decisions |
| OS and shell | Linux x86_64, bash | Reproducibility of commands and paths |
2) Inspect a local certificate (PEM)
Parse a PEM certificate and focus on identity and validity periods. Modern hostname identity uses SAN, not Common Name alone.
openssl x509 -in <certificate.pem> -noout \
-subject -issuer -serial -dates \
-fingerprint -sha256 \
-ext subjectAltName
- subject: Descriptive identity fields
- issuer: Who signed it
- serial: Unique per issuer
- dates: notBefore and notAfter validity window
- fingerprint with -sha256: SHA-256 fingerprint for tracking
- subjectAltName: List of DNS names and IPs the certificate is valid for (primary source for hostname checks)
Verification step: confirm that your target hostname is present in DNS entries within SAN.
3) Check expiration windows without guessing renewal policy
Test whether a certificate will remain valid for at least N seconds. Example: 30 days is 2592000 seconds.
openssl x509 -checkend 2592000 -noout -in <certificate.pem>
echo $?
- Exit status 0: will NOT expire within 30 days
- Non-zero: expires within 30 days or an error occurred
Verification step: if non-zero, confirm the actual notAfter date with the command in section 2.
4) Inspect a remote endpoint with s_client (handshake and presented chain)
Use Server Name Indication (SNI) to select the correct virtual host. Ask for the full chain as served, and request a nonzero exit code on verification errors. The input redirection closes stdin so the command exits cleanly.
openssl s_client -connect <host>:443 -servername <host> \
-showcerts -verify_return_error </dev/null
What to look for:
- Certificate chain as presented by the server (may be incomplete)
- Handshake completion vs. verification status
- Whether the leaf certificate matches the expected hostname (not checked automatically without -verify_hostname)
Notes:
- Trust-store selection depends on your OpenSSL build and OS defaults. You can override with -CAfile or -CApath when reproducing issues.
- A successful handshake or a displayed certificate does not, by itself, prove identity or trust.
5) Explicit hostname verification (OpenSSL 3.x)
First, confirm your s_client supports -verify_hostname.
openssl s_client -help 2>&1 | grep -i verify_hostname || echo "Option not supported"
If supported (OpenSSL 3.x recommended):
openssl s_client -connect <host>:443 -servername <host> \
-verify_hostname <host> -verify_return_error </dev/null
Expected evidence: verification OK when SAN includes the hostname and the chain validates to a trusted anchor. If verification fails, s_client returns a nonzero status and prints the failing reason.
6) Validate a local chain (trust anchor vs. intermediates)
Separate the trust anchor (root CA) from untrusted intermediates and validate the leaf.
openssl verify -CAfile <root-ca.pem> -untrusted <intermediate.pem> <leaf.pem>
Expected evidence: <leaf.pem>: OK. If it fails, reasons commonly include expired intermediates, wrong order, missing intermediate, or an untrusted root.
Important distinctions:
- Chain validation does not check hostnames; it checks signatures, time, and trust to an anchor.
- Purpose checks (e.g., sslserver vs sslclient) are separate; use -purpose sslserver if you need that explicit verification.
7) Inspect a CSR safely
openssl req -in <request.csr> -noout -text -verify
- The -verify flag confirms the CSR is self-signed by the embedded public key, proving possession of the corresponding private key when the CSR was created.
- It does not prove the requester’s identity or that any CA will sign it.
Verification step: confirm requested SAN values and key usage extensions match your intended issuance policy.
8) Recognize and convert certificate formats without overwriting
Always create a new file and verify before replacing the original.
- Recognize a certificate:
openssl x509 -in <file> -noout -subject - Recognize a private key without exposing it:
openssl pkey -in <key.pem> -noout
Table: Certificate format and conversion matrix (non-destructive)
| From | To | Command | Verification step |
|---|---|---|---|
| PEM certificate | DER certificate | openssl x509 -in <certificate.pem> -outform DER -out <certificate.der> | openssl x509 -in <certificate.der> -inform DER -noout -subject |
| DER certificate | PEM certificate | openssl x509 -in <certificate.der> -inform DER -out <certificate.pem.new> | openssl x509 -in <certificate.pem.new> -noout -subject |
| PKCS#12 bundle | PEM leaf cert | openssl pkcs12 -in <bundle.p12> -clcerts -nokeys -out <leaf-cert.pem> | openssl x509 -in <leaf-cert.pem> -noout -subject |
| PKCS#12 bundle | Encrypted PEM key | openssl pkcs12 -in <bundle.p12> -nocerts -out <key.pem> | openssl pkey -in <key.pem> -noout |
| PEM cert and key | PKCS#12 bundle | openssl pkcs12 -export -inkey <key.pem> -in <certificate.pem> -out <bundle.p12> | openssl pkcs12 -in <bundle.p12> -info -nokeys |
9) PKCS#12 inspection and export, securely
Avoid putting passwords directly on the command line; they can leak via shell history or process listings. Prefer interactive prompts, or use protected mechanisms provided by your environment.
- Inspect certificates inside a PKCS#12 without printing private keys:
openssl pkcs12 -in <bundle.p12> -info -nokeys
- Export only the leaf certificate (no keys):
openssl pkcs12 -in <bundle.p12> -clcerts -nokeys -out <leaf-cert.pem>
- Export an encrypted private key (you will be prompted for an output passphrase):
openssl pkcs12 -in <bundle.p12> -nocerts -out <key.pem>
Verification step: parse outputs with openssl x509 or openssl pkey as shown, and confirm file permissions restrict access to the private key.
10) Match a certificate to its private key without exposing the key
Derive public-key fingerprints from both sides and compare. They must be identical.
- From the certificate:
openssl x509 -in <certificate.pem> -noout -pubkey | openssl sha256
- From the private key:
openssl pkey -in <key.pem> -pubout | openssl sha256
Expected evidence: the two SHA-256 digests match. Caveats:
- The older -modulus method only applies to RSA, not ECDSA or Ed25519.
- Do not display private-key material; extracting a public key from it is sufficient for matching.
11) What each command proves (and doesn’t)
Table: Common inspection commands and their limits
| Command | Proves | Does not prove |
|---|---|---|
| openssl x509 -in <certificate.pem> -noout -text | Certificate fields, SAN, validity window | Trust to a root, hostname ownership, live connectivity |
| openssl x509 -checkend N -in <certificate.pem> | Time until expiration crosses N | Whether a CA will reissue, application reload behavior |
| openssl s_client -connect -servername -showcerts | TLS handshake succeeds, presented chain | Hostname validation unless -verify_hostname is used, app-specific trust policy |
| openssl s_client -verify_hostname <host> | Hostname identity check with SAN and chain | That your OS or app uses the same trust store or cipher policy |
| openssl verify -CAfile -untrusted <leaf.pem> | Signature chain to trust anchor, time, purpose if specified | Hostname match, live server configuration |
| openssl req -in <request.csr> -verify | CSR integrity and key possession at creation | Requester identity or CA approval |
12) Troubleshoot common TLS failures
Start with read-only inspection. Do not modify files until you know the cause.
Table: TLS failure symptom, likely cause, first safe diagnostic
| Symptom | Likely cause | First safe diagnostic |
|---|---|---|
| Handshake OK, browser shows name mismatch | Wrong SAN or wrong virtual host | openssl s_client -connect <host>:443 -servername <host> -verify_hostname <host> </dev/null |
| Handshake fails with certificate expired | Expired or not yet valid certificate | openssl x509 -in <certificate.pem> -noout -dates and -checkend N |
| Unknown CA or self-signed error | Untrusted root or missing trust anchor | openssl verify -CAfile <root-ca.pem> <leaf.pem> |
| Works on some clients, not others | Missing or wrong intermediate on server | openssl s_client -connect <host>:443 -showcerts </dev/null |
| Correct cert, still wrong site | Missing -servername SNI in client or server misrouting | openssl s_client -connect <host>:443 -servername <host> </dev/null |
| Sporadic failures by time of day | System clock skew | date on client and server; check notBefore and notAfter |
13) Safe operational practices
- Start with read-only parsing and verification; preserve originals and permissions.
- Never replace a working certificate or key without creating a new file and verifying it first.
- Restrict key access with filesystem permissions; audit who can read private keys.
- Do not paste private keys or full PKCS#12 contents into tickets or logs.
- Confirm application reload behavior separately: some servers require a restart to use a new certificate or key.
- Document the trust anchor and intermediate chain explicitly in deployment automation.
- Remember that openssl s_client is a diagnostic tool; it does not exactly reproduce every application’s TLS stack, trust store, or policy.
14) Pre-deployment and post-deployment checklist
Table: Deployment checks you can verify quickly
| Phase | Checklist item | Evidence |
|---|---|---|
| Before deploy | OpenSSL 3.x available and recorded | openssl version -a captured in logs |
| Before deploy | Leaf cert SAN includes the target hostnames | openssl x509 -in <certificate.pem> -noout -ext subjectAltName |
| Before deploy | Chain validates to intended trust anchor | openssl verify -CAfile <root-ca.pem> -untrusted <intermediate.pem> <leaf.pem> |
| Before deploy | Key matches the certificate | Matching SHA-256 of derived public keys |
| After deploy | Server presents complete chain | openssl s_client -connect <host>:443 -servername <host> -showcerts </dev/null |
| After deploy | Hostname verification succeeds | openssl s_client -connect <host>:443 -servername <host> -verify_hostname <host> </dev/null |
| After deploy | App reload or restart confirmed | App logs and versioned config audit |
Conclusion
When diagnosing TLS, treat parsing, chain validation, hostname verification, and the network handshake as distinct steps. Start with version inventory, inspect local files safely, then test the live endpoint with SNI and explicit hostname checks. Keep conversions non-destructive, never expose private keys, and verify application reload behavior independently. With these OpenSSL 3.x commands and checklists, you can move from symptoms to evidence-backed fixes quickly and safely.