#! /bin/bash set -e DOMAIN=example.com HOST=mail.${DOMAIN} TEST=./tls_dane key() { local key=$1; shift if [ ! -f "${key}.pem" ]; then openssl genpkey 2>/dev/null \ -paramfile <(openssl ecparam -name prime256v1) \ -out "${key}.pem" fi } req() { local key=$1; shift local cn=$1; shift key "$key" openssl req -new -sha256 -key "${key}.pem" 2>/dev/null \ -config <(printf "[req]\n%s\n%s\n[dn]\nCN=%s\n" \ "prompt = no" "distinguished_name = dn" "${cn}") } req_nocn() { local key=$1; shift key "$key" openssl req -new -sha256 -subj / -key "${key}.pem" 2>/dev/null \ -config <(printf "[req]\n%s\n[dn]\nCN_default =\n" \ "distinguished_name = dn") } cert() { local cert=$1; shift local exts=$1; shift openssl x509 -req -sha256 -out "${cert}.pem" 2>/dev/null \ -extfile <(printf "%s\n" "$exts") "$@" } genroot() { local cn=$1; shift local key=$1; shift local cert=$1; shift local skid=$1; shift local akid=$1; shift exts=$(printf "%s\n%s\n%s\n" "$skid" "$akid" "basicConstraints = CA:true") req "$key" "$cn" | cert "$cert" "$exts" -signkey "${key}.pem" -set_serial 1 -days 30 } genca() { local cn=$1; shift local key=$1; shift local cert=$1; shift local skid=$1; shift local akid=$1; shift local ca=$1; shift local cakey=$1; shift exts=$(printf "%s\n%s\n%s\n" "$skid" "$akid" "basicConstraints = CA:true") req "$key" "$cn" | cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \ -set_serial 2 -days 30 "$@" } genee() { local cn=$1; shift local key=$1; shift local cert=$1; shift local ca=$1; shift local cakey=$1; shift exts=$(printf "%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \ "subjectKeyIdentifier = hash" \ "authorityKeyIdentifier = keyid, issuer" \ "basicConstraints = CA:false" \ "extendedKeyUsage = serverAuth" \ "subjectAltName = @alts" "DNS=${cn}") req "$key" "$cn" | cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \ -set_serial 2 -days 30 "$@" } genss() { local cn=$1; shift local key=$1; shift local cert=$1; shift exts=$(printf "%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \ "subjectKeyIdentifier = hash" \ "authorityKeyIdentifier = keyid, issuer" \ "basicConstraints = CA:true" \ "extendedKeyUsage = serverAuth" \ "subjectAltName = @alts" "DNS=${cn}") req "$key" "$cn" | cert "$cert" "$exts" -set_serial 1 -days 30 -signkey "${key}.pem" "$@" } gennocn() { local key=$1; shift local cert=$1; shift req_nocn "$key" | cert "$cert" "" -signkey "${key}.pem" -set_serial 1 -days -1 "$@" } runtest() { local desc=$1; shift local usage=$1; shift local selector=$1; shift local mtype=$1; shift local tlsa=$1; shift local ca=$1; shift local chain=$1; shift local digest case $mtype in 0) digest="";; 1) digest=sha256;; 2) digest=sha512;; *) echo "bad mtype: $mtype"; exit 1;; esac printf "%d %d %d %-24s %s: " "$usage" "$selector" "$mtype" "$tlsa" "$desc" if [ -n "$ca" ]; then ca="$ca.pem"; fi "$TEST" "$usage" "$selector" "$digest" "$tlsa.pem" "$ca" "$chain.pem" \ "$@" > /dev/null } checkpass() { runtest "$@" && { echo pass; } || { echo fail; exit 1; }; } checkfail() { runtest "$@" && { echo fail; exit 1; } || { echo pass; }; } #--------- genss "$HOST" sskey sscert gennocn akey acert # Tests that might depend on akid/skid chaining # for rakid in "" \ "authorityKeyIdentifier = keyid,issuer" \ "authorityKeyIdentifier = issuer" \ "authorityKeyIdentifier = keyid" do for cakid in "" \ "authorityKeyIdentifier = keyid,issuer" \ "authorityKeyIdentifier = issuer" \ "authorityKeyIdentifier = keyid" do for rskid in "" "subjectKeyIdentifier = hash" do for caskid in "" "subjectKeyIdentifier = hash" do genroot "Root CA" rootkey rootcert "$rskid" "$rakid" genca "CA 1" cakey1 cacert1 "$caskid" "$cakid" rootcert rootkey genca "CA 2" cakey2 cacert2 "$caskid" "$cakid" cacert1 cakey1 genee "$HOST" eekey eecert cacert2 cakey2 cat eecert.pem cacert2.pem cacert1.pem rootcert.pem > chain.pem cat eecert.pem cacert2.pem cacert1.pem > chain1.pem for s in 0 1 do checkpass "OOB root TA" 2 "$s" 0 rootcert "" chain1 "$HOST" checkpass "OOB TA" 2 "$s" 0 cacert2 "" eecert "$HOST" checkpass "in-chain root TA" 2 "$s" 1 rootcert "" chain "$HOST" for m in 0 1 2 do checkpass "valid TA" 2 "$s" "$m" rootcert "" chain "$HOST" for ca in "cacert1" "cacert2"; do checkpass "valid TA" 2 "$s" "$m" "$ca" "" chain "$HOST" checkpass "valid TA" 2 "$s" "$m" "$ca" "" chain1 "$HOST" checkpass "valid TA+CA" 2 "$s" "$m" "$ca" rootcert chain1 "$HOST" checkpass "sub-domain" 2 "$s" "$m" "$ca" "" chain1 whatever ".$DOMAIN" checkfail "wrong name" 2 "$s" "$m" "$ca" "" chain1 "whatever" done done done done done done done # These tests don't depend in the akid/skid chaining: # for s in 0 1 do checkfail "missing TA" 2 "$s" 1 rootcert "" chain1 "$HOST" for m in 0 1 2 do checkpass "depth 0 TA" 2 "$s" "$m" sscert "" sscert "$HOST" checkfail "non-TA" 2 "$s" "$m" eecert rootcert chain "$HOST" checkfail "depth 0 TA namecheck" 2 "$s" "$m" sscert sscert sscert whatever checkpass "valid EE" 3 "$s" "$m" eecert "" chain whatever checkpass "key-only EE" 3 "$s" "$m" acert "" acert whatever checkfail "wrong EE" 3 "$s" "$m" cacert2 "" chain whatever done done rm -f *.pem