xref: /lighttpd1.4/doc/scripts/cert-staple.sh (revision 21ffe62a)
1#!/bin/sh
2#
3# cert-staple.sh - retrieve from CA and manage OCSP stapling info for leaf cert
4#
5# Copyright(c) 2022 Glenn Strauss gstrauss()gluelogic.com  All rights reserved
6# License: BSD 3-clause (same as lighttpd)
7
8CERT_PEM="$1"   # input (cert.pem)
9CHAIN_PEM="$2"  # input (chain.pem)
10OCSP_DER="$3"   # output symlink (staple.der)
11
12OCSP_TMP=""     # temporary file
13next_delta=90000  # 25 hours
14
15if [ -z "$CERT_PEM" ] || [ -z "$CHAIN_PEM" ] || [ -z "$OCSP_DER" ] \
16   || [ ! -f "$CERT_PEM" ] || [ ! -f "$CHAIN_PEM" ]; then
17    echo 1>&2 "usage: cert-staple.sh cert.pem chain.pem staple.der"
18    exit 1
19fi
20
21errexit() {
22    [ -n "$OCSP_TMP" ] && rm -f "$OCSP_TMP"
23    exit 1
24}
25
26# short-circuit if Next Update is > $next_delta in the future
27next_ts=$(readlink "$OCSP_DER" 2>/dev/null)
28if [ -n "$next_ts" ]; then
29    next_ts="${next_ts##*.}"
30    ts=$(date +%s)
31    ts=$(( $ts + $next_delta ))
32    if [ -n "$next_ts" ] && [ "$next_ts" -gt "$ts" ]; then
33        exit 0
34    fi
35fi
36
37# get URI of OCSP responder from certificate
38OCSP_URI=$(openssl x509 -in "$CERT_PEM" -ocsp_uri -noout)
39[ $? = 0 ] && [ -n "$OCSP_URI" ] || exit 1
40
41# exception for (unsupported, end-of-life) older versions of OpenSSL
42OCSP_HOST=
43OPENSSL_VERSION=$(openssl version)
44if [ "${OPENSSL_VERSION}" != "${OPENSSL_VERSION#OpenSSL 1.0.}" ]; then
45    # get authority from URI
46    OCSP_HOST=$(echo "$OCSP_URI" | cut -d/ -f3)
47fi
48
49# get OCSP response from OCSP responder
50OCSP_TMP="$OCSP_DER.$$"
51OCSP_RESP=$(openssl ocsp -issuer "$CHAIN_PEM" -cert "$CERT_PEM" -respout "$OCSP_TMP" -noverify -no_nonce -url "$OCSP_URI" ${OCSP_HOST:+-header Host "$OCSP_HOST"})
52[ $? = 0 ] || errexit
53
54# parse OCSP response from OCSP responder
55#
56#$CERT_PEM: good
57#        This Update: Jun  5 21:00:00 2020 GMT
58#        Next Update: Jun 12 21:00:00 2020 GMT
59
60ocsp_status="$(printf %s "$OCSP_RESP" | head -1)"
61[ "$ocsp_status" = "$CERT_PEM: good" ] || errexit
62
63next_update="$(printf %s "$OCSP_RESP" | grep 'Next Update:')"
64next_date="$(printf %s "$next_update" | sed 's/.*Next Update: //')"
65[ -n "$next_date" ] || errexit
66sysname=$(uname -s)
67if [ "$sysname" = "FreeBSD" ] || \
68   [ "$sysname" = "OpenBSD" ] || \
69   [ "$sysname" = "DragonFly" ]; then
70    ocsp_expire=$(date -j -f "%b %e %T %Y %Z" "$next_date" "+%s")
71else
72    ocsp_expire=$(date -d "$next_date" +%s)
73fi
74
75# validate OCSP response
76ocsp_verify=$(openssl ocsp -issuer "$CHAIN_PEM" -verify_other "$CHAIN_PEM" -cert "$CERT_PEM" -respin "$OCSP_TMP" -no_nonce -out /dev/null 2>&1)
77[ "$ocsp_verify" = "Response verify OK" ] || errexit
78
79# rename and update symlink to install OCSP response to be used in OCSP stapling
80OCSP_OUT="$OCSP_DER.$ocsp_expire"
81mv "$OCSP_TMP" "$OCSP_OUT" || errexit
82OCSP_TMP=""
83ln -sf "${OCSP_OUT##*/}" "$OCSP_DER" || errexit
84
85# debug: display text output of OCSP .der file
86#openssl ocsp -respin "$OCSP_DER" -resp_text -noverify
87
88# remove old OCSP responses which have expired
89now=$(date +%s)
90for i in "$OCSP_DER".*; do
91    ts="${i#${OCSP_DER}.}"
92    if [ -n "$ts" ] && [ "$ts" -lt "$now" ]; then
93        rm -f "$i"
94    fi
95done
96