#!/bin/sh
##
##  x509-util.sh -- X.509 Certificate Generation Utility
##  Copyright (c) 2007-2014 Ralf S. Engelschall <rse@engelschall.com>
##
##  This program is free software; you can redistribute it and/or modify
##  it under the terms of the GNU General Public License as published by
##  the Free Software Foundation; either version 2 of the License, or
##  (at your option) any later version.
##
##  This program is distributed in the hope that it will be useful,
##  but WITHOUT ANY WARRANTY; without even the implied warranty of
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
##  General Public License for more details.
##
##  You should have received a copy of the GNU General Public License
##  along with this program; if not, write to the Free Software
##  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
##  USA, or contact Ralf S. Engelschall <rse@engelschall.com>.
##

#   sane run-time environment
umask 022

#   parameter defaults
prefix="@l_prefix@"
tag="snakeoil"
password="snakeoil"
domain="snakeoil.invalid"
organization="Snakeoil Corporation"
lifetime="3650"
num_server="2"
num_client="4"
num_object="4"
tmpdir="${TMPDIR-/tmp}"
outdir="`pwd`"

#   parse command line options
while [ $# -gt 0 ]; do
    case "$1" in
        --prefix=*       ) prefix=`echo "$1"       | sed -e 's;^--[^=]*=;;'`; shift ;;
        --tag=*          ) tag=`echo "$1"          | sed -e 's;^--[^=]*=;;'`; shift ;;
        --password=*     ) password=`echo "$1"     | sed -e 's;^--[^=]*=;;'`; shift ;;
        --domain=*       ) domain=`echo "$1"       | sed -e 's;^--[^=]*=;;'`; shift ;;
        --organization=* ) organization=`echo "$1" | sed -e 's;^--[^=]*=;;'`; shift ;;
        --lifetime=*     ) lifetime=`echo "$1"     | sed -e 's;^--[^=]*=;;'`; shift ;;
        --num-server=*   ) num_server=`echo "$1"   | sed -e 's;^--[^=]*=;;'`; shift ;;
        --num-client=*   ) num_client=`echo "$1"   | sed -e 's;^--[^=]*=;;'`; shift ;;
        --num-object=*   ) num_object=`echo "$1"   | sed -e 's;^--[^=]*=;;'`; shift ;;
        --tmpdir=*       ) tmpdir=`echo "$1"       | sed -e 's;^--[^=]*=;;'`; shift ;;
        --outdir=*       ) outdir=`echo "$1"       | sed -e 's;^--[^=]*=;;'`; shift ;;
        --help )
           echo "Usage: x509-util [--prefix=<prefix>] [--tag=<tag>] [--password=<password>]"
           echo "    [--domain=<domain>] [--organization=<organization>] [--lifetime=<lifetime>]"
           echo "    [--num-server=<num-server>] [--num-client=<num-client>] [--num-object=<num-object>]"
           echo "    [--tmpdir=<tmpdir>] [--outdir=<outdir>] [--help]"
           exit 0
           ;;
        * )
           break
           ;;
    esac
done

#   display information about parameters
echo "++ generating X.509 certificate/key set with configuration:"
echo "-- OpenPKG prefix                (--prefix):       $prefix"
echo "-- Short-hand tag                (--tag):          $tag"
echo "-- Encryption Password           (--password):     $password"
echo "-- DNS Domain                    (--domain):       $domain"
echo "-- Organization Name             (--organization): $organization"
echo "-- Certificate Lifetimes         (--lifetime):     $lifetime"
echo "-- Number of Server Certificates (--num-server):   $num_server"
echo "-- Number of Client Certificates (--num-client):   $num_client"
echo "-- Number of Object Certificates (--num-object):   $num_object"
echo "-- Temporary Directory           (--tmpdir):       $tmpdir"
echo "-- Output Directory              (--outdir):       $outdir"

#   sanity check environment
if [ ! -x "$prefix/bin/shtool" ]; then
    echo "ERROR: $prefix/bin/shtool (OpenPKG \"shtool\" package) required" 1>&2
    exit 1
fi
if [ ! -x "$prefix/bin/csp" ]; then
    echo "ERROR: $prefix/bin/csp (OpenPKG \"csp\" package) required" 1>&2
    exit 1
fi
if [ ! -x "$prefix/bin/perl" ]; then
    echo "ERROR: $prefix/bin/perl (OpenPKG \"perl\" package) required" 1>&2
    exit 1
fi

#   ensure out directory exists
test -d $outdir || mkdir $outdir 2>/dev/null || true

