Verify Certificate Chains with OpenSSL Without Guessing
You will finish with a repeatable way to check that a certificate chains to a trusted CA and that its identity matches the hostname you intend to use. The examples take about 10 minutes and use only temporary files. They do not alter the system trust store or require root.
The route
Jump straight to the step you need, or tick off Done means at the end.
Before you start
Install the OpenSSL package and have a PEM certificate ready. In a normal deployment you will also need the CA certificate that should trust it, plus any intermediate certificates supplied by the issuer. The command accepts one or more certificate files; if you give it none, it reads one certificate from standard input.
There is a version detail on this machine: the installed manpage is for the Ubuntu package version 3.0.13, while the executable found in the current shell reports OpenSSL 3.6.1. Check the binary before relying on a newer option:
openssl version -a
openssl verify -help
Use the manpage installed alongside the binary you deploy. The core commands below are present in both versions.
1. Verify a certificate against a CA file
Pass the CA certificate with -CAfile, then name the certificate being checked. This is the clearest form when you have a specific trust anchor rather than wanting to use the host's default store.
openssl verify \
-CAfile /path/to/trusted-ca.pem \
/path/to/server.pem
Success ends with the certificate path followed by OK:
/path/to/server.pem: OK
A successful chain check covers the certificate path and its verification rules. It does not, by itself, prove that the certificate is suitable for a particular DNS name. Add an explicit hostname check for that.
Checkpoint: confirm the trust source
If the result is unexpectedly good, make sure the CA file is the one you intended. Without -CAfile, OpenSSL can load its configured default CA file, directory and store. Use -no-CAfile, -no-CApath and -no-CAstore when you need to disable those default sources and test only the sources you named. These switches affect this command only; they do not change system configuration.
For a private CA, prefer an explicit file in an access-controlled location. Do not add a private CA to a machine-wide store merely to make one test pass. That changes the trust decision for other software and is a separate administrative action.
2. Supply intermediates without trusting them
An issuing CA is usually an intermediate, not a trust anchor. Give it with -untrusted. OpenSSL may use it to build the chain, but it is not automatically treated as trusted.
openssl verify \
-CAfile /path/to/root-ca.pem \
-untrusted /path/to/intermediate-ca.pem \
/path/to/server.pem
This distinction is a common source of confusing tests. -trusted supplies certificates with trust settings; -untrusted supplies certificates for chain building. If the server certificate is part of a bundle, split out the leaf and intermediates so you know which input is doing what.
Ask OpenSSL to show the chain it built when you need to audit the result:
openssl verify -show_chain \
-CAfile /path/to/root-ca.pem \
-untrusted /path/to/intermediate-ca.pem \
/path/to/server.pem
Typical output includes the leaf at depth 0 and the trust anchor at the greatest depth. Entries introduced from the untrusted input are marked (untrusted). The exact subject formatting depends on the OpenSSL version and name options.
3. Check the identity as well as the chain
For a TLS server certificate, use -verify_hostname with the name a client will send. This checks the certificate identity, including its subject alternative names.
openssl verify \
-CAfile /path/to/root-ca.pem \
-verify_hostname service.example.test \
/path/to/server.pem
Use -verify_ip for an IP address and -verify_email for an email identity. Do not substitute a common name check for the hostname option: the command's verification mode is the useful test of how a TLS client identifies the peer.
To make a harmless local test, create a short-lived certificate whose subject alternative name is service.example.test, then run the command above. With the matching name, expect OK. With a different name, expect a failure similar to:
error 62 at 0 depth lookup: hostname mismatch
error /path/to/server.pem: verification failed
The exit status matters in scripts: success is zero, while a failed verification returns a non-zero status. Capture it immediately if later diagnostic commands must run.
4. Read a failure at the right depth
OpenSSL can report several problems in one run. A diagnostic normally names the certificate, gives an error number, states the depth, and adds a readable reason.
| Depth | Meaning |
|---|---|
| 0 | The target leaf certificate. |
| 1 | The CA that signed the leaf. |
| 2 and above | Higher CAs while walking towards the trust anchor. |
A hostname mismatch at depth 0 points at the identity in the leaf. An invalid CA at depth 1 usually means the supplied intermediate is not a valid issuer for the leaf. An inability to get the local issuer certificate often means that an intermediate is missing or the selected trust source is wrong. Check the certificate subjects and issuers with:
openssl x509 -in /path/to/server.pem -noout -subject -issuer -dates
openssl x509 -in /path/to/intermediate-ca.pem -noout -subject -issuer
Keep the error number when reporting a problem. The text is easier to read, but the number gives you a stable starting point for looking up the matching X509 verification error in the OpenSSL documentation.
Safety boundaries and recovery
Verification is read-only. Options such as -no_check_time, -partial_chain and -ignore_critical change what the command accepts; they are diagnostic exceptions, not repairs. Do not use them in a production health check unless the policy is deliberate and documented. In particular, ignoring certificate time can hide an expired or not-yet-valid certificate.
-crl_download may fetch revocation data from certificate distribution-point entries. Treat that as network activity: it can be slow, unavailable, or subject to the certificate's external URLs. CRL checking is not enabled merely because a certificate contains a CRL pointer; request it explicitly with -crl_check or -crl_check_all and provide suitable CRLs with -CRLfile when that is your policy.
Nothing in these examples changes a service, certificate, key, or trust store. If you accidentally test the wrong CA, correct the command's input paths and rerun it. If you changed a configuration outside this guide while troubleshooting, restore that file from its backup or undo only the documented change; do not delete a system trust directory to force a result.
Done means
- The intended CA was selected explicitly, or the default trust sources were consciously reviewed.
- Intermediates were supplied as
-untrustedinputs rather than silently promoted to trust anchors. - The certificate chain returned
OKwith exit status zero. - The expected hostname, IP address or email identity was checked separately.
- Any exception option or network-based CRL lookup is documented rather than used as a shortcut.