Created
June 25, 2025 00:03
-
-
Save thiago-rezende/d0730cc9abb7eb9a0dcdf99de1938ef8 to your computer and use it in GitHub Desktop.
self-signed certificates utility
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /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