Root Certification Authority

Be you own Root Certification Authority. Create a Root Certificate and use it to sign any needed End-Entity Certificates.

For homenet use, RFC 8375 recommends the Special-Use Domain home.arpa.

$ ./RootCertificate.sh
$ ./Certificate.sh
$ ls -1
RootCertificate.sh
Certificate.sh
HOME_ARPA_CA.crt
HOME_ARPA_CA.key
HOME_ARPA_CA.srl
HOME_ARPA.crt
HOME_ARPA.key
HOME_ARPA.pfx

RootCertificate.sh creates a Root Certificate:

#!/bin/bash

# Create the Root Certification Authority's Root Certificate

CERTNAME="HOME_ARPA_CA"
COMMON_NAME="HOME.ARPA CA"

## --

openssl=/usr/local/opt/openssl/bin/openssl   # homebrew on macOS

OPENSSL_CONF="$CERTNAME.cfg"
export COMMON_NAME OPENSSL_CONF

## --

cat >$OPENSSL_CONF << 'EOF'

[ req ]
default_bits           = 4095
default_md             = sha256
prompt                 = no
encrypt_key            = no
utf8                   = yes
string_mask            = utf8only
distinguished_name     = req_distinguished_name
req_extensions         = v3_req
x509_extensions        = v3_req

[ req_distinguished_name ]
commonName             = ${ENV::COMMON_NAME}

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

authorityKeyIdentifier = keyid:always,issuer

EOF

$openssl req -new -x509 -days 3650 -keyout "$CERTNAME.key" -out "$CERTNAME.crt"

rm "$OPENSSL_CONF"

download: RootCertificate.sh

Certificate.sh creates a End-Entity Certificate signed by the Root Certificate:

#!/bin/bash

# Create End-Entity Certificate signed by the Root Certificate

CERTNAME="HOME_ARPA"
COMMON_NAME="home.arpa"
SUBJECT_ALT_NAME="DNS:home.arpa,DNS:*.home.arpa"
PFX_PASSWORd="changeit"
ROOT_CERTNAME="HOME_ARPA_CA" # CERTNAME from CACertificate.sh

## --

openssl=/usr/local/opt/openssl/bin/openssl   # homebrew on macOS

OPENSSL_CONF="$CERTNAME.cfg"
export COMMON_NAME SUBJECT_ALT_NAME OPENSSL_CONF

## --

cat >$OPENSSL_CONF << 'EOF'

[ req ]
default_bits           = 2048
default_md             = sha256
prompt                 = no
encrypt_key            = no
utf8                   = yes
string_mask            = utf8only
distinguished_name     = req_distinguished_name
req_extensions         = v3_req
x509_extensions        = v3_req

[ req_distinguished_name ]
commonName             = ${ENV::COMMON_NAME}

[ v3_req ]
subjectAltName         = ${ENV::SUBJECT_ALT_NAME}
basicConstraints       = critical, CA:false
keyUsage               = critical, digitalSignature, keyEncipherment
extendedKeyUsage       = critical, serverAuth
subjectKeyIdentifier   = hash

EOF


# generate a private key and a certificate signing request
$openssl req -new -keyout "$CERTNAME.key" -out "$CERTNAME.csr"

# Sign the certificate with CA (max 825 days if iOS support is needed, https://support.apple.com/en-us/HT210176)
$openssl x509 -req -days 825 \
  -CAkey $ROOT_CERTNAME.key -CA $ROOT_CERTNAME.crt -CAcreateserial -CAserial "$ROOT_CERTNAME.srl" \
  -extfile $OPENSSL_CONF -extensions v3_req \
  -in "$CERTNAME.csr" -out "$CERTNAME.crt"

rm "$OPENSSL_CONF"
rm "$CERTNAME.csr"

#$openssl x509 -outform der -in "$CERTNAME.crt" -out "$CERTNAME.der"

# generate a pfx file
cat "$CERTNAME.key" "$CERTNAME.crt" "$ROOT_CERTNAME.crt" |\
  $openssl pkcs12 -export -out "$CERTNAME.pfx" -in - -name "$COMMON_NAME" -password "pass:$PFX_PASSWORd"
 
download: Certificate.sh