Simple script to check expiry dates on a java keystore (.jks) file

Here in my company we regularly need to check for expired certificates or just to have a proactive management checking which certificates are close to their expiry dates and issue new ones to avoid service disruption.

For that reason I’ve created a simple bash script which can be used in conjunction with nagios to check for expiring certicates.

#!/bin/sh
 
########################################################
#
#       Check certificates inside a java keystore
#
########################################################
TIMEOUT="timeout -k 10s 5s "
KEYTOOL="$TIMEOUT keytool"
THRESHOLD_IN_DAYS="30"
KEYSTORE=""
PASSWORD=""
RET=0
 
ARGS=`getopt -o "p:k:t:" -l "password:,keystore:,threshold:" -n "$0" -- "$@"`
 
function usage {
        echo "Usage: $0 --keystore <keystore> [--password <password>] [--threshold <number of days until expiry>]"
        exit
}
 
 
 
function start {
        CURRENT=`date +%s`
 
        THRESHOLD=$(($CURRENT + ($THRESHOLD_IN_DAYS*24*60*60)))
        if [ $THRESHOLD -le $CURRENT ]; then
                echo "[ERROR] Invalid date."
                exit 1
        fi
        echo "Looking for certificates inside the keystore $(basename $KEYSTORE) expiring in $THRESHOLD_IN_DAYS day(s)..."
 
        $KEYTOOL -list -v -keystore "$KEYSTORE"  $PASSWORD 2>&1 > /dev/null
        if [ $? -gt 0 ]; then echo "Error opening the keystore."; exit 1; fi
 
        $KEYTOOL -list -v -keystore "$KEYSTORE"  $PASSWORD | grep Alias | awk '{print $3}' | while read ALIAS
        do
                #Iterate through all the certificate alias
                EXPIRACY=`$KEYTOOL -list -v -keystore "$KEYSTORE"  $PASSWORD -alias $ALIAS | grep Valid`
                UNTIL=`$KEYTOOL -list -v -keystore "$KEYSTORE"  $PASSWORD -alias $ALIAS | grep Valid | perl -ne 'if(/until: (.*?)\n/) { print "$1\n"; }'`
                UNTIL_SECONDS=`date -d "$UNTIL" +%s`
                REMAINING_DAYS=$(( ($UNTIL_SECONDS -  $(date +%s)) / 60 / 60 / 24 ))
                if [ $THRESHOLD -le $UNTIL_SECONDS ]; then
                        echo "[OK]      Certificate $ALIAS expires in '$UNTIL' ($REMAINING_DAYS day(s) remaining)."
                else
                        echo "[WARNING] Certificate $ALIAS expires in '$UNTIL' ($REMAINING_DAYS day(s) remaining)."
                        RET=1
                fi
 
        done
        echo "Finished..."
        exit $RET
}
 
eval set -- "$ARGS"
 
while true
do
        case "$1" in
                -p|--password)
                        if [ -n "$2" ]; then PASSWORD=" -storepass $2"; else echo "Invalid password"; exit 1; fi
                        shift 2;;
                -k|--keystore)
                        if [ ! -f "$2" ]; then echo "Keystore not found: $1"; exit 1; else KEYSTORE=$2; fi
                        shift 2;;
                -t|--threshold)
                        if [ -n "$2" ] && [[ $2 =~ ^[0-9]+$ ]]; then THRESHOLD_IN_DAYS=$2; else echo "Invalid threshold"; exit 1; fi
                        shift 2;;
                --)
                        shift
                        break;;
        esac
done
 
if [ -n "$KEYSTORE" ]
then
        start
else
        usage
fi

All you have to do is call it like this:

./checkCertificate --keystore [YOUR_KEYSTORE_FILE] --password [YOUR_PASSWORD] --threshold [THRESHOLD_IN_DAYS]

The threshold indicates how many days are left until the expiry date is reached. I’m sure that there are several other ways of doing it but this is my own 🙂

