Skip to content

Instantly share code, notes, and snippets.

@thiago-rezende
Created June 25, 2025 00:03
Show Gist options
  • Save thiago-rezende/d0730cc9abb7eb9a0dcdf99de1938ef8 to your computer and use it in GitHub Desktop.
Save thiago-rezende/d0730cc9abb7eb9a0dcdf99de1938ef8 to your computer and use it in GitHub Desktop.
self-signed certificates utility
#! /usr/bin/env bash
#
# ┌─┐┌─┐┬ ┌─┐ ┌─┐┬┌─┐┌┐┌┌─┐┌┬┐
# └─┐├┤ │ ├┤───└─┐││ ┬│││├┤ ││
# └─┘└─┘┴─┘└ └─┘┴└─┘┘└┘└─┘─┴┘
#
# Filename: self-signed.sh
# GitHub: https://github.com/thiago-rezende
# Maintainer: Thiago Rezende <[email protected]>
# script variables
script_name=`basename "$0"`
script_version='v1.0.0'
script_description='self-signed certificates'
# script directories
script_directory=`dirname "$0"`
script_logs_directory=${SCRIPT_LOGS_DIRECTORY:-"${script_directory}/logs"}
# script verbosity variables
script_verbose_output=${SCRIPT_VERBOSE_OUTPUT:-1}
# positional arguments
positional_arguments=()
# ansi colors
declare -r \
ansi_red='\033[31m' \
ansi_blue='\033[34m' \
ansi_cyan='\033[36m' \
ansi_gray='\033[30m' \
ansi_white='\033[37m' \
ansi_green='\033[32m' \
ansi_yellow='\033[33m' \
ansi_magenta='\033[35m' \
ansi_red_bold='\033[0;31;1m' \
ansi_blue_bold='\033[0;34;1m' \
ansi_cyan_bold='\033[0;36;1m' \
ansi_black_bold='\033[0;30;1m' \
ansi_green_bold='\033[0;32;1m' \
ansi_white_bold='\033[0;37;1m' \
ansi_yellow_bold='\033[0;33;1m' \
ansi_magenta_bold='\033[0;35;1m' \
ansi_reset='\033[0m'
# forced operations
forced_operations=0
# authority variables
authority__folder="${script_directory}/authority"
authority__config="${authority__folder}/authority.conf"
authority__key="${authority__folder}/authority.key"
authority__crt="${authority__folder}/authority.crt"
authority__srl="${authority__folder}/authority.slr"
authority__key__size="${AUTHORITY__KEY__SIZE:-"4096"}"
authority__expiration="${AUTHORITY__EXPIRATION:-"365"}"
authority__state="${AUTHORITY__STATE:-"State"}"
authority__country="${AUTHORITY__COUNTRY:-"00"}"
authority__locality="${AUTHORITY__LOCALITY:-"Locality"}"
authority__common_name="${AUTHORITY__COMMON_NAME:-"Common Name"}"
authority__organization="${AUTHORITY__ORGANIZATION:-"Organization"}"
authority__organizational_unit="${AUTHORITY__ORGANIZATIONAL_UNIT:-"Organizational Unit"}"
# certificates variables
certificates__folder="${script_directory}/certificates"
certificates__key__size="${CERTIFICATES__KEY__SIZE:-"4096"}"
certificates__expiration="${CERTIFICATES__EXPIRATION:-"365"}"
certificates__state="${CERTIFICATES__STATE:-"State"}"
certificates__country="${CERTIFICATES__COUNTRY:-"00"}"
certificates__locality="${CERTIFICATES__LOCALITY:-"Locality"}"
certificates__common_name="${CERTIFICATES__COMMON_NAME:-"Common Name"}"
certificates__organization="${CERTIFICATES__ORGANIZATION:-"Organization"}"
certificates__organizational_unit="${CERTIFICATES__ORGANIZATIONAL_UNIT:-"Organizational Unit"}"
# script usage message
script__usage() {
echo -e "[$ansi_green_bold $script_name $ansi_reset]$ansi_white <$ansi_yellow_bold ${script_version}$ansi_reset$ansi_white > $ansi_reset"
echo -e " $ansi_blue_bold $script_description $ansi_reset"
echo -e ""
echo -e "[$ansi_white_bold usage $ansi_reset]"
echo -e " $ansi_green $script_name $ansi_white utility $ansi_yellow command $ansi_magenta < argument > $ansi_cyan [ options ] $ansi_reset"
echo -e ""
echo -e "[$ansi_white_bold utilities $ansi_reset]"
echo -e " $ansi_white help $ansi_reset - show this help message"
echo -e " $ansi_white certificate $ansi_reset - execute the$ansi_white certificate$ansi_reset utility"
echo -e ""
echo -e "[$ansi_white_bold certificate $ansi_reset]"
echo -e " $ansi_yellow wildcard $ansi_reset $ansi_magenta < domain > $ansi_reset - generate a wildcard certificate"
echo -e " $ansi_yellow authority $ansi_reset - generate the certificate authority"
echo -e ""
echo -e "[$ansi_white_bold options $ansi_reset]"
echo -e " $ansi_cyan --quiet $ansi_reset - reduce verbosity"
echo -e " $ansi_cyan --forced $ansi_reset - forced operations"
echo -e ""
exit 0
}
# generic validation error
invalid() {
local kind=$1
local object=$2
local ansi_color=${3:-"${ansi_yellow}"}
if [ -z "$object" ]; then
script__usage
else
echo >&2 -e "[$ansi_red error $ansi_reset] invalid $kind '$ansi_color $object $ansi_reset'"
echo >&2 -e "|- run '$ansi_green ${script_name}$ansi_white help $ansi_reset' to check the script usage"
fi
exit 1
}
# generic missing error
missing() {
local kind=$1
local ansi_color=${3:-"${ansi_yellow}"}
echo >&2 -e "[$ansi_red error $ansi_reset] missing '$ansi_color $kind $ansi_reset'"
echo >&2 -e "|- run '$ansi_green ${script_name}$ansi_white help $ansi_reset' to check the script usage"
exit 1
}
# generic failure procedure
failure() {
local utility=$1
local command=$2
local output=$3
echo >&2 -e "[$ansi_red error $ansi_reset] utility '$ansi_white $utility $ansi_reset' failed on '$ansi_yellow $command $ansi_reset'"
echo >&2 -e "|- [$ansi_white log $ansi_reset] check '$ansi_yellow $output $ansi_reset' for more information"
exit 1
}
# verbosity setup
verbosity__setup() {
if [ "${script_verbose_output}" -ne 0 ]; then
exec 3>&1
else
exec 3>/dev/null
fi
}
# logging setup
logging__setup() {
if [ -d $script_logs_directory ]; then
return
fi
echo >&3 -e "[$ansi_white logging $ansi_reset] settnig up the '$ansi_cyan logging $ansi_reset' environment"
echo >&3 -e "|- [$ansi_white mkdir $ansi_reset] creating the '$ansi_yellow ${script_logs_directory}$ansi_reset ' directory"
mkdir >&/tmp/${script_name%.*}__setup__logging__mkdir.log -p $script_logs_directory
if [ $? -ne 0 ]; then
failure "setup" "logging" "/tmp/${script_name%.*}__setup__logging__mkdir.log"
fi
}
# certificate handler
certificate__handler() {
logging__setup
certificate__setup
local command=$1
case $command in
wildcard) certificate__wildcard "${@:2}";;
authority) certificate__authority "${@:2}";;
*) if [ -z $command ]; then missing "command" $ansi_yellow; else invalid "command" $command $ansi_yellow; fi;;
esac
}
# certificate setup
certificate__setup() {
echo >&3 -e "[$ansi_white certificate $ansi_reset] setting up the '$ansi_yellow certificate $ansi_reset' environment"\
if [ ! -d "${authority__folder}" ]; then
echo >&3 -e "|- [$ansi_white mkdir $ansi_reset] creating the '$ansi_yellow ${authority__folder}$ansi_reset ' directory"
mkdir >&${script_logs_directory}/certificate__mkdir__authority.log -p ${authority__folder}
if [ $? -ne 0 ]; then
failure "certificate" "setup" "${script_logs_directory}/certificate__mkdir__authority.log"
fi
fi
if [ ! -d "${certificates__folder}" ]; then
echo >&3 -e "|- [$ansi_white mkdir $ansi_reset] creating the '$ansi_yellow ${certificates__folder}$ansi_reset ' directory"
mkdir >&${script_logs_directory}/certificate__mkdir__certificates.log -p ${certificates__folder}
if [ $? -ne 0 ]; then
failure "certificate" "setup" "${script_logs_directory}/certificate__mkdir__certificates.log"
fi
fi
}
# certificate wildcard config
__certificate__wildcard__config() {
local domain=$1
local wildcard__domain=$2
local wildcard__folder=$3
local wildcard__config=$4
echo >&3 -e "[$ansi_white certificate $ansi_reset] generating certificate '$ansi_yellow wildcard $ansi_reset' config"
if [ ! -d "${wildcard__folder}" ]; then
echo >&3 -e "|- [$ansi_white mkdir $ansi_reset] creating the '$ansi_yellow ${wildcard__folder}$ansi_reset ' directory"
mkdir >&${script_logs_directory}/certificate__wildcard__config__mkdir.log -p ${wildcard__folder}
if [ $? -ne 0 ]; then
failure "certificate" "wildcard" "${script_logs_directory}/certificate__wildcard__config__mkdir.log"
fi
fi
if [ $forced_operations -eq 0 ] && [ -f "$wildcard__config" ]; then
echo >&3 -e "|- [$ansi_blue info $ansi_reset] config '$ansi_yellow $wildcard__config $ansi_reset' already exists"
return
fi
echo >&3 -e "|- [$ansi_white cat $ansi_reset] saving config to '$ansi_yellow $wildcard__config $ansi_reset'"
cat > "$wildcard__config" <<EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
ST = $certificates__state
C = $certificates__country
L = $certificates__locality
CN = $certificates__common_name
O = $certificates__organization
OU = $certificates__organizational_unit
[v3_req]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = $domain
DNS.2 = $wildcard__domain
EOF
}
# certificate wildcard
certificate__wildcard() {
certificate__authority
local domain=${1:-"localhost"}
local wildcard__domain="*.${domain}"
local wildcard__folder="${certificates__folder}/${domain}"
local wildcard__config="${wildcard__folder}/${domain}.conf"
__certificate__wildcard__config $domain $wildcard__domain $wildcard__folder $wildcard__config
local wildcard__key="${wildcard__folder}/${domain}.key"
local wildcard__crt="${wildcard__folder}/${domain}.crt"
local wildcard__csr="${wildcard__folder}/${domain}.csr"
local wildcard__pem="${wildcard__folder}/${domain}.pem"
echo >&3 -e "[$ansi_white certificate $ansi_reset] generating a '$ansi_yellow wildcard $ansi_reset' certificate"
if [ $forced_operations -eq 0 ] && ([ -f "$wildcard__key" ] || [ -f "$wildcard__crt" ] || [ -f "$wildcard__pem" ] || [ -f "$wildcard__csr" ]); then
echo >&3 -e "|- [$ansi_blue info $ansi_reset] '$ansi_yellow wildcard $ansi_reset' certificate already exists"
return
fi
echo >&3 -e "|- [$ansi_white openssl $ansi_reset] generating '$ansi_yellow $wildcard__key $ansi_reset'"
openssl >&${script_logs_directory}/certificate__wildcard__openssl__genrsa.log genrsa -out "$wildcard__key" $certificates__key__size
if [ $? -ne 0 ]; then
failure "certificate" "wildcard" "${script_logs_directory}/certificate__wildcard__openssl__genrsa.log"
fi
echo >&3 -e "|- [$ansi_white openssl $ansi_reset] generating '$ansi_yellow $wildcard__csr $ansi_reset'"
openssl >&${script_logs_directory}/certificate__wildcard__openssl__req__csr.log req -new -key "$wildcard__key" -out "$wildcard__csr" -config "$wildcard__config"
if [ $? -ne 0 ]; then
failure "certificate" "wildcard" "${script_logs_directory}/certificate__wildcard__openssl__req__csr.log"
fi
echo >&3 -e "|- [$ansi_white openssl $ansi_reset] generating '$ansi_yellow $wildcard__crt $ansi_reset'"
openssl >&${script_logs_directory}/certificate__wildcard__openssl__req__crt.log x509 -req -in "$wildcard__csr" -CA "$authority__crt" -CAkey "$authority__key" -CAcreateserial -out "$wildcard__crt" -days "$certificates__expiration" -sha256 -extfile "$wildcard__config" -extensions v3_req
if [ $? -ne 0 ]; then
failure "certificate" "wildcard" "${script_logs_directory}/certificate__wildcard__openssl__req__crt.log"
fi
echo >&3 -e "|- [$ansi_white cat $ansi_reset] generating '$ansi_yellow $wildcard__pem $ansi_reset'"
cat $wildcard__key $wildcard__crt > $wildcard__pem
if [ $? -ne 0 ]; then
failure "certificate" "wildcard" "${script_logs_directory}/certificate__wildcard__cat__pem.log"
fi
}
# certificate authority config
__certificate__authority__config() {
echo >&3 -e "[$ansi_white certificate $ansi_reset] generating certificate '$ansi_yellow authority $ansi_reset' config"
if [ $forced_operations -eq 0 ] && [ -f "$authority__config" ]; then
echo >&3 -e "|- [$ansi_blue info $ansi_reset] config '$ansi_yellow $authority__config $ansi_reset' already exists"
return
fi
echo >&3 -e "|- [$ansi_white cat $ansi_reset] saving config to '$ansi_yellow $authority__config $ansi_reset'"
cat > "$authority__config" <<EOF
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_ca
prompt = no
[req_distinguished_name]
ST = $authority__state
C = $authority__country
L = $authority__locality
CN = $authority__common_name
O = $authority__organization
OU = $authority__organizational_unit
[v3_ca]
basicConstraints = critical, CA:true
keyUsage = critical, keyCertSign, cRLSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
EOF
}
# certificate authority
certificate__authority() {
__certificate__authority__config
echo >&3 -e "[$ansi_white certificate $ansi_reset] generating certificate '$ansi_yellow authority $ansi_reset'"
if [ $forced_operations -eq 0 ] && ([ -f "$authority__key" ] || [ -f "$authority__crt" ]); then
echo >&3 -e "|- [$ansi_blue info $ansi_reset] certificate '$ansi_yellow authority $ansi_reset' already exists"
return
fi
echo >&3 -e "|- [$ansi_white openssl $ansi_reset] generating '$ansi_yellow $authority__key $ansi_reset'"
openssl >&${script_logs_directory}/certificate__authority__openssl__genrsa.log genrsa -out "$authority__key" $authority__key__size
if [ $? -ne 0 ]; then
failure "certificate" "authority" "${script_logs_directory}/certificate__authority__openssl__genrsa.log"
fi
echo >&3 -e "|- [$ansi_white openssl $ansi_reset] generating '$ansi_yellow $authority__crt $ansi_reset'"
openssl >&${script_logs_directory}/certificate__authority__openssl__req.log req -x509 -new -nodes -key "$authority__key" -sha256 -days "$authority__expiration" -out "$authority__crt" -config $authority__config -extensions v3_ca
if [ $? -ne 0 ]; then
failure "certificate" "authority" "${script_logs_directory}/certificate__authority__openssl__req.log"
fi
}
# options handler
while [ $# -gt 0 ]; do
case $1 in
# quiet option
--quiet) script_verbose_output=0; shift;;
# forced option
--forced) forced_operations=1; shift;;
# usage option
-h|--help) script__usage;;
# invalid option
-*|--*) invalid "option" $1 $ansi_cyan;;
# positional argument
*) positional_arguments+=("$1"); shift;;
esac
done
set -- "${positional_arguments[@]}"
# verbosity setup
verbosity__setup
# argument handler
case $1 in
help) script__usage;;
certificate) certificate__handler "${@:2}";;
*) invalid "utility" ${1:-""} $ansi_white;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment