#!/bin/sh
set -eu

die () {
    echo "$@" >&2
    exit 1
}

which openssl >/dev/null || die "Can't find openssl."
#seed="$(hexdump -n10 -e '10/1 "%02o" "\n"' /dev/urandom)"
config=\
"[ ca ]
default_ca = CA_default

[ CA_default ]
dir = $PWD
certs = \$dir/certs
certificate = \$dir/CA.crt
private_key = \$dir/CA.key
default_md = sha256
email_in_dn = no
RANDFILE = /dev/urandom
database = /dev/null
serial = serial

[ req ]
distinguished_name = req_distinguished_name
prompt = no
encrypt_key = no
default_md = sha256
default_bits = 2048
RANDFILE = /dev/urandom

[ req_distinguished_name]
#C = 2 letter country code
#ST = State
#L = Locality
#O = Organization name
#OU = Organizational unit
#emailAddress = email address
CN = \${ENV::cn}

[ v3_ca ]
basicConstraints = CA:true

[ v3_req ]
basicConstraints = CA:false
subjectAltName = @AltNames

[ AltNames ]
DNS.1 = \${ENV::cn}
DNS.2 = *.\${ENV::cn}
"

usage () {
    echo "Usage: $0 init|gen|sign|resign"
}

init () {
    # shellcheck disable=SC2039
    local cn
    cn="$(basename "$PWD")"
    export cn
    mkdir -p certs keys
    if [ -e openssl.cnf ]
    then
        echo openssl.cnf already exists, skipping generation. >&2
    else
        echo "$config" > "openssl.cnf"
    fi
    if [ -e CA.srl ]
    then
        echo CA.srl already exists, skipping. >&2
    else
        echo 1000 > CA.srl
    fi
    if [ -e CA.key ]
    then
        echo CA.key already exists, skipping. >&2
    else
        openssl genrsa -out CA.key 2048
    fi
    if [ -e CA.crt ]
    then
        echo CA.crt already exists, skipping. >&2
    else
        openssl req \
            -x509 \
            -config openssl.cnf \
            -new \
            -key CA.key \
            -extensions v3_ca \
            -days 3650 \
            -out CA.crt
    fi
    if [ -e CA.p12 ]
    then
        echo PKCS12 file already exists, skipping. >&2
    else
        openssl pkcs12 \
            -export \
            -in CA.crt \
            -inkey CA.key \
            -out CA.p12 \
            -passout pass:
    fi
}

sign_key () {
    # shellcheck disable=SC2039
    local csr cn
    if [ $# -lt 1 ] || [ "$1" = "" ]
    then
        die "No host specified."
    fi
    if [ ! -f CA.crt ] || [ ! -f CA.key ] || [ ! -d keys ] || [ ! -d certs ] ||
        [ ! -f openssl.cnf ]
    then
        die "CA isn't initialized properly."
    fi
    if [ ! -f "keys/$1" ]
    then
        die "Can't find key to sign."
    fi
    csr="$(mktemp -t ssl-ca-XXXXXXXXX)"
    cn="$1.$(basename "$PWD")"
    export cn
    openssl req \
        -key "keys/$1" \
        -new \
        -reqexts v3_req \
        -config openssl.cnf \
        -out "$csr"
    openssl x509 \
        -req \
        -in "$csr" \
        -out "certs/$1" \
        -CA CA.crt \
        -CAserial CA.srl \
        -extensions v3_req \
        -extfile openssl.cnf \
        -days 3650 \
        -CAkey CA.key
    rm "$csr"
}

gen_key () {
    if [ $# -lt 1 ] || [ "$1" = "" ]
    then
        die "No host specified."
    fi
    if [ ! -d keys ]
    then
        die "keys directory doesn't exists, run ssl-ca init to rectify."
    fi
    if [ -e "keys/$1" ]
    then
        die "Key already exists."
    fi
    openssl genrsa -out "keys/$1" 2048
}

if [ $# -lt 1 ]
then
    usage
    exit 1
fi

case "$1" in
    init)
        init
        ;;
    gen)
        gen_key "$2"
        sign_key "$2"
        ;;
    sign)
        for key in keys/*
        do
            if [ ! -f "certs/$(basename "$key")" ]
            then
                sign_key "$(basename "$key")"
            fi
        done
        ;;
    resign)
        for key in keys/*
        do
            sign_key "$(basename "$key")"
        done
        ;;
    *)
        usage
        exit 1
        ;;
esac
