While working on my company’s implemenation of STIR/SHAKEN , I ran into a very basic issue – how do I easily create test self-signed SHAKEN certificates?
While I did find a couple of posts on how to do this, I wanted a nice script that would accomplish the following:
Generate either a self-signed certificate or a key/CSR pair for use with an official certificate authority
Allow easy passing of the certificate subject information (Country, State, Locality, etc.)
Create certificate filenames with a unique identifier, to prevent name collisions (by default, the identifier is a UNIX timestamp)
Below is the script I came up with:
#!/usr/bin/env bash
set -e
# Set default values for variables
BUILD_DIR="/tmp/stir-shaken"
CA_DIR="${BUILD_DIR}/ca"
SP_DIR="${BUILD_DIR}/sp"
SP_CREATED_FILES_LIST="${SP_DIR}/stir_shaken_files.txt"
IDENTIFIER=$(date +%s)
DEBUG_MODE=0
SELF_SIGNED=0
CERT_COUNTRY=""
CERT_STATE=""
CERT_LOCALITY=""
CERT_ORG=""
CERT_OU=""
CERT_CN="SHAKEN"
# Default, 5 years.
SP_PRIVKEY_EXPIRY_DAYS=${SP_PRIVKEY_EXPIRY_DAYS:-1825}
# Default, 1 year.
SP_PUBKEY_EXPIRY_DAYS=${SP_PUBKEY_EXPIRY_DAYS:-366}
usage() {
echo
cat << EOF
Usage:
${0} [-b BUILD_DIR] [-i IDENTIFIER] [-d] [-h] [-s] <CERT_COUNTRY> <CERT_STATE> <CERT_LOCALITY> <CERT_ORG> <CERT_OU> [CERT_CN]
Arguments:
-h : Show this help message
-s : Generate self-signed certificate
-i IDENTIFIER : Unique Key/Cert/CSR identifier (default: current timestamp)
-d : Enable debug mode
-b BUILD_DIR : Build directory (default: ${BUILD_DIR})
CERT_COUNTRY : Required. Certificate subject country
CERT_STATE : Required. Certificate subject state
CERT_LOCALITY : Required. Certificate subject locality
CERT_ORG : Required. Certificate subject organization
CERT_OU : Required. Certificate subject organizational unit
CERT_CN : Certificate subject common name (default: SHAKEN)
Environment overrides:
SP_PRIVKEY_EXPIRY_DAYS: Default: ${SP_PRIVKEY_EXPIRY_DAYS}
SP_PUBKEY_EXPIRY_DAYS: Default: ${SP_PUBKEY_EXPIRY_DAYS}
EOF
exit 1
}
log() {
echo "$1"
}
log_debug() {
if [[ ${DEBUG_MODE} -eq 1 ]]; then
echo "$1"
fi
}
log_error() {
echo "$1" >&2
}
validate_cert_info() {
if [[ -z "${CERT_COUNTRY}" || -z "${CERT_STATE}" || -z "${CERT_LOCALITY}" || -z "${CERT_ORG}" || -z "${CERT_OU}" || -z "${CERT_CN}" ]]; then
echo
log_error "ERROR: Incomplete certificate subject information."
log_error "Please provide all of the following: CERT_COUNTRY, CERT_STATE, CERT_LOCALITY, CERT_ORG, CERT_OU"
echo
usage
fi
}
make_build_dir() {
log "Cleaning old certs"
rm -rf "${BUILD_DIR}"
mkdir -p "${BUILD_DIR}"
}
create_sp_key() {
log "Creating SP key"
local sp_key="stir_shaken_sp_key_${IDENTIFIER}.pem"
mkdir -p "${SP_DIR}"
cd "${SP_DIR}"
openssl ecparam -noout -name prime256v1 -genkey -out ${sp_key}
}
create_self_signed_certs() {
log "Creating CA certificate and key"
local sp_key="stir_shaken_sp_key_${IDENTIFIER}.pem"
local sp_csr="stir_shaken_sp_csr_${IDENTIFIER}.pem"
local sp_cert="stir_shaken_sp_cert_${IDENTIFIER}.pem"
local ca_key="stir_shaken_ca_key.pem"
local ca_cert="stir_shaken_ca_cert.pem"
mkdir -p "${CA_DIR}"
cd "${CA_DIR}"
openssl ecparam -noout -name prime256v1 -genkey -out ${ca_key}
openssl req -x509 -new -nodes -key ${ca_key} -sha256 -days ${SP_PRIVKEY_EXPIRY_DAYS} -subj "/O=${CERT_ORG}/CN=${CERT_CN}" -out ${ca_cert}
cd "${SP_DIR}"
log "Configuring TNAuthList"
cat >TNAuthList.conf << EOF
asn1=SEQUENCE:tn_auth_list
[tn_auth_list]
field1=EXP:0,IA5:1001
EOF
openssl asn1parse -genconf TNAuthList.conf -out TNAuthList.der
cat >openssl.conf << EOF
[ req ]
distinguished_name = req_distinguished_name
req_extensions = v3_req
[ req_distinguished_name ]
commonName = "${CERT_CN}"
[ v3_req ]
EOF
od -An -t x1 -w TNAuthList.der | sed -e 's/ /:/g' -e 's/^/1.3.6.1.5.5.7.1.26=DER/' >> openssl.conf
log "Creating SP certificate"
openssl req -new -nodes -key ${sp_key} -keyform PEM -subj "/C=${CERT_COUNTRY}/ST=${CERT_STATE}/L=${CERT_LOCALITY}/O=${CERT_ORG}/OU=${CERT_OU}/CN=${CERT_CN}" -sha256 -config openssl.conf -out ${sp_csr}
log "Signing SP certificate"
openssl x509 -req -in ${sp_csr} -CA "${CA_DIR}/${ca_cert}" -CAkey "${CA_DIR}/${ca_key}" -CAcreateserial -days ${SP_PUBKEY_EXPIRY_DAYS} -sha256 -extfile openssl.conf -extensions v3_req -out ${sp_cert}
log "Verifying CA certificate"
if [[ ${DEBUG_MODE} -eq 1 ]]; then
openssl x509 -in "${CA_DIR}/${ca_cert}" -text -noout
else
openssl x509 -in "${CA_DIR}/${ca_cert}" -noout > /dev/null 2>&1
fi
if [[ $? -ne 0 ]]; then
echo
log_error "ERROR: CA certificate verification failed."
echo
exit 1
fi
log "Verifying SP certificate"
if [[ ${DEBUG_MODE} -eq 1 ]]; then
openssl x509 -in "${SP_DIR}/${sp_cert}" -text -noout
else
openssl x509 -in "${SP_DIR}/${sp_cert}" -noout > /dev/null 2>&1
fi
if [[ $? -ne 0 ]]; then
echo
log_error "ERROR: SP certificate verification failed."
echo
exit 1
fi
log "Setting proper permissions on generated files"
chmod 600 "${CA_DIR}/${ca_key}" "${SP_DIR}/${sp_key}"
chmod 644 "${CA_DIR}/${ca_cert}" "${SP_DIR}/${sp_csr}" "${SP_DIR}/${sp_cert}"
echo "Key: ${SP_DIR}/${sp_key}" > "${SP_CREATED_FILES_LIST}"
echo "Certificate: ${SP_DIR}/${sp_cert}" >> "${SP_CREATED_FILES_LIST}"
log "Self-signed STIR/SHAKEN certificate generated successfully!"
log "Key: ${SP_DIR}/${sp_key}"
log "Certificate: ${SP_DIR}/${sp_cert}"
log "Created files list written to: ${SP_CREATED_FILES_LIST}"
}
create_csr() {
log "Creating CSR for SP certificate"
local sp_key="stir_shaken_sp_key_${IDENTIFIER}.pem"
local sp_csr="stir_shaken_sp_csr_${IDENTIFIER}.pem"
openssl req -new -nodes -key ${sp_key} -keyform PEM -subj "/C=${CERT_COUNTRY}/ST=${CERT_STATE}/L=${CERT_LOCALITY}/O=${CERT_ORG}/OU=${CERT_OU}/CN=${CERT_CN}" -sha256 -out ${sp_csr}
log "Verifying CSR"
if [[ ${DEBUG_MODE} -eq 1 ]]; then
openssl req -in "${SP_DIR}/${sp_csr}" -text -noout
else
openssl req -in "${SP_DIR}/${sp_csr}" -noout > /dev/null 2>&1
fi
if [[ $? -ne 0 ]]; then
echo
log_error "ERROR: CSR verification failed."
echo
exit 1
fi
log "Setting proper permissions on generated files"
chmod 600 "${SP_DIR}/${sp_key}"
chmod 644 "${SP_DIR}/${sp_csr}"
echo "Key: ${SP_DIR}/${sp_key}" > "${SP_CREATED_FILES_LIST}"
echo "CSR: ${SP_DIR}/${sp_csr}" >> "${SP_CREATED_FILES_LIST}"
log "STIR/SHAKEN CSR generated successfully!"
log "Key: ${SP_DIR}/${sp_key}"
log "CSR: ${SP_DIR}/${sp_csr}"
log "Created files list written to: ${SP_CREATED_FILES_LIST}"
}
# Process script arguments
while getopts ":b:i:dhs" opt; do
case ${opt} in
h) usage;;
s) SELF_SIGNED=1;;
i) IDENTIFIER="${OPTARG}";;
d) DEBUG_MODE=1;;
b) BUILD_DIR="${OPTARG}";;
\?) log_error "Invalid option -${OPTARG}"; usage;;
esac
done
shift $((OPTIND -1))
# Set certificate subject information from positional arguments
CERT_COUNTRY="${1:-$CERT_COUNTRY}"
CERT_STATE="${2:-$CERT_STATE}"
CERT_LOCALITY="${3:-$CERT_LOCALITY}"
CERT_ORG="${4:-$CERT_ORG}"
CERT_OU="${5:-$CERT_OU}"
CERT_CN="${6:-$CERT_CN}"
validate_cert_info
make_build_dir
create_sp_key
if [[ ${SELF_SIGNED} -eq 1 ]]; then
create_self_signed_certs
else
create_csr
fi
Credit to the original source posts, here and here .