Skip to content

Instantly share code, notes, and snippets.

@AlexAtkinson
Last active October 3, 2024 17:54
Show Gist options
  • Save AlexAtkinson/98efb3718e493abd263c32a0cd5032e6 to your computer and use it in GitHub Desktop.
Save AlexAtkinson/98efb3718e493abd263c32a0cd5032e6 to your computer and use it in GitHub Desktop.
Create a CA & Server Cert
#!/usr/bin/env bash
# ----------------------------------------------------------------------------------------------------------------------
#
# make-ca-cert.sh
#
# SYNOPSIS
# Creates a CA if one doesn't already exist, and installs it (debian).
# Creats a server certificate (including wildcard SAN) signed by the CA.
#
# NOTE
# See here for an example project that makes use of this script.
# https://github.com/AlexAtkinson/fetch-cors-cookies
#
# ----------------------------------------------------------------------------------------------------------------------
# Environment Settings
# ----------------------------------------------------------------------------------------------------------------------
# none
# ----------------------------------------------------------------------------------------------------------------------
# Variables
# ----------------------------------------------------------------------------------------------------------------------
THIS_SCRIPT="${0##*/}"
DIR_NAME="${PWD##*/}"
PARENT_DIR_PATH="${PWD%/*}"
PARENT_DIR_NAME="${PARENT_DIR_PATH##*/}"
RC_LOG="false"
[[ "$RC_LOG" == "true" ]] && LOG_FILE="${THIS_SCRIPT}.log"
IFS_BAK=$IFS
# ----------------------------------------------------------------------------------------------------------------------
# Functions
# ----------------------------------------------------------------------------------------------------------------------
show_help() {
echo -e "$(cat << EOF
NAME
${0##*/} - Manage self signed certs for this localhost.
SYNOPSIS
${0##*/} [-ndsh]
DESCRIPTION
-n \e[4mNAME\e[0m
The version to be released. Requies parameter: '-c'
-d \e[4mDAYS\e[0m
The duration in days that the certificate will be valid.
-s \e[4mSUBJ\e[0m
The subject for the certificate (in quotes). Example:
'/C=CA/ST=Ontario/L=Toronto/O=EvilKittens Co/OU=LifeQuality/CN=foo.co'
-h \e[4mHELP\e[0m
Show this help menu.
EXAMPLES:
Generate a self signed cert for hell.com:
./make-ca-cert.sh -n hell.com -d 3650 -s '/C=CA/ST=Ontario/L=Toronto/O=Foo Co/OU=LifeQuality/CN=hell.com'
EOF
)
"
exit 1
}
# This function enables syslog style error code handling with colors.
# Named loggerx so as to avoid clobbering logger if present.
# Note: There is no 9th severity level in RFC5424.
function loggerx() {
[[ $1 -eq 0 ]] && echo -e "$(date --utc +"%FT%T.%3NZ") - \e[01;30;41mEMERGENCY\e[0m: ${*:2}"
[[ $1 -eq 1 ]] && echo -e "$(date --utc +"%FT%T.%3NZ") - \e[01;31;43mALERT\e[0m: ${*:2}"
[[ $1 -eq 2 ]] && echo -e "$(date --utc +"%FT%T.%3NZ") - \e[01;97;41mCRITICAL\e[0m: ${*:2}"
[[ $1 -eq 3 ]] && echo -e "$(date --utc +"%FT%T.%3NZ") - \e[01;31mERROR\e[0m: ${*:2}"
[[ $1 -eq 4 ]] && echo -e "$(date --utc +"%FT%T.%3NZ") - \e[01;33mWARNING\e[0m: ${*:2}"
[[ $1 -eq 5 ]] && echo -e "$(date --utc +"%FT%T.%3NZ") - \e[01;30;107mNOTICE\e[0m: ${*:2}"
[[ $1 -eq 6 ]] && echo -e "$(date --utc +"%FT%T.%3NZ") - \e[01;39mINFO\e[0m: ${*:2}"
[[ $1 -eq 7 ]] && echo -e "$(date --utc +"%FT%T.%3NZ") - \e[01;97;46mDEBUG\e[0m: ${*:2}"
[[ $1 -eq 9 ]] && echo -e "$(date --utc +"%FT%T.%3NZ") - \e[01;32mSUCCESS\e[0m: ${*:2}"
}
function et() { loggerx 5 "TASK START: $task..."; }
function rc_handler() {
if [[ $1 -eq $2 ]] ; then
loggerx 9 "TASK END: $task."
else
loggerx 3 "TASK END: $task - exit code $2"
[[ "$3" == "KILL" ]] && exit "$2"
fi
}
function rc() {
result=$?
if [[ "$RC_LOG" == "true" ]]; then
rc_handler "$1" "$result" | tee -a "$LOG_FILE"
else
rc_handler "$1" "$result" "$2"
fi
}
# ------------------------------------------------------------------------------
# Arguments
# ------------------------------------------------------------------------------
OPTIND=1
while getopts "hn:d:s:" opt; do
case $opt in
h)
show_help
;;
n)
arg_n='set'
NAME="$OPTARG"
;;
d)
arg_d='set'
DAYS="$OPTARG"
;;
s)
arg_s='set'
SUBJ="$OPTARG"
;;
*)
echo "ERROR: Invalid argument!"
show_help
;;
esac
done
shift $((OPTIND-1))
# ----------------------------------------------------------------------------------------------------------------------
# Sanities
# ----------------------------------------------------------------------------------------------------------------------
if [[ -z $NAME ]]; then loggerx 3 "Missing argument: -n"; show_help; fi
if [[ -z $DAYS ]]; then loggerx 3 "Missing argument: -d"; show_help; fi
if [[ -z $SUBJ ]]; then loggerx 3 "Missing argument: -s"; show_help; fi
# ----------------------------------------------------------------------------------------------------------------------
# Main Operations
# ----------------------------------------------------------------------------------------------------------------------
## Setup local CA if missing
if [[ ! -f "$(hostname)CA.key" ]]; then
loggerx 5 "No $(hostname)CA.key found. Setting up CA for this localhost: '$(hostname)'."
loggerx 4 "You will be promopted to create a password for this CA. Record it somewhere safe."
read -p "Press [ANY KEY] once you've read the above warning to continue..."
task="Create key: $(hostname)CA.key"; et
openssl genrsa -des3 -out "$(hostname)CA.key" 4096; rc 0 KILL
task="Create cert: $(hostname)CA.pem"; et
openssl req -x509 -new -nodes -key "$(hostname)CA.key" -sha256 -days 825 -out "$(hostname)CA.pem" ; rc 0 KILL
task="Installing self-signed root cert in /usr/local/share/ca-certificates/"; et
if -f /usr/local/share/ca-certificates/$(hostname)CA.crt; then
loggerx 4 "The file /usr/local/share/ca-certificates/$(hostname)CA.crt already exists!"
read -p "CONFIRM: Overwrite existing CA? [Y/N]: " ca_overwrite_answer
if [[ "$ca_overwrite_answer" == "Y" ]]; then
task="Delete file: /usr/local/share/ca-certificates/$(hostname)CA.crt"; et
rm -f /usr/local/share/ca-certificates/$(hostname)CA.crt; rc 0 KILL
task="Copy $(hostanme)CA.pem to /usr/local/share/ca-certificates/"; et
sudo cp "$(hostname)CA.pem" "/usr/local/share/ca-certificates/$(hostname)CA.crt"; rc 0 KILL
else
loggerx 2 "Exiting..."
exit 1
fi
fi
task="Copy $(hostanme)CA.pem to /usr/local/share/ca-certificates/"; et
sudo cp "$(hostname)CA.pem" "/usr/local/share/ca-certificates/$(hostname)CA.crt"; rc 0 KILL
task="Update CA Certificates (Note: sudo in use)"; et
sudo update-ca-certificates; rc 0 KILL
NEW_CA="true"
task="Installing self-signed root cert in /usr/local/share/ca-certificates/"; rc 0 KILL
fi
## Setup server certs
if [[ -d "certs/$NAME" ]]; then
loggerx 4 "The directory certs/$NAME already exists!"
read -p "CONFIRM: Overwrite Exiting Certs? [Y/N]: " cert_overwrite_answer
if [[ "$cert_overwrite_answer" == "Y" ]]; then
task="Delete directory: certs/$NAME"; et
rm -rf "certs/$NAME"; rc 0 KILL
else
loggerx 2 "Exiting..."
exit 1
fi
fi
if [[ ! -d "certs/$NAME" ]]; then
task="Creating directory: certs/$NAME"; et
mkdir "certs/$NAME"; rc 0 KILL
fi
task="Create private key: certs/$NAME/server.key"; et
openssl genrsa -out "certs/$NAME/server.key" 4096; rc 0 KILL
task="Create certificate signing request: certs/$NAME/server.csr"; et
openssl req -new \
-key "certs/$NAME/server.key" \
-subj "$SUBJ" \
-out "certs/$NAME/server.csr"; rc 0 KILL
task="Create extension file: certs/$NAME/cert.ext"; et
cat <<EOF >>"certs/$NAME/cert.ext"
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = $NAME
DNS.2 = *.$NAME
IP.1 = 127.0.0.1
EOF
rc 0 KILL
task="Create certificate: certs/$NAME/server.crt"; et
openssl x509 -req -in "certs/$NAME/server.csr" \
-CA "$(hostname)CA.pem" \
-CAkey "$(hostname)CA.key" \
-CAcreateserial \
-days "$DAYS" \
-sha256 \
-out "certs/$NAME/server.crt" \
-extfile "certs/$NAME/cert.ext"
rc 0 KILL
task="Verify certificate: certs/$NAME/server.crt against the CA cert"; et
openssl verify -CAfile "$(hostname)CA.pem" -verify_hostname "$NAME" "certs/$NAME/server.crt"; rc 0 KILL
printf "%s\n" "" "Once the following steps are complete, TLS will be valid for your local host:" \
" - Add the necessary entries in your '/etc/hosts' file. EG:" \
" 127.0.0.1 hell.com" \
" - Setup your server to host on port 443 and use the .crt and .key files found in 'certs/$NAME/'"
[[ "$NEW_CA" == "true" ]] && echo " - Restart your browser. Necessary because a new local CA has been trusted by your host."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment