#!/usr/bin/env bash
#  =================================================================
#           #     #                 #     #
#           ##    #   ####   #####  ##    #  ######   #####
#           # #   #  #    #  #    # # #   #  #          #
#           #  #  #  #    #  #    # #  #  #  #####      #
#           #   # #  #    #  #####  #   # #  #          #
#           #    ##  #    #  #   #  #    ##  #          #
#           #     #   ####   #    # #     #  ######     #
#
#        ---   The NorNet Testbed for Multi-Homed Systems  ---
#                        https://www.nntb.no
#  =================================================================
#
#  High-Performance Connectivity Tracer (HiPerConTracer)
#  Copyright (C) 2015-2024 by Thomas Dreibholz
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  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.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#  Contact: dreibh@simula.no

# Bash options:
set -o nounset
set -o errexit


if [ $# -lt 1 ] ; then
   echo >&2 "ERROR:$0 server_name [ssl_directory]"
   exit 1
fi
if [ $# -ge 2 ] ; then
   SSL_DIRECTORY="$2"
else
   SSL_DIRECTORY="/tmp/ssl"
fi

SERVER="$1"
SERVER_OPENSSL_CONF="${SSL_DIRECTORY}/${SERVER}.conf"
SERVER_KEY="${SSL_DIRECTORY}/${SERVER}.key"
SERVER_CSR="${SSL_DIRECTORY}/${SERVER}.csr"
SERVER_CRT="${SSL_DIRECTORY}/${SERVER}.crt"
SERVER_CHAIN="${SSL_DIRECTORY}/${SERVER}-chain.pem"

# Get all IP addresses:
ALT_NAMES="DNS:localhost,DNS:`hostname -f`"
ALT_ADDRESSES=`hostname -I | xargs -n1 | awk 'BEGIN { first=1 } { if(!first) { printf(","); } printf("IP:%s",$1); first=0 }'`

RSA_BITS=4096

CA1="TestLevel1"
CA1_DIR="${SSL_DIRECTORY}/${CA1}"
CA1_OPENSSL_CONF="${CA1}.conf"
CA1_KEY="private/${CA1}.key"
CA1_CSR="${CA1}.csr"
CA1_CRT="certs/${CA1}.crt"
CA1_PEM="chains/${CA1}-chain.pem"
CA1_CRL="crl/${CA1}.crl"

CA2="TestIntermediate"
CA2_DIR="${SSL_DIRECTORY}/${CA2}"
CA2_OPENSSL_CONF="${CA2}.conf"
CA2_KEY="private/${CA2}.key"
CA2_CSR="${CA2}.csr"
CA2_CRT="certs/${CA2}.crt"
CA2_PEM="${CA2}-chain.pem"
CA2_CRL="crl/${CA2}.crl"

CA3="TestLeaf"
CA3_DIR="${SSL_DIRECTORY}/${CA3}"
CA3_OPENSSL_CONF="${CA3}.conf"
CA3_KEY="private/${CA3}.key"
CA3_CSR="${CA3}.csr"
CA3_CRT="certs/${CA3}.crt"
CA3_PEM="${CA3}-chain.pem"
CA3_CRL="crl/${CA3}.crl"


if [ ! -e ${SSL_DIRECTORY}  ]; then
   sudo mkdir -p ${SSL_DIRECTORY}
fi

# ====== Make Level-1 CA ====================================================
if [ ! -d ${CA1_DIR}/private -o ! -e ${CA1_DIR}/${CA1_CRT} ] ; then

   echo -e "\x1b[34mCreating Level-1 CA key and certificate ...\x1b[0m"

   sudo mkdir -p \
      ${CA1_DIR}/certs \
      ${CA1_DIR}/crl \
      ${CA1_DIR}/newcerts \
      ${CA1_DIR}/chains
   sudo mkdir -m700 -p ${CA1_DIR}/private
   if [ ! -e ${CA1_DIR}/index.txt ] ; then
      sudo touch ${CA1_DIR}/index.txt
   fi
   if [ ! -e ${CA1_DIR}/serial ] ; then
      echo "1000" | sudo tee ${CA1_DIR}/serial >/dev/null
   fi
   if [ ! -e ${CA1_DIR}/crlnumber ] ; then
      echo "1000" | sudo tee ${CA1_DIR}/crlnumber >/dev/null
   fi

   sudo tee ${CA1_DIR}/${CA1_OPENSSL_CONF} >/dev/null <<EOF
[ ca ]
default_ca        = CA_default

[ CA_default ]
# Directory and file locations.
dir                    = ${CA1_DIR}
certs                  = \$dir/certs
crl_dir                = \$dir/crl
new_certs_dir          = \$dir/newcerts
database               = \$dir/index.txt
serial                 = \$dir/serial
RANDFILE               = \$dir/private/.rand

# The root key and root certificate.
private_key            = \$dir/${CA1_KEY}
certificate            = \$dir/${CA1_CRT}

# For certificate revocation lists.
crlnumber              = \$dir/crlnumber
crl                    = \$dir/crl/${CA1_CRL}
crl_extensions         = crl_ext
default_crl_days       = 30

# SHA-1 is deprecated, so use SHA-512 instead.
default_md             = sha512

name_opt               = ca_default
cert_opt               = ca_default
default_days           = 375
preserve               = no
policy                 = policy_strict

[ policy_strict ]
countryName            = supplied
stateOrProvinceName    = supplied
organizationName       = supplied
organizationalUnitName = optional
commonName             = supplied
emailAddress           = optional

[ req ]
default_bits           = 16384
default_md             = sha512
distinguished_name     = req_distinguished_name

[ req_distinguished_name ]
countryName            = NO
stateOrProvinceName    = Oslo
localityName           = Oslo
organizationName       = Simula Metropolitan Centre for Digital Engineering
commonName             = ${CA1} Level-1 CA Certificate

[ v3_ca ]
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints       = critical, CA:true
keyUsage               = critical, digitalSignature, cRLSign, keyCertSign

[ v3_leaf_ca ]
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints       = critical, CA:true, pathlen:0
keyUsage               = critical, digitalSignature, cRLSign, keyCertSign
EOF

   # Create Level-1 CA certificate (self-signed)
   sudo openssl req -x509 \
      -newkey rsa:${RSA_BITS} -sha512 -nodes \
      -keyout ${CA1_DIR}/${CA1_KEY} \
      -out ${CA1_DIR}/${CA1_CRT} \
      -extensions v3_ca \
      -days 3650 \
      -utf8 -subj "/CN=${CA1} Level-1 CA Certificate/C=NO/ST=Oslo/L=Oslo/O=Simula Metropolitan Centre for Digital Engineering" \
      -config ${CA1_DIR}/${CA1_OPENSSL_CONF}

   # openssl x509 -noout -text -in ${CA1_DIR}/${CA1_CRT}

   sudo cp ${CA1_DIR}/${CA1_CRT} ${CA1_DIR}/${CA1_PEM}
   sudo openssl verify -CAfile ${CA1_DIR}/${CA1_PEM} ${CA1_DIR}/${CA1_CRT}
fi

# ====== Make Level-2 CA ====================================================
if [ ! -d ${CA2_DIR}/private -o ! -e ${CA2_DIR}/${CA2_CRT} ] ; then

   echo -e "\x1b[34mCreating Level-2 CA key and certificate ...\x1b[0m"

   sudo mkdir -p \
      ${CA2_DIR}/certs \
      ${CA2_DIR}/crl \
      ${CA2_DIR}/newcerts \
      ${CA2_DIR}/chains
   sudo mkdir -m700 -p ${CA2_DIR}/private
   if [ ! -e ${CA2_DIR}/index.txt ] ; then
      sudo touch ${CA2_DIR}/index.txt
   fi
   if [ ! -e ${CA2_DIR}/serial ] ; then
      echo "1000" | sudo tee ${CA2_DIR}/serial >/dev/null
   fi
   if [ ! -e ${CA2_DIR}/crlnumber ] ; then
      echo "1000" | sudo tee ${CA2_DIR}/crlnumber >/dev/null
   fi

   sudo tee ${CA2_DIR}/${CA2_OPENSSL_CONF} >/dev/null <<EOF
[ ca ]
default_ca             = CA_default

[ CA_default ]
# Directory and file locations.
dir                    = ${CA2_DIR}
certs                  = \$dir/certs
crl_dir                = \$dir/crl
new_certs_dir          = \$dir/newcerts
database               = \$dir/index.txt
serial                 = \$dir/serial
RANDFILE               = \$dir/private/.rand

# The root key and root certificate.
private_key            = \$dir/${CA2_KEY}
certificate            = \$dir/${CA2_CRT}

# For certificate revocation lists.
crlnumber              = \$dir/crlnumber
crl                    = \$dir/crl/${CA2_CRL}
crl_extensions         = crl_ext
default_crl_days       = 30

# SHA-1 is deprecated, so use SHA-512 instead.
default_md             = sha512

name_opt               = ca_default
cert_opt               = ca_default
default_days           = 375
preserve               = no
policy                 = policy_strict

[ policy_strict ]
countryName            = supplied
stateOrProvinceName    = supplied
organizationName       = supplied
organizationalUnitName = optional
commonName             = supplied
emailAddress           = optional

[ req ]
default_bits           = 16384
default_md             = sha512
distinguished_name     = req_distinguished_name
x509_extensions        = v3_ca

[ req_distinguished_name ]
countryName            = NO
stateOrProvinceName    = Oslo
localityName           = Oslo
organizationName       = Simula Metropolitan Centre for Digital Engineering
commonName             = ${CA2} Level-2 CA Certificate

[ v3_ca ]
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints       = critical, CA:true
keyUsage               = critical, digitalSignature, cRLSign, keyCertSign

[ v3_leaf_ca ]
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints       = critical, CA:true, pathlen:0
keyUsage               = critical, digitalSignature, cRLSign, keyCertSign
EOF

   # Create Level-2 CA certificate
   sudo openssl req \
      -newkey rsa:${RSA_BITS} -sha512 -nodes \
      -keyout ${CA2_DIR}/${CA2_KEY} \
      -out ${CA2_DIR}/${CA2_CSR} \
      -utf8 -subj "/CN=${CA1} Level-2 CA Certificate/C=NO/ST=Oslo/L=Oslo/O=Simula Metropolitan Centre for Digital Engineering" \
      -config ${CA2_DIR}/${CA2_OPENSSL_CONF}
   sudo openssl ca \
      -config ${CA1_DIR}/${CA1_OPENSSL_CONF} \
      -in ${CA2_DIR}/${CA2_CSR} \
      -out ${CA2_DIR}/${CA2_CRT} \
      -batch -notext \
      -extensions v3_ca \
      -days 3650 \
      -utf8 -subj "/CN=${CA1} Level-2 CA Certificate/C=NO/ST=Oslo/L=Oslo/O=Simula Metropolitan Centre for Digital Engineering" \

   # openssl x509 -noout -text -in ${CA2_DIR}/${CA2_CRT}

   sudo cat ${CA1_DIR}/${CA1_PEM} ${CA2_DIR}/${CA2_CRT} | sudo tee ${CA2_DIR}/${CA2_PEM} >/dev/null
   sudo openssl verify -CAfile ${CA2_DIR}/${CA2_PEM} ${CA2_DIR}/${CA2_CRT}
fi

# ====== Make Level-3 CA ====================================================
if [ ! -d ${CA3_DIR}/private -o ! -e ${CA3_DIR}/${CA3_CRT} ] ; then

   echo -e "\x1b[34mCreating Level-3 CA key and certificate ...\x1b[0m"

   sudo mkdir -p \
      ${CA3_DIR}/certs \
      ${CA3_DIR}/crl \
      ${CA3_DIR}/newcerts \
      ${CA3_DIR}/chains
   sudo mkdir -m700 -p ${CA3_DIR}/private
   if [ ! -e ${CA3_DIR}/index.txt ] ; then
      sudo touch ${CA3_DIR}/index.txt
   fi
   if [ ! -e ${CA3_DIR}/serial ] ; then
      echo "1000" | sudo tee ${CA3_DIR}/serial >/dev/null
   fi
   if [ ! -e ${CA3_DIR}/crlnumber ] ; then
      echo "1000" | sudo tee ${CA3_DIR}/crlnumber >/dev/null
   fi

   sudo tee ${CA3_DIR}/${CA3_OPENSSL_CONF} >/dev/null <<EOF
[ ca ]
default_ca             = CA_default

[ CA_default ]
# Directory and file locations.
dir                    = ${CA3_DIR}
certs                  = \$dir/certs
crl_dir                = \$dir/crl
new_certs_dir          = \$dir/newcerts
database               = \$dir/index.txt
serial                 = \$dir/serial
RANDFILE               = \$dir/private/.rand

# The root key and root certificate.
private_key            = \$dir/${CA3_KEY}
certificate            = \$dir/${CA3_CRT}

# For certificate revocation lists.
crlnumber              = \$dir/crlnumber
crl                    = \$dir/crl/${CA3_CRL}
crl_extensions         = crl_ext
default_crl_days       = 30

# SHA-1 is deprecated, so use SHA-512 instead.
default_md             = sha512

name_opt               = ca_default
cert_opt               = ca_default
default_days           = 375
preserve               = no
policy                 = policy_strict

[ policy_strict ]
countryName            = supplied
stateOrProvinceName    = supplied
organizationName       = supplied
organizationalUnitName = optional
commonName             = supplied
emailAddress           = optional

[ req ]
default_bits           = 16384
default_md             = sha512
distinguished_name     = req_distinguished_name
x509_extensions        = v3_ca

[ req_distinguished_name ]
countryName            = NO
stateOrProvinceName    = Oslo
localityName           = Oslo
organizationName       = Simula Metropolitan Centre for Digital Engineering
commonName             = ${CA3} Level-3 CA Certificate

[ v3_ca ]
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints       = critical, CA:true
keyUsage               = critical, digitalSignature, cRLSign, keyCertSign

[ v3_leaf_ca ]
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints       = critical, CA:true, pathlen:0
keyUsage               = critical, digitalSignature, cRLSign, keyCertSign

[ usr_cert ]
basicConstraints       = CA:FALSE
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid,issuer
keyUsage               = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage       = clientAuth, emailProtection

[ server_cert ]
basicConstraints       = CA:FALSE
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage               = critical, digitalSignature, keyEncipherment
extendedKeyUsage       = serverAuth
subjectAltName         = \${ENV::SAN}
EOF

   # Create Level-3 CA certificate
   sudo SAN="" openssl req \
      -newkey rsa:${RSA_BITS} -sha512 -nodes \
      -keyout ${CA3_DIR}/${CA3_KEY} \
      -out ${CA3_DIR}/${CA3_CSR} \
      -utf8 -subj "/CN=${CA3} Level-3 CA Certificate/C=NO/ST=Oslo/L=Oslo/O=Simula Metropolitan Centre for Digital Engineering/OU=Centre for Resilient Networks and Applications" \
      -config ${CA3_DIR}/${CA3_OPENSSL_CONF}
   sudo openssl ca \
      -config ${CA2_DIR}/${CA2_OPENSSL_CONF} \
      -in ${CA3_DIR}/${CA3_CSR} \
      -out ${CA3_DIR}/${CA3_CRT} \
      -batch -notext \
      -extensions v3_leaf_ca \
      -days 3650 \
      -utf8 -subj "/CN=${CA3} Level-3 CA Certificate/C=NO/ST=Oslo/L=Oslo/O=Simula Metropolitan Centre for Digital Engineering/OU=Centre for Resilient Networks and Applications" \

   # openssl x509 -noout -text -in ${CA3_DIR}/${CA3_CRT}

   sudo cat ${CA2_DIR}/${CA2_PEM} ${CA3_DIR}/${CA3_CRT} | sudo tee ${CA3_DIR}/${CA3_PEM} >/dev/null
   sudo openssl verify -CAfile ${CA3_DIR}/${CA3_PEM} ${CA3_DIR}/${CA3_CRT}
fi

# ====== Make server certificate ============================================
if [ ! -e ${SERVER_KEY} -o ! -e ${SERVER_CRT} ] ; then

   echo -e "\x1b[34mCreating server key and certificate ...\x1b[0m"

   sudo tee ${SERVER_OPENSSL_CONF} >/dev/null <<EOF
[ req ]
default_bits        = 8192
default_md          = sha512
distinguished_name  = req_distinguished_name
req_extensions      = req_ext
[ req_distinguished_name ]
countryName         = NO
stateOrProvinceName = Oslo
localityName        = Oslo
organizationName    = Simula Metropolitan Centre for Digital Engineering
commonName          = ${SERVER}
[ req_ext ]
subjectAltName      = \${ENV::SAN}
EOF

   # X509v3 Subject Alternative Name:
   SAN="${ALT_NAMES},${ALT_ADDRESSES}"

   sudo SAN="${SAN}" openssl req \
      -newkey rsa:${RSA_BITS} -sha512 -nodes \
      -keyout ${SERVER_KEY} \
      -out ${SERVER_CSR} \
      -utf8 -subj "/CN=${SERVER}" \
      -config ${SERVER_OPENSSL_CONF}
   sudo SAN="${SAN}" openssl ca \
      -config ${CA3_DIR}/${CA3_OPENSSL_CONF} \
      -in ${SERVER_CSR} \
      -out ${SERVER_CRT} \
      -batch -notext \
      -extensions server_cert \
      -days 3650 \
      -utf8 -subj "/CN=${SERVER}/C=NO/ST=Oslo/L=Oslo/O=Simula Metropolitan Centre for Digital Engineering/OU=Centre for Resilient Networks and Applications"

   openssl req -noout -text -in ${SERVER_CSR}

   sudo cat ${CA3_DIR}/${CA3_PEM} ${SERVER_CRT} | sudo tee ${SERVER_CHAIN} >/dev/null
fi

sudo openssl verify -CAfile ${CA3_DIR}/${CA3_PEM} ${SERVER_CRT}
