Root Certification Authority

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

The .internal top-level domain is reserved for private-use applications. These scripts show how we might create certificates for *.home.internal:

$ ./RootCertificate.sh
$ ./Certificate.sh

$ ls -1 HOME*
HOME_INTERNAL.crt
HOME_INTERNAL.key
HOME_INTERNAL.pfx
HOME_INTERNAL_CHAIN.pem
HOME_INTERNAL_CHAIN_WITH_KEY.pem
HOME_INTERNAL_ROOT.crt
HOME_INTERNAL_ROOT.key

RootCertificate.sh creates a Root Certificate:

#!/bin/bash

# Create the Root Certification Authority's Root Certificate

CERTNAME="HOME_INTERNAL_ROOT"
COMMON_NAME="HOME.INTERNAL ROOT"

## --

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, pathlen:0
authorityKeyIdentifier = keyid:always, issuer
keyUsage               = 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

set -e

# Create End-Entity Certificate signed by the Root Certificate

DAYS=365
CERTNAME="HOME_INTERNAL"
COMMON_NAME="home.internal"
SUBJECT_ALT_NAME="DNS:home.internal,DNS:*.home.internal,DNS:localhost"
PFX_PASSWORd="changeit"
ROOT_CERTNAME="HOME_INTERNAL_ROOT" # CERTNAME from RootCertificate.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       = CA:false
keyUsage               = digitalSignature, keyEncipherment
extendedKeyUsage       = 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 root CA cert
$openssl x509 -req -days $DAYS \
  -CAkey $ROOT_CERTNAME.key -CA $ROOT_CERTNAME.crt \
  -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"

$openssl verify -CAfile $ROOT_CERTNAME.crt $CERTNAME.crt

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

cat $CERTNAME.key $CERTNAME.crt > ${CERTNAME}_CHAIN_WITH_KEY.pem
cat               $CERTNAME.crt > ${CERTNAME}_CHAIN.pem

download: Certificate.sh

TLS Analysis

SSLyze can analyze the SSL/TLS configuration of a server, e.g.
docker run --rm -it -v/tmp/HOME_INTERNAL_ROOT.crt:/tmp/cert.crt nablac0d3/sslyze --certinfo_ca_file /tmp/cert.crt myhost.home.internal

home.arpa

The Special-Use Domain 'home.arpa' (RFC 8375) has been added in the ICANN domains section of the Public Suffix List, and browsers like Google Chrome does not allow wildcard certs for public/ICANN registry controlled domains. Before this, '*.home.arpa' worked, but now '*.subdomain.home.arpa' would be necessary.