Wednesday 29 March 2017

Signing your own Development Domain Certificate with your own Authority Certificate

When developing a servlet based application that relies upon your links being HTTPS, you will often find yourself generating a self-signed certificate for your development site.

At first your browser baulks, but you add a site exception and continue. However, you are never quite sure what will happen when it gets to production, and sometimes you must use client libraries that simply refuse to ignore an untrusted certificate or badly configured site.

Here is the alternative: get your certificate signed. Normally this would be costly, but if the server is only to be used by you or your department, then you can sign the certificate yourself with a certificate authority (CA) that you have created. You add the CA certificate into browser or Java JDK and the site becomes properly trusted.

I've done this a few times, so I have written a script. You will need bash, openssl and the JDK's keytool. I do this using Cygwin on Windows.

#!/bin/bash

# Things you might want to change
domain="*.mydomain.com"
keyalias="domaincert"
keystorefile="mykeystore"
password="changeit"
validity_days=3650

c_CN="$domain"
c_OU="My Domain Unit"
c_O="My Domain Organisation"
c_L="My City"
c_ST="My State/County"
c_C="UK"

ca_CN="My CA"
ca_OU="My CA Signing Unit"
ca_O="My CA Organisation"
ca_L="My CA City"
ca_ST="My CA State/County"
ca_C="UK"

# The working directory
ssldir="tmp.X509CA"

# Do you have the tools you need?
which openssl >/dev/null || exit 1
which keytool >/dev/null || exit 1

echo "Creating openssl workarea and copy of configuration in $ssldir ..."
rm -rf "$ssldir"
mkdir "$ssldir"
mkdir "$ssldir/ca"
mkdir "$ssldir/certs"
mkdir "$ssldir/newcerts"
mkdir "$ssldir/crl"
mkdir "$ssldir/private"
echo "01" > "$ssldir/serial"
touch "$ssldir/index.txt"

# Tweak local copy of config to use this dir
# Make openssl work with same string types as keytool.
# If signing complains that fields don't match it will probably be todo with differences in format between openssl and keytool
# http://stackoverflow.com/questions/6976127/openssl-signing-a-certificate-with-my-ca
sed -e '/\[ CA_default \]/,/\[ .* \]/s/^dir\s*=.*$/dir = '"$ssldir"'/' \
 -e '/\[ req \]/,/\[ .* \]/s/^string_mask\s*=.*$/string_mask = pkix/' \
 /usr/ssl/openssl.cnf > "$ssldir/sslopenssl.cnf"

echo ""
echo "Creating self-signed certificate authority (CA) $ssldir/cacert.pem ..."
echo "openssl output --------------------------"
openssl req -x509 -new -config "$ssldir/sslopenssl.cnf" -days $validity_days -passout "pass:$password" -subj "/CN=$ca_CN/OU=$ca_OU/O=$ca_O/L=

$ca_L/ST=$ca_ST/C=$ca_C" -out "$ssldir/cacert.pem" -keyout "$ssldir/private/cakey.pem" || exit 1
echo "end of openssl output ----------------------"
#keytool -printcert -file "$ssldir/cacert.pem" -keypass "$password" -storepass "$password" 

echo ""
echo "Creating new certificate and private key pair into $ssldir/$keystorefile ..."
keytool -genkeypair -dname "CN=$c_CN, OU=$c_OU, O=$c_O, L=$c_L, ST=$c_ST, C=$c_C" -alias "$keyalias" -validity $validity_days -keypass 

"$password" -storepass "$password" -keystore "$ssldir/$keystorefile" || exit 1
#keytool -list -v -keystore "$ssldir/$keystorefile" -keypass "$password" -storepass "$password" 

echo ""
echo "Creating certificate signing request (CSR) into $ssldir/csr.pem ..."
keytool -certreq -alias "$keyalias" -file $ssldir/csr.pem -keystore "$ssldir/$keystorefile" -keypass "$password" -storepass "$password" || exit 1

echo ""
echo "Signing the CSR using the CA to create signed certificate in $ssldir/signed-cert ..."
echo "openssl output --------------------------"
openssl ca -utf8 -policy policy_anything -batch -config "$ssldir/sslopenssl.cnf" -days "$validity_days" -passin "pass:$password" -in 

"$ssldir/csr.pem" -out "$ssldir/signed-cert" || exit 1
echo "end of openssl output ----------------------"

echo ""
echo "Converting signed certificate to PEM format ..."
openssl x509 -in "$ssldir/signed-cert" -out "$ssldir/signed-cert.pem" -outform PEM || exit 1

echo ""
echo "Concatenate signed-certificate and CA signed-certificate to create  $ssldir/certificate.chain ..."
cat "$ssldir/signed-cert.pem" "$ssldir/cacert.pem" >  "$ssldir/certificate.chain" || exit 1

echo ""
echo "Importing key chain into $ssldir/$keystorefile ..."
echo "keytool output --------------------------"
keytool -import -noprompt -file "$ssldir/certificate.chain" -alias "$keyalias" -keypass "$password" -keystore "$ssldir/$keystorefile" -storepass 

"$password" || exit 1
echo "end of keytool output ----------------------"

#echo ""
#keytool -list -keystore "$ssldir/$keystorefile" -keypass "$password" -storepass "$password"

echo ""
echo "============ Further instructions ================"
echo ""
echo "Now copy \"$ssldir/$keystorefile\" to ~/.keystore"
echo "(or setup $keystorefile on connector in Tomcat server.xml)"
echo "(or with SslContextFactory.setKey*() in embedded Jetty)"
echo ""
echo "Import \"$ssldir/cacert.pem\" into your browser (in Firefox"
echo "  via Tools->Options->Encryption->View Certificates->Authorities->Import"
echo "  check - trust this CA to identify websites)"
echo ""
echo "To have Java clients trust the server..."
echo "BACK UP your old JDK 'jre/lib/security/cacerts' file."
echo "Import \"$ssldir/cacert.pem\" into the JDK cacerts file..."
echo "  keytool -import -alias \"MyCa\" -trustcacerts -file \"$ssldir/cacert.pem\" -keystore path/jre/lib/security/cacerts -storepass \"changeit

\""
echo "  keytool -keystore path/jre/lib/security/cacerts -storepass \"changeit\"" -list