I'm trying to validate/verify that the rsa key, ca-bundle, and certificate stored here are ok. They are not being served by a webserver. How can I verify them?
5 Answers
Assuming your certificates are in PEM format, you can do:
openssl verify cert.pem
If your "ca-bundle" is a file containing additional intermediate certificates in PEM format:
openssl verify -untrusted ca-bundle cert.pem
If your openssl isn't set up to automatically use an installed set of root certificates (e.g. in /etc/ssl/certs
), then you can use -CApath
or -CAfile
to specify the CA.

- 2,927
-
14Warning, the openssl verify command is more permissive than you might expect! By default, in addition to checking the given CAfile, it also checks for any matching CAs in the system's certs directory e.g. /etc/ssl/certs. To prevent this behavior and make sure you're checking against your particular CA cert given by CAfile, you must also pass a -CApath option with a non-existant directory, e.g.:
openssl verify -verbose -CApath nosuchdir -CAfile cacert.pem server.crt
– DSimon Jan 20 '16 at 19:35 -
2One further caveat: If you use
-CApath nosuchdir
then the combination of server.crt and cacert.pem must include the root CA; if openssl can only work up to an intermediate CA with those files then it will complain. – DSimon Jan 21 '16 at 16:32 -
Or instead you can use flag designed for this:
openssl verify -verbose -no-CAfile -no-CApath -untrusted ca.pem server.pem
. Though don't use it for checking a bundle,verify
only works for first certificate inserver.pem
. – subtleseeker Jun 08 '21 at 11:32 -
@subtleseeker: but only in and after OpenSSL 1.1.0 released Aug. 2016 – dave_thompson_085 May 12 '23 at 00:53
Here is one-liner to verify a certificate chain:
openssl verify -verbose -x509_strict -CAfile ca.pem -CApath nosuchdir cert_chain.pem
This doesn't require to install CA anywhere.
See https://stackoverflow.com/questions/20409534/how-does-an-ssl-certificate-chain-bundle-work for details.
Update
As noted by Klaas van Schelven, the answer above is misleading as openssl appears to verify only single top certificate per file. So it's necessary to issue multiple verify commands for each certificate chain node placed in separate file.