11 comments

  1. The root_ca.cert expiration date of my company is in 30 years (2043) and this far long date seems to cause an issue when checking the validity of keystores that contain that root_ca.cert…
    Do you have an idea on how to get it working in my case ?

    But are you talking about an error being raised by my script? Can you please share the output of the script you you run it against the keystore that holds that certificate?

  2. Thank you for this nice script!
    I just had to exchange the awk expression
    ‘{print $3}’
    in line 37 with
    ‘{print $NF}’
    because the position of the alias name is language dependent (“Aliasname: xyz” => $2 in German). But it should always be the last token in the line ( => $NF ).

  3. Found this via Google, very useful thanks!
    As you say, there are several ways of doing it, but the one thing that stood out was the unnecessary calling up of PERL just to cut out a column, in this bit here:
    UNTIL=`$KEYTOOL -list -v -keystore “$KEYSTORE” $PASSWORD -alias $ALIAS | grep Valid | perl -ne ‘if(/until: (.*?)\n/) { print “$1\n”; }’`
    UNTIL_SECONDS=`date -d “$UNTIL” +%s`

    This could be done with awk of course, but seeing as we are writing shell code then (assuming bash or Korn available) more efficient code could be something like:

    VALIDITY=$($KEYTOOL -list -v -keystore “$KEYSTORE” $PASSWORD -alias $ALIAS | grep Valid)
    UNTIL_SECONDS=$(date -d “${VALIDITY##*until: }” +%s)

    Saves forking for external processes. 🙂

  4. i am running the script in ksh i am getting an error in this line please help me

    -t|–threshold)
    if [ -n “$2” ] && [[ $2 =~ ^[0-9]+$ ]]; then THRESHOLD_IN_DAYS=$2; else echo “Invalid threshold”; exit 1; fi
    shift 2;;

  5. Thanks for this!

    I had to do a few changes to get it to work:

    Rename function names:
    “function usage {” -> “usage () {”
    “function start {” -> “start () {”

    On my system keytool requires adding -storepass before the password so:
    $KEYTOOL -list -v -keystore “$KEYSTORE” $PASSWORD 2>&1 > /dev/null
    would become
    $KEYTOOL -list -v -keystore “$KEYSTORE” -storepass $PASSWORD 2>&1 > /dev/null

    I had one system with an intermediate cert in the same keystore also found with alias. Due to this it would fail on the following call:
    UNTIL=`$KEYTOOL -list -v -keystore “$KEYSTORE” -storepass $PASSWORD -alias $ALIAS | grep Valid | perl -ne ‘if(/until: (.*?)\n/) { print “$1\n”; }’`

    I added head -1 to only include the first validity date:

    UNTIL=`$KEYTOOL -list -v -keystore “$KEYSTORE” -storepass $PASSWORD -alias $ALIAS | grep Valid | head -1 | perl -ne ‘if(/until: (.*?)\n/) { print “$1\n”; }’`

    Also it does not handle special characters in the storepass, would probably be better to also wrap that up

  6. Nice, but at least one moment – there can be cases, when one Alias have several certificates (there is message like “Certificate chain length: 3
    Certificate[1]:

    Certificate[2]:

    Certificate[3]:
    ..

    1. Thanks a lot for the script. Some minor modifications:
      1.
      $KEYTOOL -list -v -keystore “$KEYSTORE” $PASSWORD | grep Alias | awk ‘{print $NF}’ | while read ALIAS
      replaced with :
      $KEYTOOL -list -v -keystore “$KEYSTORE” $PASSWORD | sed -n -e ‘/Alias/ s/.*\: *//p’ | while read ALIAS
      to grab everything after the colon (:) because some people create aliases using spaces.
      2. loop over all the certificates belonging to the same alias by replacing:
      VALIDITY=$($KEYTOOL -list -v -keystore “$KEYSTORE” $PASSWORD -alias “$ALIAS” | grep Valid)
      with
      $KEYTOOL -list -v -keystore “$KEYSTORE” $PASSWORD -alias “$ALIAS” | grep Valid | while read VALIDITY
      do …. done (the done just before the previous done)

  7. Thanks for the script. A minor modification since the keytool command is not always in the user PATH.
    KEYTOOL=”`readlink -f /usr/bin/java| sed ‘s/java/keytool/’`”

Leave a Reply

Your email address will not be published.