diff options
Diffstat (limited to 'ca_maker_erg.sh')
-rwxr-xr-x | ca_maker_erg.sh | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/ca_maker_erg.sh b/ca_maker_erg.sh new file mode 100755 index 0000000..db53278 --- /dev/null +++ b/ca_maker_erg.sh @@ -0,0 +1,355 @@ +#!/bin/bash +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 <n>"; + 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 [ <integer> ]"; + echo " --dir [ <mosquitto directory> ]"; + echo " --output-format [ PEM | DER ]"; + echo " --server_only [ True | False ]"; + echo " --subjectAltName <subjectAltName>"; + echo " Example subjectAltName: DNS.1:example.com,DNS.2:192.168.1.167,IP.1:192.168.1.167'"; +} + +# This creates a very slimline CA and Server cert with a full SAN multihost server. +# The Mosquitto server requires keys and certs in PEM format, but it also exports +# the CA Cert in DER for use in clients such as the Pico W +TEMP="$(getopt -o h --long help,key-type:,curve:,rsa-bits:,ca_valid_for:,days:,dir:,output-format:,server_only:,subjectAltName: -- "$@")" + +eval set -- "$TEMP" + +if [ $# == 0 ] ; then + usage; + exit 1; +fi + +mosquitto_dir="/home/erg/cert_test_1" +key_type='EC' +curve='P-256' +rsa_bits='2048' +# Choice to encrypt the CA Key or not? either BLANK or cypher (preferably aes-256-cbc) +encryption='' +# encryption='-aes-256-cbc' +output_format='der' +# Multiple DNS names and IP Addresses. Note mbedtls can't currently use data from IP fields, +# but can read an IP from a DNS field, to the same effect. +# Use as many unique names as you need, in the format DNS.1, DNS.2, DNS.3, IP.1, IP.2 etc. +# subjectAltName='DNS.1:server_name,DNS.2:server_name.example.com,DNS.3:192.168.1.1,IP.1:192.168.1.1' +subjectAltName='DNS.1:example.com,DNS.2:192.168.0.167,IP.1:192.168.0.167' +ca_valid_for=3650 +certs_valid_for=365 +server_only="True" + +while true +do + case "$1" in + -h|--help) + usage + exit 0; + ;; + # 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 || ED25519 + --key-type) key_type="$2"; shift 2;; + # Choice of NIST Curves for EC Keys: P-256 || P-384 || P-521 + --curve) curve="$2"; shift 2;; + # Choice of Bits for RSA Keys: 2048 || 4096 + --rsa-bits) rsa_bits="$2"; shift 2;; + # How many days is the CA Cert valid for: + --ca_valid_for) ca_valid_for="$2"; shift;; + # How many days is the Cert valid for: + --days) certs_valid_for="$2"; shift 2;; + # Mosquitto directory: + --dir) mosquitto_dir="$2"; shift 2;; + # Output format: PEM || DER + --output-format) output_format="$2"; shift 2;; + # Only generate server certificate and key, not the CA. Defaults to True. + --server_only) server_only="$2";shift 2;; + --subjectAltName) subjectAltName="$2";shift 2;; + -- ) shift; break;; + * ) break;; + esac +done + +echo "subjectAltName you specified: $subjectAltName" + +if [ "$server_only" == "True" ] || [ "$server_only" == "true" ]|| [ "$server_only" == "Yes" ] || [ "$server_only" == 'yes' ]; then + server_only='True' +else + server_only='False' +fi + +# Mosquitto subdirectories: +mosquitto_dir_ca="${mosquitto_dir}/certs/CA" +mosquitto_dir_server="${mosquitto_dir}/certs/server" +mosquitto_dir_csr="${mosquitto_dir}/certs/csr_files" +ssl_ca_crt="${mosquitto_dir_ca}/ca_crt.${output_format}" +ssl_ca_key="${mosquitto_dir_ca}/ca_key.${output_format}" + +# NOTE: If you need to create one or more client Keys and Certs in either PEM or DER format, +# you can call the 'client_maker' script one or more times at the bottom of this script, so +# just scroll right to the end to add the details. You can call 'client_maker' independantly +# whenever you need more clients. + +############################################################# +# Check passed option values sane # +############################################################# +echo "Checking if CA dir exists: $mosquitto_dir_ca ..." +if [ ! -d $mosquitto_dir_ca ];then + echo "Certificate authority folder does not exist, exiting" + exit 1 +fi + +# Check CA key and cert exist: +echo "Checking CA key and cert exist: ..." +if [ ! -f ${ssl_ca_crt} ]; then # I: Double quote to prevent globbing and word splitting. + echo "CA certificate does not exist, exiting!" + exit 1 +fi +if [ ! -f "${mosquitto_dir_ca}/ca_key.pem" ] && [ ! -f "${mosquitto_dir_ca}/ca_key.der" ]; then + echo "CA key 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: +echo "Checking CA certificate valid ..." +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 +# 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 + +# 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 "$certs_valid_for" ] && [ "$certs_valid_for" -eq "$certs_valid_for" ] 2>/dev/null; then + echo "Certificate will be valid for: '$certs_valid_for' days" +else + echo "Not a number: $certs_valid_for" + 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 + + +############################################################ +# End of user defined variables +############################################################ + + +# Set the algorithm +algorithm="-algorithm ${key_type}" + +# Set the specific pkeyopt for the chosen algorithm (BLANK for ED25519) +if [ "${key_type}" == "EC" ]; then + echo 'Creating EC Key ...' + pkeyopt="-pkeyopt ec_paramgen_curve:${curve}" +elif [ "${key_type}" == "RSA" ]; then + echo 'Creating RSA Key ...' + pkeyopt="-pkeyopt rsa_keygen_bits:${rsa_bits}" +elif [ "${key_type}" == "ED25519" ]; then + echo 'Creating ED25519 Key' + pkeyopt="" +else + echo 'Key Type not found!' + exit 1 +fi + +############################################################ +# Backup existing certs and create dir structure +############################################################ + +# if certs dir already exists, rename it so we don't overwrite anything important +# but if it doesn't, then redirect the 'No such file or directory' error to null +time_stamp=$(date +"%Y-%m-%d_%H-%M") +if [ ! -d "${mosquitto_dir_ca}" ]; then + mkdir -p "${mosquitto_dir_ca}-${time_stamp}" 2>/dev/null +else + cp "${mosquitto_dir_ca}" "${mosquitto_dir_ca}-${time_stamp}" 2>/dev/null +fi + +if [ ! -d "${mosquitto_dir_server}" ]; then + mkdir -p "${mosquitto_dir_server}-${time_stamp}" 2>/dev/null +else + cp "${mosquitto_dir_server}" "${mosquitto_dir_server}-$time_stamp" 2>/dev/null +fi + +# create a sensible directory structure to store everything if not exists: +mkdir -p "${mosquitto_dir}"/certs/{DH,CA,server,clients,csr_files} + + +########################################################### +# dhparamfile Creation +########################################################### + +# Output DH parameters for safe prime group ffdhe2048 +if [ "$server_only" == "True" ]; then +openssl genpkey \ + -genparam \ + -algorithm DH \ + -pkeyopt group:ffdhe2048 \ + -out "$mosquitto_dir/certs/DH/dhp_ffdhe2048.pem" +fi + +########################################################### +# CA Creation +########################################################### + +if [ "${server_only}" == 'True' ]; then +openssl genpkey \ + $algorithm $pkeyopt \ + -outform pem \ + -out "$mosquitto_dir_ca/ca_key.pem" "$encryption" +fi + + +# Self sign the CA cert + +if [ "${server_only}" == 'True' ]; then +openssl req \ + -x509 \ + -new \ + -key "$mosquitto_dir_ca/ca_key.pem" \ + -days $ca_valid_for\ + -subj '/CN=MQTT CA' \ + -outform pem \ + -out "$mosquitto_dir_ca/ca_crt.pem" +fi + +########################################################### +# Server Creation +########################################################### + +# Create the Key +openssl genpkey \ + $algorithm $pkeyopt \ + -outform pem \ + -out "$mosquitto_dir_server/server_key.pem" + +# Create the certificate signing request (CSR) +openssl req \ + -new \ + -subj "/CN=MQTT Server" \ + -addext "subjectAltName = ${subjectAltName}" \ + -nodes \ + -key "$mosquitto_dir_server/server_key.pem" \ + -out "$mosquitto_dir_server/server_req.csr" + +# Sign and authenticate it with the CA +# We use copy_extensions to include the subjectAltNames from the CSR in the Server Cert +echo "Sign and authenticate CSR with the CA ..." +openssl x509 \ + -req \ + -in "$mosquitto_dir_server/server_req.csr" \ + -copy_extensions copy \ + -CA "${mosquitto_dir_ca}/ca_crt.pem" \ + -CAkey "${mosquitto_dir_ca}/ca_key.pem" \ + -CAcreateserial \ + -days "${certs_valid_for}" \ + -outform pem \ + -out "$mosquitto_dir_server/server_crt.pem" + + +########################################################### +# Key and Cert Checking +########################################################### + +# Examine the key to check that it looks OK +openssl pkey \ + -in "$mosquitto_dir_server/server_key.pem" \ + -text \ + -noout + +# Examine the CSR +openssl req \ + -in "$mosquitto_dir_server/server_req.csr" \ + -noout \ + -text + +# Validate the CA certificate +openssl x509 \ + -in "$mosquitto_dir_server/server_crt.pem" \ + -text \ + -noout + +#clean up after the server cert creation +mv "$mosquitto_dir_server/server_req.csr" "$mosquitto_dir_csr" + +########################################################### +# Key Export and Read Permissions fix +########################################################### + +#We need to export a copy of our CA Certificate in DER format for micropython. +openssl x509 \ + -in "$mosquitto_dir_ca/ca_crt.pem" \ + -out "$mosquitto_dir_ca/ca_crt.der" \ + -outform "$output_format" + +# and save a copy of it somewhere useful +cp \ + "$mosquitto_dir_ca/ca_crt.der" \ + "$mosquitto_dir/certs/clients" + +# but pem based clients need the pem version of it too +cp \ + "$mosquitto_dir_ca/ca_crt.pem" \ + "$mosquitto_dir/certs/clients" + +# We also need to give read access to server_key.pem so it can be used by Mosquitto +chmod 644 "$mosquitto_dir_server/server_key.pem" + + +# move the ca_maker script to certs/CA and remove its execute permissions +# to reduce the probability of running it by accident again +# and overwriting everything in your certs directory +#mv $mosquitto_dir/ca_maker $mosquitto_dir/certs/CA/ca_maker +#chmod -x $mosquitto_dir/certs/CA/ca_maker + +# auto run the client_maker pem/der username +# ./client_maker der pico +#client_maker pem user2 +#client_maker pem user3 +#client_maker der user4 |