6

I'm trying to download certificate chains and verify it locally as new CA certs are pushed to clients, basically getting a view of which sites will work at any given time. I use the following command to download the chain:

echo -n | openssl s_client -showcerts -connect example.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > mycert.pem

But when using openssl verify I get an error message:

root@host:~# openssl version 
OpenSSL 1.1.0e  16 Feb 2017

root@host:~# echo -n | openssl s_client -showcerts -connect example.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > mycert.pem 
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
verify return:1
depth=1 C = US, O = DigiCert Inc, CN = DigiCert SHA2 Secure Server CA
verify return:1
depth=0 C = US, ST = California, L = Los Angeles, O = Internet Corporation for Assigned Names and Numbers, OU = Technology, CN = www.example.org
verify return:1
DONE


root@host:~# openssl verify mycert.pem 
C = US, ST = California, L = Los Angeles, O = Internet Corporation for Assigned Names and Numbers, OU = Technology, CN = www.example.org
error 20 at 0 depth lookup: unable to get local issuer certificate
error mycert.pem: verification failed

What is the correct way of verifying a certificate chain like this offline?

1

4 Answers 4

2

You should put the certificate you want to verify in one file, and the chain in another file:

openssl verify -CAfile chain.pem mycert.pem

It's also important (of course) that openssl knows how to find the root certificate if not included in chain.pem. If you need to do this (if you're using your own CA) then you can specify an alternative directory too look for it in with -CApath

2
  • I've tried using the -CApath parameter using the built-in system store, but get exactly the same response. I don't know in advance which cert will be used, so need a way to use the system store to check.
    – s3c
    Mar 28, 2017 at 7:29
  • I think Victor is going to have the correct answer eventually. Due to some bugs or bad interactions OpenSSL is usually not wired with a CA Store. You will eventually need -CAfile of -CApath. You can get the latest CAcerts from cuRL, and then -CAfile cacert.pem.
    – user56041
    Aug 30, 2018 at 21:52
1

So I found the answer for this, OpenSSL only verifies the first certificate in a file. That means if you have a valid chain, you should only verify the last certificate.

1
  • 1
    That's my point. -showcerts makes s_client print out the certificate chain. If you want to use that as a basis for the validation you need to specify it as an argument to -CAfile: victor@fgcr:~$ openssl verify mycert.pem mycert.pem: C = US, ST = California, L = Mountain View, O = Google Inc, CN = www.google.com error 20 at 0 depth lookup:unable to get local issuer certificate victor@fgcr:~$ openssl verify -CAfile mychain.pem mycert.pem mycert.pem: OK mycert.pem is generated without -showcerts, and mychain.pem with -showcerts. Mar 28, 2017 at 18:22
1

You can achieve your desired result with a combination of certtool and openssl. It is important to note that there are two things invovled here, one is to validate the chain itself and the other is checking if the chain is trusted against locally installed trusted root certificates.


WARNING: check negative cases

Please check negative cases, that is make sure things which your test should pick up as invalid is correctly picked up as invalid. You can do that with https://badssl.com/ or something you make yourself. If you don't know for sure that negative cases will fail whatever you are testing then the test will not help a lot.


To verify the chain is trusted:

openssl s_client -showcerts -connect example.com:443 </dev/null 2>/dev/null \
    | sed -ne '/-BEGIN/,/-END/p' \
    | certtool --verify

To verify chain's consistency only - but not trust:

openssl s_client -showcerts -connect example.com:443 </dev/null 2>/dev/null \
    | sed -ne '/-BEGIN/,/-END/p' \
    | certtool --verify-chain

Example of a trusted chain:

