UPDATE 2016/06/01: Improving the script by using pipe inside awk, thanks to @ilatypov.

When I play with X509 certificates I check that the certificate chain in the file is always complete and valid.

With openssl s_client we can see the chain and check its validity:

~ % openssl s_client -connect www.google.com:443 -CApath /etc/ssl/certs
CONNECTED(00000003)
depth=3 C = US, O = Equifax, OU = Equifax Secure Certificate Authority
verify return:1
depth=2 C = US, O = GeoTrust Inc., CN = GeoTrust Global CA
verify return:1
depth=1 C = US, O = Google Inc, CN = Google Internet Authority G2
verify return:1
depth=0 C = US, ST = California, L = Mountain View, O = Google Inc, CN = www.google.com
verify return:1
---
Certificate chain
 0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=www.google.com
   i:/C=US/O=Google Inc/CN=Google Internet Authority G2
 1 s:/C=US/O=Google Inc/CN=Google Internet Authority G2
   i:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
 2 s:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
   i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority
---
Server certificate
[snip]
    Verify return code: 0 (ok)
---

That's cool but... it works only with remote certificates. What to do with local files?

We have openssl verify to check the validity of the chain of a local file:

~ % openssl verify -untrusted google.crt google.crt
google.crt: OK

It says OK, cool but it's not very verbose: I don't see the chain like openssl s_client does and if I play with openssl x509 it will only use the first certificate of the file.

The solution is to split all the certificates from the file and use openssl x509 on each of them.

Someone already done a oneliner to split certificates from a file using awk. I initially based my script on it but @ilatypov proposed a solution in comments1 that is far better as it does not rely on temporary files. Here is the updated script:

#!/bin/bash

chain_pem="${1}"

if [[ ! -f "${chain_pem}" ]]; then
    echo "Usage: $0 BASE64_CERTIFICATE_CHAIN_FILE" >&2
    exit 1
fi

if ! openssl x509 -in "${chain_pem}" -noout 2>/dev/null ; then
    echo "${chain_pem} is not a certificate" >&2
    exit 1
fi

awk -F'\n' '
        BEGIN {
            showcert = "openssl x509 -noout -subject -issuer"
        }

        /-----BEGIN CERTIFICATE-----/ {
            printf "%2d: ", ind
        }

        {
            printf $0"\n" | showcert
        }

        /-----END CERTIFICATE-----/ {
            close(showcert)
            ind ++
        }
    ' "${chain_pem}"

echo
openssl verify -untrusted "${chain_pem}" "${chain_pem}"

Here is the output for the certificate file from www.google.com2 (my script was saved as ssl_chain.sh):

~ % ssl_chain.sh google.crt
 0: subject= /C=US/ST=California/L=Mountain View/O=Google Inc/CN=www.google.com
issuer= /C=US/O=Google Inc/CN=Google Internet Authority G2
 1: subject= /C=US/O=Google Inc/CN=Google Internet Authority G2
issuer= /C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
 2: subject= /C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
issuer= /C=US/O=Equifax/OU=Equifax Secure Certificate Authority

google.crt: OK

Enjoy!


  1. Given that I have deleted comments from my blog, the link is now a direct link to the original Disqus thread ↩︎

  2. I've retrieved their certificate by using openssl s_client but by default it shows only the first certificate. Use the option -showcerts to see the complete chain ↩︎