#   create temporary location
echo "++ creating temporary location"
if [ ! -d $tmpdir ]; then
    echo "$0: ERROR: temporary directory not existing: $tmpdir" 1>&2
    exit 1
fi
tmpdir="$tmpdir/x509-util.$$.tmp"
test -d $tmpdir && rm -rf $tmpdir >/dev/null 2>&1 || true
mkdir $tmpdir || exit $?
cd $tmpdir || exit $?

#   display processing information
echo "++ creating CSP environment"

#   create CSP environment
rm -rf etc csp
ln -s $prefix/etc/csp etc
mkdir csp
$prefix/bin/csp $tag create

#   optionally adjust configuration
$prefix/bin/shtool subst -q \
    -e "s;example\\.com;$domain;g" \
    csp/$tag/extensions.conf
rm -f csp/$tag/extensions.conf.orig

#   display processing information
echo "++ generate CA private key and certificate"

#   generate CA private key and certificate
$prefix/bin/csp $tag init \
    --keysize=4096 --keypass="$password" --type=ca --days=$lifetime \
    "CN=Certificate Authority (CA), OU=Certification Department, O=$organization"
idx=0

#   make all files available under common path
echo "-- $tag-ca.key.pem"
cp csp/$tag/private/ca.key $outdir/$tag-ca.key.pem
echo "-- $tag-ca.crt.pem"
cp csp/$tag/ca.crt $outdir/$tag-ca.crt.pem

#   provide textual presentations
echo "-- $tag-ca.key.txt"
$prefix/bin/openssl rsa \
    -in $outdir/$tag-ca.key.pem -passin "pass:$password" \
    -noout -text \
    >$outdir/$tag-ca.key.txt
echo "-- $tag-ca.crt.txt"
$prefix/bin/openssl x509 \
    -in $outdir/$tag-ca.crt.pem \
    -noout -text \
    >$outdir/$tag-ca.crt.txt

#   generate regular private keys and certificates
for type in server client object; do
    eval "max=\$num_$type"
    i=1
    while [ $i -le $max ]; do
        #   determine name
        name="$type"
        if [ $max -gt 1 ]; then
            name="$name$i"
        fi
        i=`expr $i + 1`

        echo "++ generate $type private key and certificate"

        #   generate private key and certificate request
        echo "-- $tag-$name.key.pem"
        department=`$prefix/bin/perl -e 'printf("%s%s", uc(substr($ARGV[0], 0, 1)), substr($ARGV[0], 1));' $type`
        keysize=1024
        if [ ".$type" = .server ]; then
            keysize=2048
        fi
        $prefix/bin/csp $tag request \
            --csrfile=$outdir/$tag-$name.csr.pem \
            --keyfile=$outdir/$tag-$name.key.pem \
            --keysize=$keysize --keypass="$password" \
            "CN=$name.$domain, OU=$department, O=$organization"

        #   generate server certificate
        echo "-- $tag-$name.crt.pem"
        echo "y" | $prefix/bin/csp $tag sign \
            --csrfile=$outdir/$tag-$name.csr.pem \
            --type=$type --days=$lifetime --capass="$password" >/dev/null

        #   cleanup by removing certificate request
        rm -f $outdir/$tag-$name.csr.pem

        #   make all files available under common path
        idx=`expr $idx + 1`
        cp  csp/$tag/certs/`echo . | awk '{ printf("%02X", idx); }' idx=$idx`.pem \
            $outdir/$tag-$name.crt.pem

        #   provide textual presentations
        echo "-- $tag-$name.key.txt"
        $prefix/bin/openssl rsa \
            -in $outdir/$tag-$name.key.pem -passin "pass:$password" \
            -noout -text \
            >$outdir/$tag-$name.key.txt
        echo "-- $tag-$name.crt.txt"
        $prefix/bin/openssl x509 \
            -in $outdir/$tag-$name.crt.pem \
            -noout -text \
            >$outdir/$tag-$name.crt.txt

        #   make server private key insecure but necessarily unprotected
        #   for unattended use by a server daemon
        if [ ".$type" = .server ]; then
            $prefix/bin/openssl rsa \
                -in  $outdir/$tag-$name.key.pem -passin "pass:$password" \
                -out $outdir/$tag-$name.key.pem.new 2>/dev/null
            mv  $outdir/$tag-$name.key.pem.new \
                $outdir/$tag-$name.key.pem
        fi
    done
done

#   cleanup
echo "++ deleting temporary location"
cd / || exit $?
rm -rf $tmpdir >/dev/null 2>&1 || true

