Skip to content

Instantly share code, notes, and snippets.

@thomasleveil
Created January 10, 2017 00:16
Show Gist options
  • Save thomasleveil/e35bca36e0452fa97d5f88ee0379d342 to your computer and use it in GitHub Desktop.
Save thomasleveil/e35bca36e0452fa97d5f88ee0379d342 to your computer and use it in GitHub Desktop.
Test Certification Authority

create_server_certificate.sh

create_server_certificate.sh is a script helping with issuing server certificates that can be used to provide TLS on test web servers.

It also creates a Certificate Authority (CA) root key and certificate. This CA root certificate can be used to validate the server certificates it generates.

The intent is to easily produce a CA root certificate and issue server certificate for test purpose.

For instance, with curl:

curl --cacert /somewhere/ca-root.crt https://www.example.com/

or with wget:

wget --certificate=/somewhere/ca-root.crt https://www.example.com/

or with the python requests module:

import requests
r = requests.get("https://www.example.com", verify="/somewhere/ca-root.crt")

Usage

Simple domain

Create a server certificate for domain www.example.com:

./create_server_certificate.sh www.example.com

Will produce:

  • www.example.com.key
  • www.example.com.crt

Multiple domains

Create a server certificate for main domain www.example.com and alternative domains example.com, foo.com and bar.com:

./create_server_certificate.sh www.example.com foo.com bar.com

Will produce:

  • www.example.com.key
  • www.example.com.crt

Wildcard domain

Create a server certificate for wildcard domain *.example.com:

./create_server_certificate.sh "*.example.com"

Note that you need to use quotes around the domain string or the shell would expand *.

Will produce:

  • *.example.com.key
  • *.example.com.crt

Again, to prevent your shell from expanding *, use quotes. i.e.: cat "*.example.com.crt".

Such a server certificate would be valid for domains:

  • foo.example.com
  • bar.example.com

but not for domains:

  • example.com
  • foo.bar.example.com

Wildcard domain on multiple levels

While you can technically create a server certificate for wildcard domain *.example.com and alternative name *.*.example.com, client implementations generally do not support multiple wildcards in a domain name.

For instance, a python script using urllib3 would fail to validate domain foo.bar.example.com presenting a certificate with name *.*.example.com. It is advised to stay away from producing such certificates.

If you want to give it a try:

./create_server_certificate.sh "*.example.com" "*.*.example.com"

Such a server certificate would be valid for domains:

  • foo.example.com
  • bar.example.com
  • foo.bar.example.com
#!/bin/bash
set -u
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
if [[ "$#" -eq 0 ]]; then
cat <<-EOF
To generate a server certificate, provide the domain name as a parameter:
$(basename $0) www.my-domain.tdl
$(basename $0) www.my-domain.tdl alternate.domain.tld
You can also create certificates for wildcard domains:
$(basename $0) '*.my-domain.tdl'
EOF
exit 0
else
DOMAIN="$1"
ALTERNATE_DOMAINS="DNS:$( echo "$@" | sed 's/ /,DNS:/g')"
fi
###############################################################################
# Create a nginx container (which conveniently provides the `openssl` command)
###############################################################################
CONTAINER=$(docker run -d -v $DIR:/work -w /work -e SAN="$ALTERNATE_DOMAINS" nginx:1.11.8)
# Configure openssl
docker exec $CONTAINER bash -c '
mkdir -p /ca/{certs,crl,private,newcerts} 2>/dev/null
echo 1000 > /ca/serial
touch /ca/index.txt
cat > /ca/openssl.cnf <<-"OESCRIPT"
[ ca ]
# `man ca`
default_ca = CA_default
[ CA_default ]
# Directory and file locations.
dir = /ca
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 = /work/ca-root.key
certificate = /work/ca-root.crt
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
name_opt = ca_default
cert_opt = ca_default
default_days = 10000
preserve = no
policy = policy_loose
[ policy_loose ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
# Options for the `req` tool (`man req`).
default_bits = 2048
distinguished_name = req_distinguished_name
string_mask = utf8only
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
# Extension to add when the -x509 option is used.
x509_extensions = v3_ca
[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
countryName = Country Name (2 letter code)
stateOrProvinceName = State or Province Name
localityName = Locality Name
0.organizationName = Organization Name
organizationalUnitName = Organizational Unit Name
commonName = Common Name
emailAddress = Email Address
[ v3_ca ]
# Extensions for a typical CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ server_cert ]
# Extensions for server certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = server
nsComment = server certificate generated for test purpose
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
[ san_env ]
subjectAltName=${ENV::SAN}
OESCRIPT
'
# shortcut for calling `openssl` inside the container
function openssl {
docker exec $CONTAINER openssl "$@"
}
function exitfail {
echo
echo ERROR: "$@"
docker rm -f $CONTAINER
exit 1
}
###############################################################################
# Setup Certificate authority
###############################################################################
if ! [[ -f "$DIR/ca-root.key" ]]; then
echo
echo "> Create a Certificate Authority root key: $DIR/ca-root.key"
openssl genrsa -out ca-root.key 2048
[[ $? -eq 0 ]] || exitfail failed to generate CA root key
fi
# Create a CA root certificate
if ! [[ -f "$DIR/ca-root.crt" ]]; then
echo
echo "> Create a CA root certificate: $DIR/ca-root.crt"
openssl req -config /ca/openssl.cnf \
-key ca-root.key \
-new -x509 -days 3650 -subj "/O=test suite/CN=www.test.tld" -extensions v3_ca \
-out ca-root.crt
[[ $? -eq 0 ]] || exitfail failed to generate CA root certificate
# Verify certificate
openssl x509 -noout -text -in ca-root.crt
fi
###############################################################################
# create server key and certificate signed by the certificate authority
###############################################################################
echo
echo "> Create a host key: $DIR/$DOMAIN.key"
openssl genrsa -out "$DOMAIN.key" 2048
echo
echo "> Create a host certificate signing request"
SAN="$ALTERNATE_DOMAINS" openssl req -config /ca/openssl.cnf \
-key "$DOMAIN.key" \
-new -out "/ca/$DOMAIN.csr" -days 1000 -extensions san_env -subj "/CN=$DOMAIN"
[[ $? -eq 0 ]] || exitfail failed to generate server certificate signing request
echo
echo "> Create server certificate: $DIR/$DOMAIN.crt"
SAN="$ALTERNATE_DOMAINS" openssl ca -config /ca/openssl.cnf -batch \
-extensions server_cert \
-extensions san_env \
-in "/ca/$DOMAIN.csr" \
-out "$DOMAIN.crt"
[[ $? -eq 0 ]] || exitfail failed to generate server certificate
# Verify host certificate
#openssl x509 -noout -text -in "$DOMAIN.crt"
docker rm -f $CONTAINER >/dev/null
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment