Reputation: 6664
If I have the actual file and a Bash shell in Mac or Linux, how can I query the cert file for when it will expire? Not a web site, but actually the certificate file itself, assuming I have the csr, key, pem and chain files.
Upvotes: 581
Views: 872169
Reputation: 607
Here's a bash script to track the renewal of your Letsencrypt or other certificates. The result is sorted for easy viewing.
#!/bin/sh
echo "Certs expiration"
for filename in /etc/letsencrypt/live/*/cert.pem; do
cert=$(openssl x509 -enddate -subject -noout -in "$filename")
date=$(echo "$cert" | awk 'FNR == 1' | cut -c10- | date +%s -f -)
dexp=$(echo "$cert" | awk 'FNR == 1' | cut -c10-)
subj=$(echo "$cert" | awk 'FNR == 2' | cut -c14-)
echo "$date $dexp $subj"
done | sort -n -k1 | cut -d' ' -f2- | awk '{print "- "$0}'
You can adapt the path where your certificates are stored.
Output example:
Certs expiration
- Sep 30 03:30:14 2024 GMT example1.com
- Sep 30 03:31:19 2024 GMT example1.org
- Oct 7 03:31:04 2024 GMT example2.com
- Oct 7 03:40:39 2024 GMT example2.org
- Oct 7 07:35:30 2024 GMT example3.com
Enjoy!
Upvotes: 2
Reputation: 23774
using openssl
here is a script to print out human readable text cert-etime-check.sh
#!/bin/bash
set -euo pipefail
### get a cert file
declare -r cert_file=${1:? error ... cert_file ?}
### start and end dates
declare -r beg_date=$(openssl x509 -noout -startdate -in $cert_file)
declare -r end_date=$(openssl x509 -noout -enddate -in $cert_file)
### subject and issuer
declare -r subject=$(openssl x509 -noout -subject -in $cert_file)
declare -r issuer=$(openssl x509 -noout -issuer -in $cert_file)
### calculate days gone and left
declare -ir beg_sec=$(date --date="${beg_date##*=}" +%s)
declare -ir end_sec=$(date --date="${end_date##*=}" +%s)
declare -ir now_sec=$(date +%s)
declare -ir day_gone=$(( $((now_sec - beg_sec)) / 86400))
declare -ir day_left=$(( $((end_sec - now_sec)) / 86400))
### print result
date --date="${beg_date##*=}" +"beg: %F-%A (gone: $day_gone)"
date --date="${end_date##*=}" +"end: %F-%A (left: $day_left)"
echo sum: $((day_gone + $day_left))
echo sub: ${subject##* = }
echo iss: ${issuer##* = }
echo
date +"now: %F-%A"
usage: ./cert-etime-check.sh cert.pem
output:
beg: 2024-03-04-Monday (gone: 26)
end: 2024-06-02-Sunday (left: 63)
sum: 89
sub: ******.***
iss: R3
now: 2024-03-31-Sunday
Upvotes: 0
Reputation: 70712
openssl
fields into bash variablesAs this question is tagged bash, I often use UNIX EPOCH
to store dates, this is useful for compute time left with $EPOCHSECONDS
and format output via printf '%(dateFmt)T
bashism:
{ read -r certStart;read -r certEnd;}< <(date -f <(cut -d = -f 2 <(
openssl x509 -dates -noout -in "$file")) +%s)
Then
printf '%-6s %(%a %d %b %Y, %H %Z)T\n' start $certStart end $certEnd
start Mon 01 Nov 2004, 17 UTC
end Mon 01 Jan 2035, 05 UTC
Sample, listing content of /etc/ssl/certs
and compute days left:
for file in /etc/ssl/certs/*pem;do
{ read -r certStart;read -r certEnd;}< <(
date -f <(cut -d = -f 2 <(
openssl x509 -dates -noout -in "$file")) +%s)
printf "%(%d %b %Y %T)T - %(%d %b %Y %T)T: %6d %s\n" \
$certStart $certEnd $(( (certEnd - EPOCHSECONDS)/86400 )) ${file##*/}
done
05 May 2011 09:37:37 - 31 Dec 2030 09:37:37: 3034 ACCVRAIZ1.pem
26 Oct 2010 08:38:03 - 26 Oct 2040 08:38:03: 6620 Buypass_Class_2_Root_CA.pem
19 Jan 2010 00:00:00 - 18 Jan 2038 23:59:59: 5609 COMODO_RSA_Certification_Authority.pem
13 Nov 2012 00:00:00 - 19 Jan 2038 03:14:07: 5609 GlobalSign_ECC_Root_CA_-_R4.pem
06 Apr 2001 07:29:40 - 06 Apr 2021 07:29:40: -522 Sonera_Class_2_Root_CA.pem
29 Jun 2004 17:39:16 - 29 Jun 2034 17:39:16: 4310 Starfield_Class_2_CA.pem
04 Feb 2016 12:32:16 - 31 Dec 2029 17:23:16: 2669 TrustCor_RootCert_CA-1.pem
01 Nov 2004 17:14:04 - 01 Jan 2035 05:37:19: 4495 XRamp_Global_CA_Root.pem
...
for file in /etc/ssl/certs/*pem;do
mapfile -t x509 < <(openssl x509 -noout -dates -subject -in "$file")
x509=("${x509[@]#*=}")
mapfile -t dates < <(IFS=$'\n';date -f - <<<"${x509[*]::2}" +%s)
str="${x509[-1]}"
declare -A Subj='([CN]="${file##*/}")'
while [[ "$str" ]] ;do
lhs=${str%%=*} rhs=${str#$lhs= } rhs=${rhs%% = *} rhs=${rhs%, *}
Subj[${lhs// }]="$rhs"
str=${str#"$lhs= $rhs"} str=${str#, }
done
printf "%(%d %b %Y %T)T - %(%d %b %Y %T)T: %s\n" \
${dates[@]} "${Subj[CN]}"
done
05 May 2011 09:37:37 - 31 Dec 2030 09:37:37: 3034 ACCVRAIZ1
26 Oct 2010 08:38:03 - 26 Oct 2040 08:38:03: 6620 Buypass Class 2 Root CA
19 Jan 2010 00:00:00 - 18 Jan 2038 23:59:59: 5609 COMODO RSA Certification Authority
13 Nov 2012 00:00:00 - 19 Jan 2038 03:14:07: 5609 GlobalSign
06 Apr 2001 07:29:40 - 06 Apr 2021 07:29:40: -522 Sonera Class2 CA
29 Jun 2004 17:39:16 - 29 Jun 2034 17:39:16: 4310 Starfield_Class_2_CA.pem
04 Feb 2016 12:32:16 - 31 Dec 2029 17:23:16: 2669 TrustCor RootCert CA-1
01 Nov 2004 17:14:04 - 01 Jan 2035 05:37:19: 4495 XRamp Global Certification Authority
...
Note: Some certs don't have CN
field in subject. For this I've initialized $Subj
array by setting CN
field to filename:
declare -A Subj='([CN]="${file##*/}")'
Sharing here a full bash script, showing all certificates from command line arguments, which could by file
, domain name
or IPv4 address
. Will ouput past days, days left, number of alternative domain, and all alts in one (long) line:
#!/bin/bash
showCert() {
local x509 dates lhs rhs str alts
mapfile -t x509 < <(
openssl x509 -noout -dates -subject -ext subjectAltName -in "$1")
x509=("${x509[@]#*=}")
mapfile -t dates < <(IFS=$'\n';date -f - <<<"${x509[*]::2}" +%s)
str="${x509[2]}"
local -A Subj;Subj[CN]="${file##*/}"
while [[ -n "$str" ]]; do
lhs=${str%%=*} rhs=${str#$lhs= } rhs=${rhs%% = *} rhs=${rhs%, *}
Subj[${lhs// }]="$rhs"
str=${str#"$lhs= $rhs"} str=${str#, }
done
read -ra alts <<<"${x509[4]//,}"
alts=("${alts[@]#*:}")
printf " %(%d %b %Y %H:%M)T %(%d %b %Y %H:%M)T %6d %6d %-30s %3d %s\n" \
"${dates[@]}" $(((dates[1]-EPOCHSECONDS)/86400)) $(((EPOCHSECONDS-
dates[0])/86400)) "${Subj[CN]}" "${#alts[@]}" "${alts[*]}"
}
checkIsIpv4() { # throw an error if not valid IPv4
local _iPointer _i _a _vareq=()
for _i ;do
case $_i in *[^0-9.]* ) return 1 ;; esac
read -ra _a <<<"${_i//./ }"
[ ${#_a[@]} -eq 4 ] || return 1
for _iPointer in "${_a[@]}" ;do
(( _iPointer == ( _iPointer & 255 ) )) || return 2
done
done
}
checkIsLabel() {
((${#1}<4 || ${#1}>253)) && return 1
[[ -z ${1//[a-zA-Z0-9.-]} ]] || return 2
[[ -z ${1//.} ]] && return 3
set -- ${1//./ }
(($#<2 )) && return 4
:
}
printf ' %-17s %-17s %6s %6s %-30s %2s\n' Not\ before Not\ after left \
past Common\ Name Alt
for arg ;do
if [ -f "$arg" ] ;then
showCert "$arg"
elif checkIsLabel "$arg" || checkIsIpv4 "$arg" ;then
showCert <(openssl s_client -ign_eof -connect "$arg:443" \
<<<$'HEAD / HTTP/1.0\r\n\r' 2> /dev/null)
else
echo "Unknown argument: '$arg'."
fi
done
showCert
create a array variable $x590
with dates, subject and alt names.mapfile
and (only 1 fork to) date
to convert both start and end dates.$Subj
for parsing Subject string (3rd line: ${x509[2]}
)$alts
.printf
to format and print each certificats showing
./certShow.sh /etc/ssl/certs/ssl-cert-snakeoil.pem www.example.com
Not before Not after left past Common Name Alt
08 Sep 2021 16:49 06 Sep 2031 16:49 3277 372 hostname.local 1 hostname.local
14 Mar 2022 00:00 14 Mar 2023 23:59 179 186 www.example.org 8 www.example.org example.net example.edu example.com example.org www.example.com www.example.edu www.example.net
Upvotes: 3
Reputation: 601
Command:
# cat {key_name} | openssl x509 -noout -enddate
Example: # cat tower.cert | openssl x509 -noout -enddate
Result:
notAfter=Dec 7 04:03:32 2023 GMT
Upvotes: 32
Reputation: 31
I have made a bash script related to the same to check if the certificate is expired or not. You can use the same if required.
Script
https://github.com/zeeshanjamal16/usefulScripts/blob/master/sslCertificateExpireCheck.sh
ReadMe
https://github.com/zeeshanjamal16/usefulScripts/blob/master/README.md
Upvotes: 1
Reputation: 6102
If you just want to know whether the certificate has expired (or will do so within the next N seconds), the -checkend <seconds>
option to openssl x509
will tell you:
if openssl x509 -checkend 86400 -noout -in file.pem
then
echo "Certificate is good for another day!"
else
echo "Certificate has expired or will do so within 24 hours!"
echo "(or is invalid/not found)"
fi
This saves having to do date/time comparisons yourself.
openssl
will return an exit code of 0
(zero) if the certificate has not expired and will not do so for the next 86400 seconds, in the example above. If the certificate will have expired or has already done so - or some other error like an invalid/nonexistent file - the return code is 1
.
(Of course, it assumes the time/date is set correctly)
Be aware that older versions of openssl have a bug which means if the time specified in checkend
is too large, 0 will always be returned (https://github.com/openssl/openssl/issues/6180).
Upvotes: 252
Reputation: 601
One line checking on true/false if cert of domain will be expired in some time later(ex. 15 days):
openssl x509 -checkend $(( 24*3600*15 )) -noout -in <(openssl s_client -showcerts -connect my.domain.com:443 </dev/null 2>/dev/null | openssl x509 -outform PEM)
if [ $? -eq 0 ]; then
echo 'good'
else
echo 'bad'
fi
Upvotes: 8
Reputation: 2167
Same as accepted answer, But note that it works even with .crt
file and not just .pem
file, just in case if you are not able to find .pem
file location.
openssl x509 -enddate -noout -in e71c8ea7fa97ad6c.crt
Result:
notAfter=Mar 29 06:15:00 2020 GMT
Upvotes: 17
Reputation: 1042
If (for some reason) you want to use a GUI application in Linux, use gcr-viewer
(in most distributions it is installed by the package gcr
(otherwise in package gcr-viewer
))
gcr-viewer file.pem
# or
gcr-viewer file.crt
Upvotes: 1
Reputation: 1147
Here's a bash function which checks all your servers, assuming you're using DNS round-robin. Note that this requires GNU date and won't work on Mac OS
function check_certs () {
if [ -z "$1" ]
then
echo "domain name missing"
exit 1
fi
name="$1"
shift
now_epoch=$( date +%s )
dig +noall +answer $name | while read _ _ _ _ ip;
do
echo -n "$ip:"
expiry_date=$( echo | openssl s_client -showcerts -servername $name -connect $ip:443 2>/dev/null | openssl x509 -inform pem -noout -enddate | cut -d "=" -f 2 )
echo -n " $expiry_date";
expiry_epoch=$( date -d "$expiry_date" +%s )
expiry_days="$(( ($expiry_epoch - $now_epoch) / (3600 * 24) ))"
echo " $expiry_days days"
done
}
Output example:
$ check_certs stackoverflow.com
151.101.1.69: Aug 14 12:00:00 2019 GMT 603 days
151.101.65.69: Aug 14 12:00:00 2019 GMT 603 days
151.101.129.69: Aug 14 12:00:00 2019 GMT 603 days
151.101.193.69: Aug 14 12:00:00 2019 GMT 603 days
Upvotes: 16
Reputation: 96
For MAC OSX (El Capitan) This modification of Nicholas' example worked for me.
for pem in /path/to/certs/*.pem; do
printf '%s: %s\n' \
"$(date -jf "%b %e %H:%M:%S %Y %Z" "$(openssl x509 -enddate -noout -in "$pem"|cut -d= -f 2)" +"%Y-%m-%d")" \
"$pem";
done | sort
Sample Output:
2014-12-19: /path/to/certs/MDM_Certificate.pem
2015-11-13: /path/to/certs/MDM_AirWatch_Certificate.pem
macOS didn't like the --date=
or --iso-8601
flags on my system.
Upvotes: 2
Reputation: 13750
Here's my bash command line to list multiple certificates in order of their expiration, most recently expiring first.
for pem in /etc/ssl/certs/*.pem; do
printf '%s: %s\n' \
"$(date --date="$(openssl x509 -enddate -noout -in "$pem"|cut -d= -f 2)" --iso-8601)" \
"$pem"
done | sort
Sample output:
2015-12-16: /etc/ssl/certs/Staat_der_Nederlanden_Root_CA.pem
2016-03-22: /etc/ssl/certs/CA_Disig.pem
2016-08-14: /etc/ssl/certs/EBG_Elektronik_Sertifika_Hizmet_S.pem
Upvotes: 50
Reputation: 123400
With openssl
:
openssl x509 -enddate -noout -in file.pem
The output is on the form:
notAfter=Nov 3 22:23:50 2014 GMT
Also see MikeW's answer for how to easily check whether the certificate has expired or not, or whether it will within a certain time period, without having to parse the date above.
Upvotes: 1060