Shell script to check SSL certificate info like expiration date and subject

Remembering the correct openssl syntax for fetching certificate from a remote host or parsing a local certificate file for useful information is a chore, so I finally took my notes and combined into an easy to use shell script.

The script is mostly useful for not having to remember cryptic syntax, but in some cases will also parse and present information in a more readable format than the default openssl output.

It's also easy to add parsing and formatting as needed.

For example, to get certificate expiration date from a remote host, you would normally have to remember a command like:

$ echo | openssl s_client -connect gmail.com:443 2>/dev/null | openssl x509 -noout -enddate

And for your trouble, would be rewarded with output like this:

notAfter=Jun 10 00:00:00 2014 GMT

With my little script, you'd only have to type:

$ ssl-cert-info --host gmail.com --end

And get back output in your own timezone:

2014-06-09 17:00:00 PDT

However, if you really want to see raw output from openssl, you could pass any openssl option through to it like so (the option passed through in this case is '-dates'):

$ ssl-cert-info --host gmail.com --option -dates

And get back output like this:

notBefore=Mar 12 09:48:29 2014 GMT
notAfter=Jun 10 00:00:00 2014 GMT

Though I find the following much easier to understand:

$ ssl-cert-info --host gmail.com --dates
valid from: 2014-03-12 02:48:29 PDT
valid till: 2014-06-09 17:00:00 PDT

And here's the script:

#!/bin/bash

usage()
{
cat <&2
            echo "see --help for usage"
            exit 1
                  ;;
  esac
  shift
done

CheckLocalCert()
{ 
  openssl x509 -in $crt -noout $opt
}

CheckRemoteCert()
{
  echo |
  openssl s_client $servername -connect $host:$port 2>/dev/null |
  openssl x509 -noout $opt
}

if [ -z "$(type -t FormatOutput)" ]; then
  FormatOutput() { cat; }
fi

if [ -z "$opt" ]; then
  opt="-text -certopt no_header,no_version,no_serial,no_signame,no_pubkey,no_sigdump,no_aux"
fi

if [ -z "$source" ]; then
  echo "ERROR: missing certificate source."
  echo "Provide one via '--file' or '--host' arguments."
  echo "See '--help' for examples." 
  exit 1
fi

if [ "$source" == "local" ]; then
  [ -n "$DEBUG" ] && echo "DEBUG: certificate source is local file"
  if [ -z "$crt" ]; then
    echo "ERROR: missing certificate file"
    exit 1
  fi
  [ -n "$DEBUG" ] && echo
  CheckLocalCert | FormatOutput
fi

if [ "$source" == "remote" ]; then
  [ -n "$DEBUG" ] && echo "DEBUG: certificate source is remote host"
  if [ -z "$host" ]; then
    echo "ERROR: missing remote host value."
    echo "Provide one via '--host' argument"
    exit 1
  fi
  if [ -z "$port" ]; then
    [ -n "$DEBUG" ] && echo "DEBUG: defaulting to 443 for port."
    port="443"
  fi
  [ -n "$DEBUG" ] && echo
  CheckRemoteCert | FormatOutput
fi

8 Comments

  • 1. n2 replies at 24th April 2014, 6:58 am :

    Thanks for the script! Will come real handy and beats having to memorise or alias openssl s_client -connect host:443 2>/dev/null | openssl x509 -noout -text

  • 2. luke replies at 13th August 2014, 6:59 am :

    Thanks.

  • 3. Indranil Das Gupta replies at 30th August 2014, 12:29 am :

    Hi Alain,

    I would like to use this bash script in one of my projects (which is incidentally on GPLv2+ licensing).

    How may I consider your script to be licensed as – Public Domain, BSD,
    MIT, GPLv2+ or AGPL?

    thanks in advance.
    -indra

  • 4. Alain Kelder replies at 30th August 2014, 4:51 pm :

    Hi Indra,

    Everything on this blog is CC BY 3.0 US: http://creativecommons.org/licenses/by/3.0/us/

    Regards,
    Alain

  • 5. Thomas replies at 15th February 2016, 4:56 am :

    Hi Alain.

    Thank you so much for this script. I found a small issue though 🙂
    Can you change the call to s_client so that it supports sni? I found how to do it here: http://blog.chrismeller.com/testing-sni-certificates-with-openssl

    I guess what need to be changed is this:

    CheckRemoteCert()
    {
      echo | openssl s_client -servername $host -connect $host:$port 2>/dev/null | openssl x509 -noout $opt
    }
    
  • 6. Alain Kelder replies at 20th February 2016, 11:34 pm :

    Thomas, thanks for the tip! I’ve updated the script.

  • 7. Sathiya replies at 27th February 2016, 11:41 pm :

    Hi Alain ,

    Thanks for the good script. We have checked this script for RHEL5 with –host option with server name it’s works .However when we try with –host local it’s thrown the below error.

    Also

    It’s thrown the same error for RHEL 6 in both the option
    –host
    –host
    RHEL 6 Error local or remote :

    unable to load certificate
    139717751199560:error:0906D06C:PEM routines:PEM_read_bio:no start line:pem_lib.c:703:Expecting: TRUSTED CERTIFICATE

    RHEL 5 error with local options

    24480:error:0906D06C:PEM routines:PEM_read_bio:no start line:pem_lib.c:647:Expecting: TRUSTED CERTIFICATE

    Pls let me know what’s wrong here …

  • 8. Alain Kelder replies at 4th June 2016, 9:28 am :

    Sathiya, the error “unable to load certificate”, means just that — openssl wasn’t able to access the certificate as provided. Did you actually run the script with “–host local” option? That will cause the script to try contact a host named “local” to fetch the certificate. Unless you actually have a host named “local” on your network, that will fail with the error that you got.

    If you want to check a local file, instead of “–host local”, try “–file /path/to/your/certificate/file”.

Leave a comment

NOTE: Enclose quotes in <blockquote></blockquote>. Enclose code in <pre lang="LANG"></pre> (where LANG is one of these).