- 381
-
No problem, glad to help. :-) One caveat that I figured out after posting my earlier comment: if the file specified with
-CAfile
is itself just an intermediate certificate, then openssl will complain. This is correct behavior, sinceverify
requires a complete chain all the way up to a root CA, but can be misleading. – DSimon Jan 21 '16 at 16:30 -
5My version (
OpenSSL 1.1.1 11 Sep 2018
) requires the argument to-CApath
to be an existing directory. – Jake Cobb Jul 23 '19 at 15:58 -
1This answer is misleading, because "openssl verify" does not in fact verify more than 1 certificate per file, and thus cannot be used on bundles. See https://stackoverflow.com/a/66782460/339144 – Klaas van Schelven Mar 24 '21 at 13:51
-
@KlaasvanSchelven, thanks for letting know about this issue. I've updated the answer with with your link. – Vadzim Mar 29 '21 at 07:35
I recently used this tool https://github.com/drwetter/testssl.sh and it provides a comprehensive report related to SSL.
Example output:
Testing protocols via sockets except NPN+ALPN
SSLv2 not offered (OK)
SSLv3 not offered (OK)
TLS 1 not offered
TLS 1.1 not offered
TLS 1.2 offered (OK)
TLS 1.3 not offered and downgraded to a weaker protocol
NPN/SPDY not offered
ALPN/HTTP2 h2, http/1.1 (offered)
Testing cipher categories
NULL ciphers (no encryption) not offered (OK)
Anonymous NULL Ciphers (no authentication) not offered (OK)
Export ciphers (w/o ADH+NULL) not offered (OK)
LOW: 64 Bit + DES, RC[2,4], MD5 (w/o export) not offered (OK)
Triple DES Ciphers / IDEA not offered
Obsolete: SEED + 128+256 Bit CBC cipher offered
non-FS Strong encryption (AEAD ciphers) offered (OK)
Forward Secure Strong encryption (AEAD ciphers) offered (OK)
Testing robust (perfect) forward secrecy, (P)FS -- omitting Null Authentication/Encryption, 3DES, RC4
PFS is offered (OK) ECDHE-RSA-AES256-GCM-SHA384
ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES256-SHA
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES128-SHA
Elliptic curves offered: prime256v1 secp384r1 secp521r1 X25519 X448
Testing server preferences
Has server cipher order? yes (OK)
Negotiated protocol TLSv1.2
Negotiated cipher ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Cipher order
TLSv1.2: ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384
ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES256-SHA384
ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA AES128-GCM-SHA256
AES128-SHA AES256-SHA
Testing server defaults (Server Hello)
TLS extensions (standard) "renegotiation info/#65281" "server name/#0"
"EC point formats/#11" "session ticket/#35"
"status request/#5" "max fragment length/#1"
"application layer protocol negotiation/#16"
"encrypt-then-mac/#22"
"extended master secret/#23"
Session Ticket RFC 5077 hint 7200 seconds, session tickets keys seems to be rotated < daily
SSL Session ID support yes
Session Resumption Tickets: yes, ID: yes
TLS clock skew Random values, no fingerprinting possible
Signature Algorithm SHA256 with RSA
Server key size RSA 2048 bits
Server key usage Digital Signature, Key Encipherment
Server extended key usage TLS Web Server Authentication, TLS Web Client Authentication
Serial / Fingerprints REDACTED / SHA1 REDACTED
SHA256 REDACTED
Common Name (CN) REDACTED (CN in response to request w/o SNI: *.REDACTED )
subjectAltName (SAN) REDACTED
Issuer Let's Encrypt Authority X3 (Let's Encrypt from US)
Trust (hostname) Ok via SAN (SNI mandatory)
Chain of trust Ok
EV cert (experimental) no
ETS/"eTLS", visibility info not present
Certificate Validity (UTC) 72 >= 30 days (2020-03-19 22:41 --> 2020-06-17 22:41)
# of certificates provided 2
Certificate Revocation List --
OCSP URI http://ocsp.int-x3.letsencrypt.org
OCSP stapling offered, not revoked
OCSP must staple extension --
DNS CAA RR (experimental) not offered
Certificate Transparency yes (certificate extension)
Testing HTTP header response @ "/"
HTTP Status Code 200 OK
HTTP clock skew -1 sec from localtime
Strict Transport Security 366 days=31622400 s, just this domain
Public Key Pinning --
Server banner nginx
Application banner --
Cookie(s) (none issued at "/")
Security headers X-Frame-Options SAMEORIGIN
X-Content-Type-Options nosniff
X-Served-By REDACTED, REDACTED
Cache-Control public, max-age=3600
Reverse Proxy banner X-Cache: HIT, HIT
X-Cache-Hits: 3, 1
Via: 1.1 varnish
Testing vulnerabilities
Heartbleed (CVE-2014-0160) not vulnerable (OK), no heartbeat extension
CCS (CVE-2014-0224) not vulnerable (OK)
Ticketbleed (CVE-2016-9244), experiment. not vulnerable (OK)
ROBOT not vulnerable (OK)
Secure Renegotiation (RFC 5746) supported (OK)
Secure Client-Initiated Renegotiation not vulnerable (OK)
CRIME, TLS (CVE-2012-4929) not vulnerable (OK)
BREACH (CVE-2013-3587) potentially NOT ok, uses gzip HTTP compression. - only supplied "/" tested
Can be ignored for static pages or if no secrets in the page
POODLE, SSL (CVE-2014-3566) not vulnerable (OK), no SSLv3 support
TLS_FALLBACK_SCSV (RFC 7507) No fallback possible (OK), no protocol below TLS 1.2 offered
SWEET32 (CVE-2016-2183, CVE-2016-6329) not vulnerable (OK)
FREAK (CVE-2015-0204) not vulnerable (OK)
DROWN (CVE-2016-0800, CVE-2016-0703) not vulnerable on this host and port (OK)
make sure you don't use this certificate elsewhere with SSLv2 enabled services
https://censys.io/ipv4?q=REDACTED could help you to find out
LOGJAM (CVE-2015-4000), experimental not vulnerable (OK): no DH EXPORT ciphers, no DH key detected with <= TLS 1.2
BEAST (CVE-2011-3389) not vulnerable (OK), no SSL3 or TLS1
LUCKY13 (CVE-2013-0169), experimental potentially VULNERABLE, uses cipher block chaining (CBC) ciphers with TLS. Check patches
RC4 (CVE-2013-2566, CVE-2015-2808) no RC4 ciphers detected (OK)
Testing 370 ciphers via OpenSSL plus sockets against the server, ordered by encryption strength
Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Cipher Suite Name (IANA/RFC)
-----------------------------------------------------------------------------------------------------------------------------
xc030 ECDHE-RSA-AES256-GCM-SHA384 ECDH 256 AESGCM 256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
xc028 ECDHE-RSA-AES256-SHA384 ECDH 256 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
xc014 ECDHE-RSA-AES256-SHA ECDH 256 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
x35 AES256-SHA RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA
xc02f ECDHE-RSA-AES128-GCM-SHA256 ECDH 256 AESGCM 128 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
xc027 ECDHE-RSA-AES128-SHA256 ECDH 256 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
xc013 ECDHE-RSA-AES128-SHA ECDH 256 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
x9c AES128-GCM-SHA256 RSA AESGCM 128 TLS_RSA_WITH_AES_128_GCM_SHA256
x2f AES128-SHA RSA AES 128 TLS_RSA_WITH_AES_128_CBC_SHA
Running client simulations (HTTP) via sockets
Android 4.4.2 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Android 5.0.0 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Android 6.0 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Android 7.0 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
Android 8.1 (native) TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
Android 9.0 (native) TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
Android 10.0 (native) TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
Chrome 74 (Win 10) TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
Chrome 79 (Win 10) TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
Firefox 66 (Win 8.1/10) TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
Firefox 71 (Win 10) TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
IE 6 XP No connection
IE 8 Win 7 No connection
IE 8 XP No connection
IE 11 Win 7 TLSv1.2 ECDHE-RSA-AES128-SHA256, 256 bit ECDH (P-256)
IE 11 Win 8.1 TLSv1.2 ECDHE-RSA-AES128-SHA256, 256 bit ECDH (P-256)
IE 11 Win Phone 8.1 TLSv1.2 ECDHE-RSA-AES128-SHA256, 256 bit ECDH (P-256)
IE 11 Win 10 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Edge 15 Win 10 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
Edge 17 (Win 10) TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
Opera 66 (Win 10) TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
Safari 9 iOS 9 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Safari 9 OS X 10.11 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Safari 10 OS X 10.12 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Safari 12.1 (iOS 12.2) TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
Safari 13.0 (macOS 10.14.6) TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
Apple ATS 9 iOS 9 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Java 6u45 No connection
Java 7u25 No connection
Java 8u161 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Java 11.0.2 (OpenJDK) TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Java 12.0.1 (OpenJDK) TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
OpenSSL 1.0.2e TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
OpenSSL 1.1.0l (Debian) TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
OpenSSL 1.1.1d (Debian) TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
Thunderbird (68.3) TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 253 bit ECDH (X25519)
Another cool CLI tool is gnutls-cli
. It is built-in on MacOS and Linux-Unix systems.

