#!/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 "; 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 " --server_only [ True | False ]"; echo " --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