Wednesday, March 19, 2008

CA signed SSL certs in glassfish, for lazy admins

So setting up CA signed SSL in Glassfish sucked, until I finally found the process for doing so. John Yeary outlines the process, but it's still manual and annoying.

Never do more than twice anything you can script, and so here we go. The script below is designed to work with CACert.org certificates. Save it to your local system and run it as a user who has access to write to the Glassfish installation.

#!/bin/sh
# Copyright 2008 Jeff Rodriguez [jeffrodriguez.com]
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

/bin/echo "This script was designed to work with CACert.org."
/bin/echo "It may work with other sites, YMMV."
/bin/echo "USE AT YOUR OWN RISK."
/bin/echo -e "\n\n"

read -rp "SSL domain name [`hostname`]: " DOMAIN
if [ -z "$DOMAIN" ]; then DOMAIN="`hostname`"; fi

read -rp "AppServer domain directory [/home/`whoami`/SUNWappserver/domains/domain1]: " DIR
if [ -z "$DIR" ]; then DIR="/home/`whoami`/SUNWappserver/domains/domain1"; fi

read -rp "Keystore password [changeit]: " KSPASS
if [ -z "$KSPASS" ]; then KSPASS="changeit"; fi

# Make sure the domain directory is correct
KS="$DIR/config/keystore.jks"
if [ ! -e "$KS" -a ! -w "$KS" ]; then
/bin/echo "Your keystore ($KS) is not writable."
exit 1
fi

# Handy alias
KSCMD="keytool -keystore $KS -storepass $KSPASS"

# Generate the key
if ( ! $KSCMD -genkey -keyalg RSA -alias $DOMAIN -keypass $KSPASS -noprompt -validity 365 -dname "cn=$DOMAIN"); then
/bin/echo "Failed to generate key."
exit 1
fi
/bin/echo -e "\n\nA private key has been created in your app server's keystore."
/bin/echo "If this script fails at any point, and you wish to start over, you must delete this private key with the command: "
/bin/echo "$KSCMD -delete -alias $DOMAIN"
read -sp "Press enter to continue." REPLY
/bin/echo -e "\n\n"

# Create the CSR
if ( ! $KSCMD -certreq -v -alias $DOMAIN -file /tmp/$DOMAIN.csr); then
/bin/echo "Failed to create certificate signing request."
exit 1
fi

/bin/echo "Below is your Certificate Signing Request (CSR)."
/bin/echo "On the CACert.org website, go to Server Certificates -> New. Paste the text below into the form and submit it."
cat /tmp/$DOMAIN.csr
rm -f /tmp/$DOMAIN.csr

# Get the certificate
/bin/echo -e "\n\nCACert will generate a signed certificate. Paste it here, finish with EOF (CTRL+D)."
cat > /tmp/$DOMAIN.crt

# Fetch the CACert certificates
/bin/echo "Before you can use your certificate, it has to be imported to the app server keystore. To do that, we first have to import the CACert.org certificates."
read -rp "Would you like the script to automatically download the certificates? [y|N]: " DLCERTS

if [ $DLCERTS = 'Y' -o $DLCERTS = 'y' ]; then
wget -q --no-check-certificate -O /tmp/cacert_root.crt "http://www.cacert.org/certs/root.crt"
wget -q --no-check-certificate -O /tmp/cacert_class3.crt "http://www.cacert.org/certs/class3.crt"
else
/bin/echo "You have chosen not to automatically download the certificates. To do this manually,"
/bin/echo "Go to http://www.cacert.org/index.php?id=3"
/bin/echo "Save the Class 1 PKI Key Root Certificate (PEM Format) as /tmp/cacert_root.crt"
/bin/echo "Save the Class 3 PKI Key Root Certificate (PEM Format) as /tmp/cacert_class3.crt"
read -sp "Press enter to continue." REPLY
fi

if [ ! -e /tmp/cacert_root.crt -o ! -e /tmp/cacert_class3.crt ]; then
/bin/echo "CACert certificates weren't found in /tmp. Either automatic download failed or you have not manually downloaded the certificates properly."
exit 1
fi

# Import the certificates
if ( ! $KSCMD -import -v -alias cacert -trustcacerts -noprompt -file /tmp/cacert_root.crt); then
/bin/echo "Failed to import CACert root certificate."
exit 1
fi
if ( ! $KSCMD -import -v -alias cacert3 -trustcacerts -noprompt -file /tmp/cacert_class3.crt); then
/bin/echo "Failed to import CACert class 3 certificate."
exit 1
fi
if ( ! $KSCMD -import -v -alias $DOMAIN -trustcacerts -noprompt -file /tmp/$DOMAIN.crt); then
/bin/echo "Failed to import domain certificate."
exit 1
fi

# Cleanup
rm -f /tmp/cacert_root.crt
rm -f /tmp/cacert_class3.crt
rm -f /tmp/$DOMAIN.crt
$KSCMD -delete -alias cacert
$KSCMD -delete -alias cacert3

# Final instructions for the user
/bin/echo -e "\n\n"
/bin/echo "The certificate has been imported into your app server keystore."
/bin/echo "To use the certificate follow these last steps:"
/bin/echo " 1. Go to your Admin Console (http://$DOMAIN:4848), Configuration, HTTP Service, HTTP Listeners, http-listener-2 (or whatever your SSL listener is)."
/bin/echo " 2. On the SSL tab, set your Certificate Nickname to $DOMAIN, and click save."
/bin/echo " 3. Restart the app server."
/bin/echo "NOTE: Your certificate will expire in 1 year, you must update the certificate before then or browsers will receive SSL warnings; other programs may fail entirely."
/bin/echo "Mark your calendar. No, really."
/bin/echo -e "\n\n"
/bin/echo "If something went, or goes wrong, it could prevent your app server from starting. One possible remedy is to locate your SSL http listener in $DIR/config/domain.xml and disable it."

Though supposedly, it's technically possible to use an existing private key and certificate, you'll shoot yourself in the face before you finish. Cut your losses and just start over with a new private key and cert. You'll have to pardon me if the script is a bit rough around the edges. Send patches my way if you like.