#!/bin/bash
# cert.sh - create httpd tls cert with san
# (c) 2017 ed <s@ocv.me>, MIT-licensed
set -e

# manual checksum validation
true ||
{
nlines=$(wc -l < "$0")

# sum each 16 lines
for (( n=16; n<=$nlines; n+=16 ))
do printf '%5s  %s\n' $n "$(
head -n $n < "$0" | tail -n 16 |
openssl dgst -binary -sha1 | openssl base64
)"
done
read -u1 -r

# sum each line
cat -n "$0" | while IFS= read -r x
do printf '%s  %s\n' "$(printf '%s\n' "$x" |
openssl dgst -binary -sha1 | openssl base64 | cut -c-3
)" "$x"
done
exit 0
}





CFN="$1"
C="$2"
ST="$3"
L="$4"
O="$5"
OU="$6"
CN="$7"
shift 7 &&
	SAN=($@) ||
	SAN=()

function okay_yn()
{
	while true
	do
		printf '\033[0m[y/n] \033[1m'
		read -n1 -r
		printf '\033[0m\n'
		printf '%s\n' "$REPLY" | grep -qE '^[yY]$' && return 0
		printf '%s\n' "$REPLY" | grep -qE '^[nN]$' && return 1
	done
	[[ $rv ]]
}


printf '\033[1;34musing the following arguments:\033[0m

  Filename:       %s

  C,   Country  = %s
  ST,  State    = %s
  L,   City     = %s
  O,   Company  = %s
  OU,  Division = %s
  CN,  Domain   = %s
  SAN, AltNames:
        SAN[01] = %s
' "$CFN" "$C" "$ST" "$L" "$O" "$OU" "$CN" "$CN"



nsans=${#SAN[@]}
for (( nsan=0; nsan<$nsans; nsan++ ))
do
	printf '        SAN[%02d] = %s\n' "$((nsan+2))" "${SAN[$nsan]}"
done



[[ "x$CN" == "x" ]] &&
{
	printf '\n\033[1;31mnot enough arguments provided.\nsupply enough arguments to fill in the above.\nblank values are permitted, except for CN.\nadd 0 or more SAN as separate arguments.\n\033[0m'
	exit 1
}

printf '\n\033[1;34mis this ok? '
okay_yn

cfg=$(mktemp)
printf '
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
string_mask = utf8only
prompt = no

[req_distinguished_name]
C = %s
ST = %s
L = %s
O = %s
OU = %s
CN = %s

[v3_req]
nsCertType = server
subjectKeyIdentifier = hash
keyUsage = critical, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = %s
' "$C" "$ST" "$L" "$O" "$OU" "$CN" "$CN" > "$cfg"

nsans=${#SAN[@]}
for (( nsan=0; nsan<$nsans; nsan++ ))
do
	#printf '  SAN %02s. %s\n' "$((nsan+2))" "${SAN[$nsan]}"
	printf 'DNS.%s = %s\n' "$((nsan+2))" "${SAN[$nsan]}" >> "$cfg"
done

printf '\n\n\033[1;34musing the following openssl configuration:\n==========================================\n\033[0m'
cat "$cfg"
printf '\n\033[1;34mis this ok? '
okay_yn

# 2147483648−(60*60*24*3+1) = 2147224447
dist=$(date -u +%s)
dist=$((2147224447-dist))
one_day=$((60*60*24))
dist=$((dist/one_day))

openssl req -x509 -nodes -days $dist -newkey rsa:4096 \
  -keyout $CFN.key -out $CFN.crt -config "$cfg" -sha256 ||
{
	printf '\033[1;31mfailed to create certificate\n'
	rm "$cfg"
	exit 1
}

dh_sys="/etc/ssl/dh4096.pem"
dh_tmp="/dev/shm/dh4096.pem"

[[ -e "$dh_sys" ]] && [[ -s "$dh_sys" ]] ||
{
	printf '\033[1;33m%s does not exist.  create it? ' "$dh_sys"
	okay_yn &&
	{
		[[ -e "$dh_tmp" ]] && [[ -s "$dh_tmp" ]] ||
			openssl dhparam -out "$dh_tmp" 4096
		
		echo
		printf '\033[1;31m'
		cp -p "$dh_tmp" "$dh_sys" ||
		{
			while true
			do
				echo
				printf '\033[1;33mfor your own convenience, execute the following as root:\ncp "%s" "%s"; chown root. "%s"\n\nconfirm with Y, skip with N: ' "$dh_tmp" "$dh_sys" "$dh_sys"
				okay_yn || break
				[[ -e "$dh_sys" ]] && break || true
			done
		}
		printf '\033[0m'
	} ||
	true
	
	[[ -e "$dh_sys" ]] ||
		dh_sys="$dh_tmp"
}

cat $CFN.key $CFN.crt > $CFN.pem

[[ -e "$dh_sys" ]] && [[ -s "$dh_sys" ]] &&
	cat "$dh_sys" >> $CFN.pem ||
	printf '\033[1;33mcould not append /etc/ssl/dh4096.pem onto the pem\033[0m\n'

printf '\033[1;32mcertificate OK\n\ndisplay it? '
okay_yn

openssl x509 -in $CFN.pem -text -noout ||
{
	printf '\033[1;31mfailed to display certificate\n'
	exit 1
}

