#!/usr/bin/env bash ############################################################################## # HEADER # ############################################################################## # Script for generating SSL certificates. ############################################################################## # END_OF_HEADER # ############################################################################## usage() { echo "Script name: $0"; echo ""; echo "Usage: $0 --key-type [ RSA | EC | EX25519 ]"; echo " --curve [ P-256 | P-384 | P512 ]"; echo " --rsa-bits [ 2048 | 4096 ]"; echo " --days "; echo ""; echo "Options: "; echo " --key-type [ RSA | EC | EC25519 ]"; echo " Note: Pico W can use EC or RSA keys only, not EC25519,"; echo " and Tasmota can only use 2048 bit RSA keys."; echo " --curve [ P-256 | P-384 | P512 ]"; echo " --rsa-bits [ 2048 | 4096 ]"; echo " --days [ ]"; echo " --dir [ ]"; echo " --output-format [ PEM | DER ]"; echo " --user "; echo " --config "; echo " --ca_key "; echo " --ca_cert "; } TEMP="$(getopt -o hkcrd --long help,key-type:,curve:,rsa-bits:,days:,dir:,output-format:,user:,config:,ca_key:,ca_cert: -- "$@")" eval set -- "$TEMP" if [ $# == 0 ] ; then usage; exit 1; fi KEY_TYPE="EC" CURVE="P-256" RSA_BITS=2048 DAYS=365 MOSQUITTO_DIR="/home/erg/cert_test" OUTPUT_FORMAT= MOSQUITTO_USER= PKEYOPT= SSL_CNF= SSL_CA_KEY= SSL_CA_CRT= while true do case "$1" in -h|--help) usage exit 0; ;; --key-type) KEY_TYPE="$2"; echo "Key type: $KEY_TYPE"; shift 2;; # Note: Pico W can use EC or RSA keys only, not EC25519, and Tasmota can only use 2048 bit RSA keys. # Default choice of Key Types: RSA, EC, or ED25519 --curve) CURVE="$2"; echo "Curve: $CURVE"; shift 2;; # Choice of NIST Curves for EC Keys: P-256, P-384 or P-521 --rsa-bits) RSA_BITS="$2"; echo "RSA bits: $RSA_BITS"; shift 2;; # Choice of Bits for RSA Keys: 2048 or 4096 --days) DAYS="$2"; echo "Valid for: $DAYS"; shift 2;; # How many days is the Cert valid for? --dir) MOSQUITTO_DIR="$2"; echo "Working directory: $MOSQUITTO_DIR"; shift 2;; --output-format) OUTPUT_FORMAT="$2"; echo "Output format: $OUTPUT_FORMAT"; shift 2;; --user) MOSQUITTO_USER="$2"; echo "Mosquitto user: $MOSQUITTO_USER"; shift 2;; --config) SSL_CNF="$2"; shift 2;; --ca_key) SSL_CA_KEY="$2"; shift 2;; --ca_cert) SSL_CA_CRT="$2"; shift 2;; -- ) shift; break;; * ) break;; esac done ############################################################################## # Checks that sane arguments passed: # ############################################################################## # Check key type: if [ "$KEY_TYPE" != 'EC' ] && [ "$KEY_TYPE" != 'RSA' ] && [ "$KEY_TYPE" != 'EC25519' ]; then echo "Wrong key type specified: '$KEY_TYPE'" echo "Valid keys: [ EC | RSA | EC25519 ]" usage exit 1; fi # Set the specific PKEYOPT for the chosen algorithm (BLANK for ED25519) if [ "${KEY_TYPE}" == "EC" ]; then echo 'Create EC Key' PKEYOPT="-pkeyopt ec_paramgen_curve:${CURVE}" elif [ "${KEY_TYPE}" == "RSA" ]; then echo 'Create RSA Key' PKEYOPT="-pkeyopt rsa_keygen_bits:${RSA_BITS}" elif [ "${KEY_TYPE}" == "ED25519" ]; then echo 'Create ED25519 Key' PKEYOPT="" else echo "Setting -PKEYOPT to defaults: -pkeyopt ec_paramgen_curve:${CURVE}" fi # Check curve: if [ "$CURVE" != 'P-256' ] && [ "$CURVE" != 'P-384' ] && [ "$CURVE" != 'P-521' ]; then echo "Wrong NIST curve specified: '$CURVE'" echo "Curve must be one of: [ P-256 | P-384 | P-521 ]" usage exit 1; fi # Check RSA bits: if [ "$RSA_BITS" != '2048' ] && [ "$RSA_BITS" != '4096' ]; then echo "Wrong RSA bits specified: '$RSA_BITS'" echo "Must be one of: [ 2048 | 4096 ]" usage exit 1; fi # Check days parameter is a number: if [ -n "$DAYS" ] && [ "$DAYS" -eq "$DAYS" ] 2>/dev/null; then echo "Certificate will be valid for: '$DAYS' days" else echo "Not a number: $DAYS" usage exit 1; fi # Check output format: if [ "${OUTPUT_FORMAT}" == 'PEM' ] || [ "${OUTPUT_FORMAT}" == 'pem' ]; then # I: Double quote to prevent globbing and word splitting. OUTPUT_FORMAT="pem" elif [ "$OUTPUT_FORMAT" == 'DER' ] || [ "$OUTPUT_FORMAT" == 'der' ]; then # I: Double quote to prevent globbing and word splitting. OUTPUT_FORMAT="der" else echo "Incorrect certificate format type specified: $OUTPUT_FORMAT"; usage exit 1; fi # Check user supplied: if [ -z "$MOSQUITTO_USER" ] && [ "$MOSQUITTO_USER" != " " ]; then echo "Specified: '$MOSQUITTO_USER' as user." echo "Need to specify a non-empty username for the certificate!"; usage exit 1; fi # Check CA key and cert exist: if [ ! -f ${SSL_CA_CRT} ] || [ ! -f ${SSL_CA_KEY} ]; then echo "CA certificate does not exist, exiting!" exit 1 fi # Check CA certificate is still valid: # Date format we get from openssl: # Nov 18 13:47:56 2034 GMT # %b %d %H:%M:%S %Y %Z # compare dates from the CA cert and current one: CERT_VALID_UNTIL=$(openssl x509 -enddate -noout -in "${SSL_CA_CRT}" | cut -d'=' -f2) echo "CA certificate valid until: ${CERT_VALID_UNTIL}" if [ ! $(($(date -d "$CERT_VALID_UNTIL" +"%s") > $(date +"%s"))) ];then echo "CA certificate expired, exiting!" exit 1 fi ############################################################################## # Set up variables # ############################################################################## # Set the algorithm ALGORITHM="-algorithm ${KEY_TYPE}" # Output client key, CSR request and certificate CLIENT_KEY="${MOSQUITTO_DIR}/certs/clients/${MOSQUITTO_USER}/${MOSQUITTO_USER}_key.${OUTPUT_FORMAT}" CLIENT_CSR="${MOSQUITTO_DIR}/certs/clients/${MOSQUITTO_USER}/${MOSQUITTO_USER}_req.csr" CLIENT_CERT="${MOSQUITTO_DIR}/certs/clients/${MOSQUITTO_USER}/${MOSQUITTO_USER}_crt.${OUTPUT_FORMAT}" ############################################################################## # Locks # ############################################################################## LOCK_FILE=/tmp/$SUBJECT.lock if [ -f "$LOCK_FILE" ]; then echo "Script is already running" exit fi trap 'rm -f $LOCK_FILE' EXIT touch "$LOCK_FILE" ############################################################################## # Backup existing certs and create dir structure # ############################################################################## # If our user certs dir already exists, rename it so we don't # overwrite anything important. time_stamp=$(date +"%Y-%m-%d_%H-%M") mv "${MOSQUITTO_DIR}/certs/csr_files/${MOSQUITTO_USER}_req.csr" "${MOSQUITTO_DIR}/certs/clients/${MOSQUITTO_USER}" 2>/dev/null mv "${MOSQUITTO_DIR}/certs/clients/${MOSQUITTO_USER}req" "${MOSQUITTO_DIR}/certs/clients/${MOSQUITTO_USER}-${time_stamp}" 2>/dev/null mkdir -p "${MOSQUITTO_DIR}/certs/clients/${MOSQUITTO_USER}" ############################################################################## # Create the key in the requested format # ############################################################################## echo "Running:" echo " openssl genpkey ${ALGORITHM} ${PKEYOPT} \ -outform ${OUTPUT_FORMAT} \ -out ${CLIENT_KEY} \ -config ${SSL_CNF} " openssl genpkey ${ALGORITHM} ${PKEYOPT} \ -outform "${OUTPUT_FORMAT}" \ -out "${CLIENT_KEY}" \ -config "${SSL_CNF}" retval=$? if [ $retval -ne 0 ]; then echo "Generating key failed!" exit $retval fi printf '\n\n' echo "#######################################################################" printf '\n\n' echo "Successfully generated client key." ############################################################################## # Create the cert signing request # ############################################################################## echo "Running:" echo " openssl req \ -new \ -nodes \ -key $CLIENT_KEY \ -subj /CN=${MOSQUITTO_USER} \ -out $CLIENT_CSR \ -config ${SSL_CNF} " openssl req \ -new \ -nodes \ -key "${CLIENT_KEY}" \ -subj "/CN=${MOSQUITTO_USER}" \ -out "${CLIENT_CSR}" \ -config "${SSL_CNF}" retval=$? if [ $retval -ne 0 ]; then echo "Generating CSR request failed!" exit $retval fi echo "Successfully generated CSR request" printf '\n\n' echo "#######################################################################" printf '\n\n' ############################################################################## # Cert signing and creation # ############################################################################## echo "Running:" echo "openssl x509 \ -req \ -in ${CLIENT_CSR} \ -CA ${SSL_CA_CRT} \ -CAkey ${SSL_CA_KEY} \ -CAcreateserial \ -out ${CLIENT_CERT} \ -outform ${OUTPUT_FORMAT} \ -days ${DAYS} \ -config ${SSL_CNF} " openssl x509 \ -req \ -in "${CLIENT_CSR}" \ -CA "${SSL_CA_CRT}" \ -CAkey "${SSL_CA_KEY}" \ -CAcreateserial \ -out "${CLIENT_CERT}" \ -outform "${OUTPUT_FORMAT}" \ -days "${DAYS}" \ retval=$? if [ $retval -ne 0 ]; then echo "Signing CSR and certificate creation failed!" exit $retval fi echo "Successfully signed CSR and created client certificate." printf '\n\n' echo "#######################################################################" printf '\n\n' ############################################################################## # Check the cert # ############################################################################## printf '# This is your new client certificate\n\n\n' openssl x509 \ -text \ -in "${CLIENT_CERT}" \ -noout printf '\n\n' echo "#######################################################################" printf '\n\n'