- 171
- 1
- 3
-
2w.r.t. the present question, the money line in the above output starts with "Chain of trust" – Klaas van Schelven Mar 24 '21 at 10:00
Expanding on the above answers.
If the intermediate certificates are in a separate file, as they are in postfix and apache to name some, you can still verify the chain on the command line.
End goal: Verify all certificates in a chain:
- Input must be subdomain, intermediate, root
- Can be one file or multiple files as long as the order is correct
- Will verify all certificates, starting with root, then intermediate(s), then subdomain last
#!/bin/bash
# check certificate chain
# Relies on bash
Input the domain, intermediate and root - if multiple intermediates they have to be
ordered in sequence "intermediate1 --> intermediateN --> root"
cat "$@" |
# Insert a single "non-base64" char to split / get to the certificates
sed '/BEGIN/i@' |
# reverse the order to get to the root cert first, etc
tac |
# Read the certificates one by one
while read -d"@" CERT
do
# Remember to reverse the certificate again
C_CERT=$(echo "$CERT" | tac)
# Grab the Common Name for the output
CN=$(echo "$C_CERT" | openssl x509 -noout -subject | sed 's/^.*CN=//')
if [ -z "$CERTS" ]
then
# The root certificate is only verified against itself
RET=$(echo "${C_CERT}" | openssl verify -verbose -CApath /no-such-dir -x509_strict 2>&1)
else
# Verify subsequent certificates agains the root, then root/intermediate(s)
RET=$(echo "${C_CERT}" | openssl verify -verbose -CAfile <(echo -e "$CERTS") -CApath /no-such-dir -x509_strict 2>&1)
fi
# Save certificates as we progress, to be used in CAfile
CERTS="${CERTS}\n${C_CERT}"
echo "$RET - CN $CN"
done
Example output:
# ./check-cert.sh my-domain.crt intermediate-and-root.crt
stdin: OK - CN Root
stdin: OK - CN Intermediate
stdin: OK - CN example.com
./check-cert.sh my-domain-error.crt intermediate-and-root.crt
stdin: OK - Root
stdin: OK - Intermediate
stdin: C = ... CN = example.com, serialNumber = 000000000
error 20 at 0 depth lookup:unable to get local issuer certificate - CN example.com/serialNumber=000000000
Original answer - wrong
This was the original answer, and it is wrong. This will only verify the first certificate in the output / files, so the chain is never verified. Oneliners sometimes makes you trip and fall...
I'm keeping it for reference - hopefully somebody else will learn from this mistake.
bash
openssl verify -verbose -x509_strict <(cat my-domain.crt intermediate.crt)

- 336
-
This answer is misleading, because "openssl verify" does not in fact verify more than 1 certificate per file, and thus cannot be used on bundles. See stackoverflow.com/a/66782460/339144 – Klaas van Schelven Mar 24 '21 at 13:51
-
1@KlaasvanSchelven thank you for this comment. I've updated my answer. It's more elaborate, and hopefully correct. – sastorsl Mar 25 '21 at 11:04
-
-untrusted chainfile
, as shown in the answer accepted 10 years ealier, was and is much simpler and more robust – dave_thompson_085 May 12 '23 at 00:55
In my case a client came up with this error when trying to access my site. I did not find any error with the certificate but I had a misconfigured Apache.
The keychain file was missing. After adding
SSLCertificateChainFile /root/.acme.sh/[YOUR_PATH]/ca.cer
everything was fine for the client.

- 815
- 2
- 10
- 19

- 1
openssl x509
manual section. – alex Jul 07 '11 at 16:03