own-pki/bin/generate-cert.sh
Gilles Mouchet 11b848c3f2 1.0.0
2026-04-30 06:48:53 +02:00

330 lines
11 KiB
Bash
Executable File

#!/bin/bash
#############################################################
# Script name: create-cert.sh
# Author: Gilles Mouchet (gilles.mouchet@gmail.com
# Version: 1.0.0
# Description: Create a cert
# License: GNU GPL v3
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# Changelog
# [1.0.0] - 2026-04-12
# Added:
# - generating private key
# - generating Certificate Signing Request (csr) file
# - signing the certificate with the CA
# Project initialization
# - initialization by gilles.mouchet@gmail.com
#
############################################################
#
version=1.0.0
############################################################
# FUNCTIONS
############################################################
#-----------------------------------------------------------
# display usage
usage() {
cat << EOF
Usage: $0 -c <ca_cert> -k <ca_key> -n <common_name> [-d <dns1,dns2>] [-i <ip1,ip2>] [-t <days>]"
Template script
Options:
-c, --ca-name <ca_name> - ca name (./info-cert.sh -c get list of CA name)
-n, --commonName <cn> - common name fqdn format (server.domain.ext)
-d, --dns <dns1,dns2,dnsx> - subject alternative name (multiple SAN separated by commas)
-i, --ip <ip1,ipx> - ip address to add to the certificate (multiple IPs separated by commas)
-t, --days <days> - validity period of the certificate in days (defaults $days days)
-y, --assumeyes - automatically answer yes for all questions.
-h, --help - show this help
-v, --version - show script version
Examples:
Generate a certificate for myweb.gmolab.net
./$(basename "$0") -c gmolab_ca -n myweb.gmolab.net
Generate a certifciate for myweb.gmolab.net with dns alias and ip
./$(basename "$0") -c gmolab_ca -n myweb.gmolab.net -i 92.168.1.10,10.0.0.5,10.10.34.25 --dns www.gmolab.net,qual-myweb.gmolab.net -t 49
EOF
}
############################################################
# MAIN
############################################################
main(){
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
# read library
source "$ROOT_DIR/lib/stdlib.sh"
# init config
init_default
init_env
# set color
set_color
# check if script is runnibf with sudo
check_sudo
# check if param exist
if [ -z "$1" ]; then
usage
exit 1
fi
# set color
set_color
# parse cli parameters
while [[ "$#" -gt 0 ]]; do
case "$1" in
-c|--ca-name)
if [[ -z "$2" || "$2" == -* ]]; then
msg_error "\nError: Argument missing for option -c or --ca-name.\n"
usage
exit 1
else
CA_CRT=$2.crt
CA_KEY=$2.key
fi
shift 2
;;
-n|--cn)
if [[ -z "$2" || "$2" == -* ]]; then
msg_error "\nError: Argument missing for option -n or --cn.\n"
usage
exit 1
else
check_format_fqdn "$2"
COMMON_NAME=$2
# record for db if no option -d or --dns exist.
# some browser needed the commName in Subject Alternative Name
DNS_LINE=$2 # record for db if no option -d or --dns exist. Some browser needed the commName in Subject Alternative Name
fi
shift 2
;;
-d|--dns)
if [[ -z "$2" || "$2" == -* ]]; then
msg_error "\nError: Argument missing for option -d or --dns.\n"
usage
exit 1
else
# add commonName and parameters -d --dns value in dnsLine variable.
# some browser needed the commName in Subject Alternative Name
DNS_LINE="$COMMON_NAME,$2"
# put dnsLine value in dns array
IFS=',' read -r -a DNS <<< "$DNS_LINE"
# check dns format
for SAN_DNS in "${DNS[@]}"; do
check_format_fqdn $SAN_DNS
done
fi
shift 2
;;
-i|--ip)
if [[ -z "$2" || "$2" == -* ]]; then
msg_error "\nError: Argument missing for option -i or --ip.\n"
usage
exit 1
else
# add parameters -i --ip values in IP_LINE variable.
IP_LINE=$2
# put ipLine value in ipAddrs array
IFS=',' read -r -a IP_ADDRS <<< "$2"
# check ip format
for SAN_IP in "${IP_ADDRS[@]}"; do
check_format_ip $SAN_IP
done
fi
shift 2
;;
-t|--days)
if [[ -z "$2" || "$2" == -* ]]; then
msg_err "\nError: Argument missing for option -t or --days.\n"
usage
exit 1
else
DAYS=$2
fi
shift 2
;;
-y|--assumeyes)
ASSUME_YES="1"
shift
;;
-v|--version)
cat << EOF
$(basename "$0") $version Copyright (C) 2003 - $(date +%Y) Gilles Mouchet
EOF
exit
;;
*|-h|--help)
usage
exit
;;
esac
done
# check if file doesn't exists
if [ -f "$CERTS_PATH/$COMMON_NAME.key" ]; then
echo -e "The ${ORANGE}$CERTS_PATH/$COMMON_NAME.key${NC} file exists!"
yes_no "Are you sure you want to continue"
fi
# if array dns is empty we add commonName
if [ "${#DNS[@]}" -eq 0 ]; then
DNS+="$COMMON_NAME"
fi
# check if mandatory variables exists
if [[ -z "$CA_CRT" || -z "$CA_KEY" || -z "$COMMON_NAME" ]]; then
usage
exit 1
fi
# check if ca key and ca crt file exist
if [[ ! -f "$CRT_CA_PATH/$CA_CRT" || ! -f "$KEY_CA_PATH/$CA_KEY" ]]; then
msg_error "One or both of the following files are missing:"
msg_warn " - $CRT_CA_PATH/$CA_CRT"
msg_warn " - $KEY_CA_PATH/$CA_KEY"
exit 1
fi
# summary
printf "
${CYAN}Summary
---------------------------------------------------------------------${NC}
Certifcate authority: ${GREEN}%s${NC}
Common name: ${GREEN}%s${NC}
" "$CRT_CA_PATH/$CA_CRT" "$COMMON_NAME"
echo -e "SAN List:"
for SAN_DNS in "${DNS[@]}"; do
msg_ok " - $SAN_DNS"
done
echo -e "IP List:"
for SAN_IP in "${IP_ADDRS[@]}"; do
msg_ok " - $SAN_IP"
done
echo -e "Validity:"
echo -e " Not before: ${GREEN}$(date +"%b %d %H:%M:%S %Y GMT")${NC}"
echo -e " Not After : ${GREEN}$(date -u -d "+$DAYS days" +"%b %d %H:%M:%S %Y GMT")${NC}"
if [ "$ASSUME_YES" == "0" ]; then
yes_no "Is it ok"
fi
echo -e "\nPrepare the openSSL configuration file"
cat > "$CERTS_PATH/${COMMON_NAME}_openssl.cnf" << EOF
[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
req_extensions = req_ext
prompt = no
[ req_distinguished_name ]
CN = $COMMON_NAME
[ req_ext ]
subjectAltName = @alt_names
[ alt_names ]
EOF
echo -e " Add SAN"
# add dns
i=1
for SAN_DNS in "${DNS[@]}"; do
echo "DNS.$i = $SAN_DNS" >> "$CERTS_PATH/${COMMON_NAME}_openssl.cnf"
((i++))
done
echo -e " Add IP"
# add ip
i=1
for SAN_IP in "${IP_ADDRS[@]}"; do
echo "IP.$i = $SAN_IP" >> "$CERTS_PATH/${COMMON_NAME}_openssl.cnf"
((i++))
done
# create certificate
echo -e "Generating the private key..."
openssl genrsa -out "${CERTS_PATH}/${COMMON_NAME}.key" 4096
echo -e "Generating csr file..."
openssl req -new -key "${CERTS_PATH}/${COMMON_NAME}.key" -out "${CERTS_PATH}/${COMMON_NAME}.csr" -config "$CERTS_PATH/${COMMON_NAME}_openssl.cnf"
echo -e "Signing the certificate with the CA..."
openssl x509 -req -in "${CERTS_PATH}/${COMMON_NAME}.csr" \
-CA "$CRT_CA_PATH/$CA_CRT" -CAkey "$KEY_CA_PATH/$CA_KEY" -CAcreateserial \
-out "${CERTS_PATH}/${COMMON_NAME}.crt" -days "$DAYS" \
-extensions req_ext -extfile "$CERTS_PATH/${COMMON_NAME}_openssl.cnf" \
#-passin pass:$KEY_CA_PASS \
> /dev/null 2>&1
rc=$?
echo -n "Result of certificate signing: "
check_rc $rc
#echo -e "\nVerify certifcate"
#echo -e "\nValidity"
#openssl x509 -in $CERTS_PATH/${COMMON_NAME}.crt -noout -dates
#echo -e "\nSubject Alternative Name"
#openssl x509 -in $CERTS_PATH/${COMMON_NAME}.crt -noout -ext subjectAltName
echo -e -n "\nVerify the validity of ${GREEN}$CERTS_PATH/${COMMON_NAME}.crt${NC} using the trust chain: "
openssl verify -CAfile $CRT_CA_PATH/$CA_CRT $CERTS_PATH/$COMMON_NAME.crt > /dev/null 2>&1
check_rc $?
# get validity date
#NOT_BEFORE=$(openssl x509 -noout -in $CERTS_PATH/${COMMON_NAME}.crt -startdate | cut -d'=' -f2)
#NOT_AFTER=$(openssl x509 -noout -in $CERTS_PATH/${COMMON_NAME}.crt -enddate | cut -d'=' -f2)
# check if commonName already exist on db
#echo -e -n "\nCheck if ${GREEN}${COMMON_NAME}${NC} exist in DB: "
#recordExist=$(sqlite3 $DB_PATH "SELECT EXISTS(SELECT 1 FROM certs WHERE common_name='${COMMON_NAME}');")
#check_rc $?
#if [ "$recordExist" == "1" ]; then
# echo -e -n "Update ${ORANGE}${COMMON_NAME}${NC} in DB: "
## cert_crt_64=$(base64 -w0 ${CERTS_TMP_PATH}/${COMMON_NAME}.crt)
## echo $cert_crt_64
# sqlite3 $DB_PATH <<EOF
#UPDATE certs SET san_dns = '$DNS_LINE',
# san_ip = '$IP_LINE',
# cert_key = '$(base64 -w0 ${CERTS_TMP_PATH}/${COMMON_NAME}.key)',
# cert_csr = '$(base64 -w0 ${CERTS_TMP_PATH}/${COMMON_NAME}.csr)',
# cert_crt = '$(base64 -w0 ${CERTS_TMP_PATH}/${COMMON_NAME}.crt)',
# not_valid_before = '$NOT_BEFORE',
# not_valid_after = '$NOT_AFTER'
#WHERE common_name = '${COMMON_NAME}';
#EOF
## check_rc $?
#else
# echo -e -n "Add ${ORANGE}${COMMON_NAME}${NC} in DB: "
# sqlite3 $DB_PATH <<EOF
#INSERT INTO certs (common_name,san_dns,san_ip,cert_key,cert_csr,cert_crt,not_valid_before,not_valid_after)
#VALUES ('${COMMON_NAME}', '$DNS_LINE','$IP_LINE',
# '$(base64 -w0 ${CERTS_TMP_PATH}/${COMMON_NAME}.key)',
# '$(base64 -w0 ${CERTS_TMP_PATH}/${COMMON_NAME}.csr)',
# '$(base64 -w0 ${CERTS_TMP_PATH}/${COMMON_NAME}.crt)',
# '$NOT_BEFORE',
# '$NOT_AFTER')
#EOF
# check_rc $?
#fi
}
main "$@"