From c2df6cd22dc4f2ecf04ebcef5493302ab3b6bdf0 Mon Sep 17 00:00:00 2001 From: Logan Date: Tue, 6 Apr 2021 16:31:50 -0700 Subject: [PATCH] Changes script name and adds symbolic link for backwards compatibility --- README.md | 14 +- self-signed-ssl | 550 +++++++++++++++++++++++++++++++++++++++++++++++ self-signed-tls | 551 +----------------------------------------------- 3 files changed, 558 insertions(+), 557 deletions(-) create mode 100755 self-signed-ssl mode change 100755 => 120000 self-signed-tls diff --git a/README.md b/README.md index dcd501b..0871164 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ brew install lstellway/formulae/self-signed-ssl **cURL** ``` -curl --output self-signed-tls https://raw.githubusercontent.com/lstellway/self-signed-ssl/master/self-signed-tls && chmod +x self-signed-tls +curl --output self-signed-ssl https://raw.githubusercontent.com/lstellway/self-signed-ssl/master/self-signed-ssl && chmod +x self-signed-ssl ```
@@ -28,22 +28,22 @@ curl --output self-signed-tls https://raw.githubusercontent.com/lstellway/self-s ## Usage ```shell -self-signed-tls [OPTIONS] +self-signed-ssl [OPTIONS] # Run with no arguments to be prompted for required values -self-signed-tls +self-signed-ssl # Only create a certificate authority and trust the generated certificate -self-signed-tls --ca-only --trust +self-signed-ssl --ca-only --trust # Only create a certificate signing request -self-signed-tls --csr-only +self-signed-ssl --csr-only # Generate a signed certificate using existing files -self-signed-tls --ca-key='/path/to/CA.key' --ca-cert='/path/to/CA.pem' --csr='/path/to/EXAMPLE.csr' +self-signed-ssl --ca-key='/path/to/CA.key' --ca-cert='/path/to/CA.pem' --csr='/path/to/EXAMPLE.csr' # Automate certificate generation -self-signed-tls --no-interaction -c 'US' -s 'California' -l 'Los Angeles' -o 'Example Org' -u 'Example Unit' -n 'example.com' -a 'www.example.com' +self-signed-ssl --no-interaction -c 'US' -s 'California' -l 'Los Angeles' -o 'Example Org' -u 'Example Unit' -n 'example.com' -a 'www.example.com' ```
diff --git a/self-signed-ssl b/self-signed-ssl new file mode 100755 index 0000000..f36312d --- /dev/null +++ b/self-signed-ssl @@ -0,0 +1,550 @@ +#!/bin/bash +# +# This script simplifies the creation of certificate authorities and +# self-signed TLS certificates using OpenSSL +# Execute with help flag (-h|--help) for documentation. + +set -e +VERSION="0.2.1" + +####################################### +# General defaults +####################################### +SCRIPT_NAME="$(basename "$0")" +OUT="$(pwd)/" +CREATE_PATH=0 +INTERACT=1 +BITS=2048 +EXIT_CODE=0 + +####################################### +# Certificate authority defaults +####################################### +CA_KEY="" +CA="" +CA_ONLY="" +TRUST="" +SUBJ="" + +####################################### +# Certificate signing request +####################################### +CSR="" +CSR_ONLY="" + +####################################### +# Certificate / subject defaults +####################################### +EXTFILE="" +DAYS=365 # 1 year +C="" +ST="" +L="" +O="" +OU="" +CN="" +HAS_CN="" +SAN="" +EMAIL="" + +####################################### +# Safely exit the script +# Arguments: +# Exit code (number) +####################################### +_safe_exit() { + trap - INT TERM EXIT + exit "$@" +} + +####################################### +# Check if packages are installed +# Arguments: +# List of commands (strings) to verify +# Outputs: +# Writes message to stderr and returns error code if package not present +####################################### +_require() { + while [ -n "$1" ]; do + if [ -z "$(command -v "$1")" ]; then + printf "Command '%s' not found\n" "${1}" + printf "Please ensure the program is installed and referenced in PATH variable\n" >&2 + return 1 + fi + shift + done +} + +####################################### +# Validate script requirements +# Arguments: +# None +# Outputs: +# Exits script if required package not found +####################################### +_check_requirements() { + # Require OpenSSL + _require "openssl" || _safe_exit 1 + + # Require sudo if trusting CA + [ -z "${TRUST}" ] || _require "sudo" || _safe_exit 1 +} + +####################################### +# Display the help Screen +# Arguments: +# None +# Outputs: +# Writes help to stdout +####################################### +_help() { + cat <&2 + _help + _safe_exit 1 + ;; + esac + done +} + +####################################### +# Build subject alternative names variable +# Arguments: +# None +####################################### +_build_san() { + # Subject Alternative Names + if [ -z "${SAN}" ] && [ -z "${HAS_CN}" ] && [ $INTERACT -eq 1 ]; then + printf "Subject Alternative Name(s) (e.g. subdomains) []: " + read -r SAN + fi + + # Build DNS + i=0 + read -r -a URLS <<< "${CN} ${SAN//,/ }" + SAN="" + for u in "${URLS[@]}"; do + if [ -n "${u}" ]; then + i=$((i+1)) + SAN="${SAN}DNS.${i} = ${u// /}"$'\n' + fi + done +} + +####################################### +# Build certificate subject +# Arguments: +# None +####################################### +_build_subj() { + if [ -n "${C}" ]; then + SUBJ="${SUBJ}/C=${C}" + fi + if [ -n "${ST}" ]; then + SUBJ="${SUBJ}/ST=${ST}" + fi + if [ -n "${L}" ]; then + SUBJ="${SUBJ}/L=${L}" + fi + if [ -n "${O}" ]; then + SUBJ="${SUBJ}/O=${O}" + fi + if [ -n "${OU}" ]; then + SUBJ="${SUBJ}/OU=${OU}" + fi + if [ -n "${CN}" ]; then + SUBJ="${SUBJ}/CN=${CN}" + fi + if [ -n "${EMAIL}" ]; then + SUBJ="${SUBJ}/emailAddress=${EMAIL}" + fi +} + +####################################### +# Check that required arguments were provided and update globals +# Arguments: +# None +####################################### +_validate_args() { + # Certificate authority files + if [ -n "${CA_KEY}" ] && [ -n "${CA}" ]; then + if [ ! -f "${CA_KEY}" ]; then + printf "The specified certificate authorify key file does not exist\n" >&2 + _safe_exit 1 + fi + + if [ ! -f "${CA}" ]; then + printf "The specified certificate authorify file does not exist\n" >&2 + _safe_exit 1 + fi + fi + + # Check CSR file + if [ -n "${CSR}" ] && [ ! -f "${CSR}" ]; then + printf "The specified certificate signing request file does not exist\n" >&2 + _safe_exit 1 + fi + + # Interactive-only arguments + if [ $INTERACT -eq 1 ]; then + # Country + if [ -z "${C}" ]; then + printf "Country Name (2 letter code) [AU]: " + read -r C + fi + + # State + if [ -z "${ST}" ]; then + printf "State or Province Name (full name) [Some-State]: " + read -r ST + fi + + # Locality + if [ -z "${L}" ]; then + printf "Locality Name (eg, city) []: " + read -r L + fi + + # Organization + if [ -z "${O}" ]; then + printf "Organization Name (eg, company) [Internet Widgits Pty Ltd]: " + read -r O + fi + + # Organizational Unit + if [ -z "${OU}" ]; then + printf "Organizational Unit Name (eg, section) []: " + read -r OU + fi + fi + + # Common Name + # This field is required and will ignore interactive rules + if [ -z "${CN}" ]; then + printf "Common Name (e.g. server FQDN or YOUR name) []: " + read -r CN + fi + + # File name used for generating files + FILE="${CN/\*\./}" + + # Build subject alternative names + _build_san + + # Email Address + if [ -z "${EMAIL}" ] && [ $INTERACT -eq 1 ]; then + printf "Email Address []: " + read -r EMAIL + fi + + # Make sure output directory ends with a directory separator + if [ "${OUT: -1}" != "/" ]; then + OUT="${OUT}/" + fi + + # Check if output directory exists + if [ ! -d "${OUT}" ]; then + if [ $CREATE_PATH -eq 1 ]; then + # Create path + mkdir -p "${OUT}" \ + || printf "Could not create directory '%s'\n" "${OUT}" >&2 \ + && _safe_exit 1 + else + # Report non-existent path + printf "The specified directory '%s' does not exist\n" "${OUT}" >&2 + _safe_exit 1 + fi + fi + + # Build certificate subject + _build_subj + + # Validate extfile if provided + if [ -n "${EXTFILE}" ] && [ ! -f "${EXTFILE}" ]; then + printf "The specified extfile '%s' does not exist\n" "${EXTFILE}" >&2 + _safe_exit 1 + fi +} + +####################################### +# Trust certificate authority on Linux system +# Arguments: +# Command (string) +# Directory (string) +####################################### +_trust_linux() { + # If command and directory exist + if [ -n "$(command -v "$1")" ] && [ -d "$(dirname "$2")" ]; then + printf "Installing certificate authority (requires sudo privileges)\n" + + # Add certificate if it doesn't exist & trust it + [ -f "$2" ] || sudo cp "${CA}" "$2" \ + && sudo "$1" \ + && return + fi + + return 1 +} + +####################################### +# Trust certificate authority +# Globals: +# OSTYPE +# Arguments: +# None +####################################### +_trust_ca() { + # Check if CA exists and script is instructed to trust + if [ -f "${CA}" ] && [ -n "${TRUST}" ]; then + if [[ "${OSTYPE}" == "darwin"* ]]; then + # MacOS (Darwin) + sudo security add-trusted-cert -d -r trustRoot \ + -k "/Library/Keychains/System.keychain" \ + "${CA}" \ + && return + elif [[ "${OSTYPE}" == "linux"* ]]; then + # Linux (Fedora/CentOS, Debian/Ubuntu) + _trust_linux "update-ca-trust" "/etc/pki/ca-trust/source/anchors/${FILE}-ca.pem" \ + || _trust_linux "update-ca-certificates" "/usr/local/share/ca-certificates/${FILE}-ca.crt" \ + && return + fi + + # Unsupported OS + printf "Error occurred while trusting certificate for OSTYPE '%s'\n" "${OSTYPE:-undefined}" >&2 + printf "Please ensure you are on a supported system and have the required packages installed.\n" >&2 + EXIT_CODE=1 + fi +} + +####################################### +# Generate certificate authority +# Arguments: +# None +####################################### +_build_ca() { + # Return early if CA args are provided or only generating CSR + if [ -n "${CA}" ] && [ -n "${CA_KEY}" ] || [ -n "${CSR_ONLY}" ]; then + return + fi + + printf "Building certificate authority\n" + + CA_KEY="${OUT}CA.key" + CA="${OUT}CA.pem" + + # Use existing CA in current directory + if [ -f "${CA_KEY}" ] && [ -f "${CA}" ]; then + return + fi + + # Generate certificate authority files + openssl genrsa -out "${CA_KEY}" "${BITS}" + openssl req -new -nodes -x509 -sha256 \ + -subj "${SUBJ}" \ + -days "${DAYS}" \ + -key "${CA_KEY}" \ + -out "${CA}" +} + +####################################### +# Generate certificate signing request +# Arguments: +# None +####################################### +_build_csr() { + # Return early if only building CA + if [ -n "${CA_ONLY}" ] || [ -f "${CSR}" ]; then + return + fi + + # Point to generated CSR file + CSR="${OUT}${FILE}.csr" + + # Generate key and certificate signing request + openssl genrsa -out "${OUT}${FILE}.key" "${BITS}" + openssl req -new -nodes -sha256 \ + -subj "${SUBJ}" \ + -newkey "rsa:${BITS}" \ + -key "${OUT}${FILE}.key" \ + -out "${CSR}" +} + +####################################### +# Generate signed certificate +# Arguments: +# None +####################################### +_build_cert() { + # Return early if only building CA or CSR + if [ -n "${CA_ONLY}" ] || [ -n "${CSR_ONLY}" ]; then + return + fi + + # Build extensions file if not provided + EXT="${EXTFILE}" + if [ ! -f "${EXTFILE}" ]; then + EXT="${OUT}${FILE}.ext" + + cat > "${EXT}" <&2 - return 1 - fi - shift - done -} - -####################################### -# Validate script requirements -# Arguments: -# None -# Outputs: -# Exits script if required package not found -####################################### -_check_requirements() { - # Require OpenSSL - _require "openssl" || _safe_exit 1 - - # Require sudo if trusting CA - [ -z "${TRUST}" ] || _require "sudo" || _safe_exit 1 -} - -####################################### -# Display the help Screen -# Arguments: -# None -# Outputs: -# Writes help to stdout -####################################### -_help() { - cat <&2 - _help - _safe_exit 1 - ;; - esac - done -} - -####################################### -# Build subject alternative names variable -# Arguments: -# None -####################################### -_build_san() { - # Subject Alternative Names - if [ -z "${SAN}" ] && [ -z "${HAS_CN}" ] && [ $INTERACT -eq 1 ]; then - printf "Subject Alternative Name(s) (e.g. subdomains) []: " - read -r SAN - fi - - # Build DNS - i=0 - read -r -a URLS <<< "${CN} ${SAN//,/ }" - SAN="" - for u in "${URLS[@]}"; do - if [ -n "${u}" ]; then - i=$((i+1)) - SAN="${SAN}DNS.${i} = ${u// /}"$'\n' - fi - done -} - -####################################### -# Build certificate subject -# Arguments: -# None -####################################### -_build_subj() { - if [ -n "${C}" ]; then - SUBJ="${SUBJ}/C=${C}" - fi - if [ -n "${ST}" ]; then - SUBJ="${SUBJ}/ST=${ST}" - fi - if [ -n "${L}" ]; then - SUBJ="${SUBJ}/L=${L}" - fi - if [ -n "${O}" ]; then - SUBJ="${SUBJ}/O=${O}" - fi - if [ -n "${OU}" ]; then - SUBJ="${SUBJ}/OU=${OU}" - fi - if [ -n "${CN}" ]; then - SUBJ="${SUBJ}/CN=${CN}" - fi - if [ -n "${EMAIL}" ]; then - SUBJ="${SUBJ}/emailAddress=${EMAIL}" - fi -} - -####################################### -# Check that required arguments were provided and update globals -# Arguments: -# None -####################################### -_validate_args() { - # Certificate authority files - if [ -n "${CA_KEY}" ] && [ -n "${CA}" ]; then - if [ ! -f "${CA_KEY}" ]; then - printf "The specified certificate authorify key file does not exist\n" >&2 - _safe_exit 1 - fi - - if [ ! -f "${CA}" ]; then - printf "The specified certificate authorify file does not exist\n" >&2 - _safe_exit 1 - fi - fi - - # Check CSR file - if [ -n "${CSR}" ] && [ ! -f "${CSR}" ]; then - printf "The specified certificate signing request file does not exist\n" >&2 - _safe_exit 1 - fi - - # Interactive-only arguments - if [ $INTERACT -eq 1 ]; then - # Country - if [ -z "${C}" ]; then - printf "Country Name (2 letter code) [AU]: " - read -r C - fi - - # State - if [ -z "${ST}" ]; then - printf "State or Province Name (full name) [Some-State]: " - read -r ST - fi - - # Locality - if [ -z "${L}" ]; then - printf "Locality Name (eg, city) []: " - read -r L - fi - - # Organization - if [ -z "${O}" ]; then - printf "Organization Name (eg, company) [Internet Widgits Pty Ltd]: " - read -r O - fi - - # Organizational Unit - if [ -z "${OU}" ]; then - printf "Organizational Unit Name (eg, section) []: " - read -r OU - fi - fi - - # Common Name - # This field is required and will ignore interactive rules - if [ -z "${CN}" ]; then - printf "Common Name (e.g. server FQDN or YOUR name) []: " - read -r CN - fi - - # File name used for generating files - FILE="${CN/\*\./}" - - # Build subject alternative names - _build_san - - # Email Address - if [ -z "${EMAIL}" ] && [ $INTERACT -eq 1 ]; then - printf "Email Address []: " - read -r EMAIL - fi - - # Make sure output directory ends with a directory separator - if [ "${OUT: -1}" != "/" ]; then - OUT="${OUT}/" - fi - - # Check if output directory exists - if [ ! -d "${OUT}" ]; then - if [ $CREATE_PATH -eq 1 ]; then - # Create path - mkdir -p "${OUT}" \ - || printf "Could not create directory '%s'\n" "${OUT}" >&2 \ - && _safe_exit 1 - else - # Report non-existent path - printf "The specified directory '%s' does not exist\n" "${OUT}" >&2 - _safe_exit 1 - fi - fi - - # Build certificate subject - _build_subj - - # Validate extfile if provided - if [ -n "${EXTFILE}" ] && [ ! -f "${EXTFILE}" ]; then - printf "The specified extfile '%s' does not exist\n" "${EXTFILE}" >&2 - _safe_exit 1 - fi -} - -####################################### -# Trust certificate authority on Linux system -# Arguments: -# Command (string) -# Directory (string) -####################################### -_trust_linux() { - # If command and directory exist - if [ -n "$(command -v "$1")" ] && [ -d "$(dirname "$2")" ]; then - printf "Installing certificate authority (requires sudo privileges)\n" - - # Add certificate if it doesn't exist & trust it - [ -f "$2" ] || sudo cp "${CA}" "$2" \ - && sudo "$1" \ - && return - fi - - return 1 -} - -####################################### -# Trust certificate authority -# Globals: -# OSTYPE -# Arguments: -# None -####################################### -_trust_ca() { - # Check if CA exists and script is instructed to trust - if [ -f "${CA}" ] && [ -n "${TRUST}" ]; then - if [[ "${OSTYPE}" == "darwin"* ]]; then - # MacOS (Darwin) - sudo security add-trusted-cert -d -r trustRoot \ - -k "/Library/Keychains/System.keychain" \ - "${CA}" \ - && return - elif [[ "${OSTYPE}" == "linux"* ]]; then - # Linux (Fedora/CentOS, Debian/Ubuntu) - _trust_linux "update-ca-trust" "/etc/pki/ca-trust/source/anchors/${FILE}-ca.pem" \ - || _trust_linux "update-ca-certificates" "/usr/local/share/ca-certificates/${FILE}-ca.crt" \ - && return - fi - - # Unsupported OS - printf "Error occurred while trusting certificate for OSTYPE '%s'\n" "${OSTYPE:-undefined}" >&2 - printf "Please ensure you are on a supported system and have the required packages installed.\n" >&2 - EXIT_CODE=1 - fi -} - -####################################### -# Generate certificate authority -# Arguments: -# None -####################################### -_build_ca() { - # Return early if CA args are provided or only generating CSR - if [ -n "${CA}" ] && [ -n "${CA_KEY}" ] || [ -n "${CSR_ONLY}" ]; then - return - fi - - printf "Building certificate authority\n" - - CA_KEY="${OUT}CA.key" - CA="${OUT}CA.pem" - - # Use existing CA in current directory - if [ -f "${CA_KEY}" ] && [ -f "${CA}" ]; then - return - fi - - # Generate certificate authority files - openssl genrsa -out "${CA_KEY}" "${BITS}" - openssl req -new -nodes -x509 -sha256 \ - -subj "${SUBJ}" \ - -days "${DAYS}" \ - -key "${CA_KEY}" \ - -out "${CA}" -} - -####################################### -# Generate certificate signing request -# Arguments: -# None -####################################### -_build_csr() { - # Return early if only building CA - if [ -n "${CA_ONLY}" ] || [ -f "${CSR}" ]; then - return - fi - - # Point to generated CSR file - CSR="${OUT}${FILE}.csr" - - # Generate key and certificate signing request - openssl genrsa -out "${OUT}${FILE}.key" "${BITS}" - openssl req -new -nodes -sha256 \ - -subj "${SUBJ}" \ - -newkey "rsa:${BITS}" \ - -key "${OUT}${FILE}.key" \ - -out "${CSR}" -} - -####################################### -# Generate signed certificate -# Arguments: -# None -####################################### -_build_cert() { - # Return early if only building CA or CSR - if [ -n "${CA_ONLY}" ] || [ -n "${CSR_ONLY}" ]; then - return - fi - - # Build extensions file if not provided - EXT="${EXTFILE}" - if [ ! -f "${EXTFILE}" ]; then - EXT="${OUT}${FILE}.ext" - - cat > "${EXT}" <