$ openssl s_client -showcerts -connect example.com:443 </dev/null 2>/dev/null | sed -ne '/-BEGIN/,/-END/p' | certtool --verify
Loaded system trust (154 CAs available)
    Subject: CN=DigiCert SHA2 Secure Server CA,O=DigiCert Inc,C=US
    Issuer: CN=DigiCert Global Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US
    Signature algorithm: RSA-SHA256
    Output: Not verified. The certificate is NOT trusted. The certificate issuer is unknown. 

    Subject: CN=DigiCert SHA2 Secure Server CA,O=DigiCert Inc,C=US
    Issuer: CN=DigiCert Global Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US
    Signature algorithm: RSA-SHA256
    Output: Not verified. The certificate is NOT trusted. The certificate issuer is unknown. 

    Subject: CN=DigiCert SHA2 Secure Server CA,O=DigiCert Inc,C=US
    Issuer: CN=DigiCert Global Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US
    Checked against: CN=DigiCert Global Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US
    Signature algorithm: RSA-SHA256
    Output: Verified. The certificate is trusted. 

    Subject: CN=www.example.org,OU=Technology,O=Internet Corporation for Assigned Names and Numbers,L=Los Angeles,ST=California,C=US
    Issuer: CN=DigiCert SHA2 Secure Server CA,O=DigiCert Inc,C=US
    Checked against: CN=DigiCert SHA2 Secure Server CA,O=DigiCert Inc,C=US
    Signature algorithm: RSA-SHA256
    Output: Verified. The certificate is trusted. 

Chain verification output: Verified. The certificate is trusted. 

Example of an untrusted chain:

$ openssl s_client -showcerts -connect untrusted-root.badssl.com:443 </dev/null 2>/dev/null | sed -ne '/-BEGIN/,/-END/p' | certtool --verify
Loaded system trust (154 CAs available)
    Subject: CN=*.badssl.com,O=BadSSL,L=San Francisco,ST=California,C=US
    Issuer: CN=BadSSL Untrusted Root Certificate Authority,O=BadSSL,L=San Francisco,ST=California,C=US
    Signature algorithm: RSA-SHA256
    Output: Not verified. The certificate is NOT trusted. The certificate issuer is unknown. 

    Subject: CN=*.badssl.com,O=BadSSL,L=San Francisco,ST=California,C=US
    Issuer: CN=BadSSL Untrusted Root Certificate Authority,O=BadSSL,L=San Francisco,ST=California,C=US
    Signature algorithm: RSA-SHA256
    Output: Not verified. The certificate is NOT trusted. The certificate issuer is unknown. 

    Subject: CN=*.badssl.com,O=BadSSL,L=San Francisco,ST=California,C=US
    Issuer: CN=BadSSL Untrusted Root Certificate Authority,O=BadSSL,L=San Francisco,ST=California,C=US
    Signature algorithm: RSA-SHA256
    Output: Not verified. The certificate is NOT trusted. The certificate issuer is unknown. 

Chain verification output: Not verified. The certificate is NOT trusted. The certificate issuer is unknown. 

Checking if the untrusted chain is valid nonetheless:

$ openssl s_client -showcerts -connect untrusted-root.badssl.com:443 </dev/null 2>/dev/null | sed -ne '/-BEGIN/,/-END/p' | certtool --verify-chain
    Subject: CN=*.badssl.com,O=BadSSL,L=San Francisco,ST=California,C=US
    Issuer: CN=BadSSL Untrusted Root Certificate Authority,O=BadSSL,L=San Francisco,ST=California,C=US
    Checked against: CN=BadSSL Untrusted Root Certificate Authority,O=BadSSL,L=San Francisco,ST=California,C=US
    Signature algorithm: RSA-SHA256
    Output: Verified. The certificate is trusted. 

Chain verification output: Verified. The certificate is trusted. 
0

The problem is in the output of -showcerts command: you only have your certificate and the certificate which signed it - and is probably an intermediate certificate, but not the full chain. To have the OK statement, you should:

  • Put your certificate (first -BEGIN END- block) in file mycert.crt
  • Put the other one(s) in file CAcerts.crt
  • Check with openssh -text -in CAcerts.crt to look for a root CA which signed this, and add it to CAfile.crt.
  • Maybe repeat this if CA is still not a root one (self-signed).
  • Then verify your cert: openssl verify -CAfile CAcerts.crt mycert.crt

Note: You can obtain the well known CA certificates from Firefox store using the preferences GUI (view certificates and then export), or in command line with certutil, which is in your Firefox profile directory. For example:

$ find $HOME/.mozilla/firefox -name cert8.db # point for example to abcdef.default directory
$ certutil -a -L -d $HOME/.mozilla/firefox/abcdef.default/ -n "Let's Encrypt` Authority X3"

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .