New user's registration have been closed due to high spamming and low trafic on this forum. Please contact forum admins directly if you need an account. Thanks !

Easy certificate management

A collection of tips on howto tweak your Bubba.
Post Reply
Gordon
Posts: 1461
Joined: 10 Aug 2011, 03:18

Easy certificate management

Post by Gordon »

I wrote this little script a while back to make my own life simpler handling certificates. I've adapted it to work on the B3 (Debian handles some things differently than the server I had this running on) and made some small additions to handle revoking certificates as well.

Before using the script edit the `Certificate info` block to match whatever names you'd like to use (it doesn't really have to exist). The script will, on demand, create a root certificate, a signed server certificate, a signed personal certificate and a revoke list.

Code: Select all

#!/bin/bash
####################################################################
#
# Helper script for creating self signed certificates
# - author: Gordon
# - revision: 2
#
####################################################################

#-------------------------------
# Certificate info 
#  - change these to reflect your own organization
#-------------------------------
ROOTNAME="ACME Corp. CA"
ORGANIZATION="ACME Corp."
COUNTRY=NL
PROVINCE="Zuid Holland"
CITY=Delft
EMAILDOMAIN=acmecorp.org

#-------------------------------
# Optional variables
#  - hint: don't touch these
#-------------------------------
CERTDIR=~/certs    # where the certificates will be stored
CABASE=`echo $ROOTNAME | sed "s/[^A-Za-z]//g"`    # the file name given to your root certificate
CERTDAYS=3650     # number of days that the certificate to create will be valid
CADAYS=7300   # number of days that the certificate authority will be valid

#-------------------------------
# Code start
#-------------------------------
function createrootcert {
  echo "Creating new Root Certificate Authority"
  mkdir -p $CERTDIR/CA   # this folder will hold the CA and accompanying files
  mkdir -p $CERTDIR/temp   # files placed here can be safely deleted afterwards, e.g. cert requests
  # If a root certificate already exists then print a warning and exit
  if [ -f $CERTDIR/CA/$CABASE.crt ]; then
    echo "Root certificate already exists!"
    exit
  fi
  # Create Root authority
  /usr/bin/openssl genrsa -out $CERTDIR/CA/$CABASE.key 2048 >/dev/null 2>&1
  /usr/bin/openssl req -new -x509 -days $CADAYS -key $CERTDIR/CA/$CABASE.key \
    -out $CERTDIR/CA/$CABASE.crt >/dev/null 2>&1<< EOREQ
$COUNTRY
$PROVINCE
$CITY
$ORGANIZATION
Certificate Authority
$ROOTNAME
info@$EMAILDOMAIN
EOREQ

  # We need to create some additional files to be able to sign our certificates
  echo "01" > $CERTDIR/CA/$CABASE.srl       # a file to hold the serial number
  touch $CERTDIR/CA/index.txt     # the certificate store
  echo "unique_subject = no" > $CERTDIR/CA/index.txt.attr   # an attribute file; 

  # Create a private copy of the openssl conf file and add a new conf entry - we'll not use `default`
  # because we don't know what that entry contains (people tend to make changes in there)
  cp /etc/ssl/openssl.cnf $CERTDIR/CA/$CABASE.cnf  
  cat >> $CERTDIR/CA/$CABASE.cnf << EOCNF

[ CA_$CABASE ]
dir		= $CERTDIR
certificate     = \$dir/CA/$CABASE.crt
private_key     = \$dir/CA/$CABASE.key
serial          = \$dir/CA/$CABASE.srl
database	= \$dir/CA/index.txt
RANDFILE        = \$dir/CA/.rand
crl             = \$dir/$CABASE.crl
certs           = \$dir
crl_dir         = \$dir
new_certs_dir   = \$dir/temp
x509_extensions = usr_cert
name_opt        = ca_default
cert_opt        = ca_default
default_days    = $CERTDAYS
default_crl_days= $CERTDAYS
default_md      = sha1
preserve        = no
EOCNF
}

function checkrootcert {
  if [ ! -f $CERTDIR/CA/$CABASE.crt ]; then
    echo "You must create a root certificate first!"
    exit
  fi
}

function createrequest {
  /usr/bin/openssl genrsa -out $CERTDIR/$1.key 1024 >/dev/null 2>&1
  /usr/bin/openssl req -new -key $CERTDIR/$1.key -out $CERTDIR/temp/$1.req >/dev/null 2>&1 << EOREQ
$COUNTRY
$PROVINCE
$CITY
$ORGANIZATION
$OUNAME
$SERVERNAME
$EMAIL


EOREQ
}

function signcert {
  /usr/bin/openssl ca -config $CERTDIR/CA/$CABASE.cnf -name CA_$CABASE -policy policy_anything \
      -in $CERTDIR/temp/$1.req -out $CERTDIR/$1.crt << EOSIG
y
y
EOSIG
}

function createservercert {
  echo "Creating new server certificate for $SERVERNAME"
  EMAIL=webmaster@$EMAILDOMAIN
  OUNAME="Web Services"
  createrequest $1
  signcert $1
}

function createclientcert {
  echo "Creating new client certificate for $CLIENTNAME"
  EMAIL=$CLIENTNAME@$EMAILDOMAIN
  OUNAME="Remote Workers"
  createrequest $1
  signcert $1
  /usr/bin/openssl pkcs12 -export -in $CERTDIR/$1.crt -inkey $CERTDIR/$1.key \
    -name "$1 Remote Worker Cert" -out $CERTDIR/$1.p12
}

function createrevokationlist {
  echo "Creating new certificate revoke list"
  /usr/bin/openssl ca -gencrl -config $CERTDIR/CA/$CABASE.cnf -name CA_$CABASE \
    -out $CERTDIR/CA/$CABASE.crl 
}

function revokecert {
  echo "Revoking certificate for $SERVERNAME"
  /usr/bin/openssl ca -config $CERTDIR/CA/$CABASE.cnf -name CA_$CABASE -revoke $CERTDIR/$1.crt
  echo 1
}


#-------------------------------
# Execution start
#-------------------------------

# Init execution values
CREATEROOTCA=0
CREATESERVER=0
CREATEPERSONAL=0
REVOKECERT=0
CREATECRL=0
HELP=1

CLIENTNAME=""
SERVERNAME=""
NEXTARG=""

# Read arguments
if [ $# -gt 0 ]; then
  i=0
  while [ $i -ne $# ]; do
    i=$(( $i + 1 ))
    arg=`eval echo "\\${$i}"`
    case $arg in
      "-r")
        if [ ! -z $NEXTARG ]; then
          echo "Bad parameter"
          HELP=1
          break
        fi
        CREATEROOTCA=1
        NEXTARG="nomore"
        HELP=0
      ;;
      "-s")
        if [ ! -z $NEXTARG ]; then
          echo "Bad parameter"
          HELP=1
          break
        fi
        NEXTARG=SERVERNAME
        SERVERNAME=$arg
        CREATESERVER=1
        HELP=0
      ;;
      "-p")
        NEXTARG=CLIENTNAME
        CLIENTNAME=$arg
        CREATEPERSONAL=1
        HELP=0
      ;;
      "-d")
        if [ ! -z $NEXTARG ]; then
          echo "Bad parameter"
          HELP=1
          break
        fi
        NEXTARG=SERVERNAME
        SERVERNAME=$arg
        REVOKECERT=1
        HELP=0
      ;;
      "-l")
        if [ ! -z $NEXTARG ]; then
          echo "Bad parameter"
          HELP=1
          break
        fi
        CREATECRL=1
        NEXTARG="nomore"
        HELP=0
      ;;
      "-h")
        HELP=1
      ;;
      *)
        if [ -z $NEXTARG ]; then
          echo "Bad parameter"
          HELP=1
          break
        else
          eval $NEXTARG=$arg
          NEXTARG="nomore"
        fi
      ;;
    esac
  done
fi

# Test for an invalid combination of parameters supplied to the script
if [ $HELP -lt 1 ]; then
 HELP=`echo "$SERVERNAME$CLIENTNAME" | sed "s/-.*/-/" | sed "s/[^-][^-]*-*//" | awk -F/ '{print(length())}'`
fi

if [ $HELP -gt 0 ]; then
  echo "Usage: "`echo $0|sed "s/.*\///"`" [-r] [-s <name>] [-p <name>] [-h]"
  echo "  -r          create new root CA (pem)"
  echo "  -s <name>   create new server certificate for <name> (pem)"
  echo "  -p <name>   create new client certificate for <name> (pkcs-12)"
  echo "  -d <name>   revoke certificate for <name>"
  echo "  -l          create new Certificate Revokation List"
  echo "  -h          this help text"
  exit
fi

if [ $CREATEROOTCA -gt 0 ]; then
  createrootcert
  createrevokationlist
fi

if [ $CREATESERVER -gt 0 ]; then
  createservercert $SERVERNAME
fi

if [ $CREATEPERSONAL -gt 0 ]; then
  createclientcert $CLIENTNAME
fi

if [ $CREATECRL -gt 0 ]; then
  createrevokationlist
fi

if [ $REVOKECERT -gt 0 ]; then
  revokecert $SERVERNAME
  createrevokationlist
fi
Given time, I'll tell later how you can use these certificates e.g. to protect the admin interface on your B3. One small note on creating client (personal) certificates: when asked for a password you must enter one. If you don't, it will not be accepted into Windows' certstore.
Last edited by Gordon on 27 Sep 2011, 06:47, edited 1 time in total.
Gordon
Posts: 1461
Joined: 10 Aug 2011, 03:18

Re: Certificates the easy way

Post by Gordon »

Okay, apparently there are some questions about how this script functions.

First off, I wrote this script for me. As a consequence comments inside the script may be somewhat scarce in parts that are crystal-clear to me. The point of the script is that I want to be able to create certificates and not have to go through all the standard dialogues asking me what country I'm in, what organisation I belong to, etc.

If you read through the script you'll see some constructs such as these:

Code: Select all

openssl req -new -key $CERTDIR/$1.key -out $CERTDIR/temp/$1.req << EOREQ
$COUNTRY
$PROVINCE
$CITY
$ORGANIZATION
$OUNAME
$SERVERNAME
$EMAIL


EOREQ
Upto the double `less-than` character this is essentially the same command as you'll find in the wiki. The additional stuff works like an input file, but without actually needing one. `<< EOREQ` means that the script should keep echoing each (evaluated) line until it comes to the line that says `EOREQ` (in this example I chose EOREQ as an acronym for `End Of REQuest`). So rather than having to type the answers yourself, or press enter several times when you've changed openssl.cnf to use your values as default, this will enter all the right answers for you and not make any typing errors.

Here's a short example if you like to see how it works:

Code: Select all

foo=bar
cat << EOI
this is a test

the value of foo is "$foo"

EOI
Gordon
Posts: 1461
Joined: 10 Aug 2011, 03:18

Restrict access to webfolders to client certificate holders

Post by Gordon »

The next code snippit defines entries that should go into the /etc/apache2/sites-available/bubba file (you will find that the top two entries are already listed there). For this example I've set the certdir to /usr/local/etc/ssl/certs

Code: Select all

# Server Certificate:
SSLCertificateFile /usr/local/etc/ssl/certs/mybubba.dyndns.org.crt

# Server Private Key:
SSLCertificateKeyFile /usr/local/etc/ssl/certs/mybubba.dyndns.org.key

# Server Certificate Chain:
SSLCertificateChainFile /home/web/certs/ACMECorpCA.crt


# Certificate Revocation Lists (CRL):
# This enables you to revoke a users permission to the shielded
# web folders. The revocation list will tell the server that a
# certificate has become invalid even though its expiration date
# has not been reached.
SSLCARevocationFile /usr/local/etc/ssl/certs/CA/ACMECorpCA.crl

# Certificate Authority (CA):
# This is the CA that will be used for client authentication.
# Since we created all certificates using the same CA, this is
# the same as the SSLCertificateChainFile
SSLCACertificateFile /home/web/certs/ACMECorpCA.crt


# shield 'extranet' so that only users with a valid certificate
# that is signed by my CA will be able to access it.
# Additionaly check specific values in the certificate that
# identify whether it is in fact meant to provide access to this
# folder (it may have been issued for some other purpose)
<Directory /home/web/extranet>
  SSLVerifyClient require
  SSLVerifyDepth 1

  SSLRequire  %{SSL_CLIENT_S_DN_O} eq "ACME Corp."  && \
              %{SSL_CLIENT_S_DN_OU} eq "Remote Workers"
</Directory>
Note that I'm referencing to the root certificate, aka CA, within the web content. There's no specific reason for that, other than that you should publish the CA (just the public part!). Officially you're supposed to publish the CRL as well, but since the Bubba will be the only place where we'll be doing any checking there's no real need to do so. If you download and install/import the CA in your webbrowser, it will no longer complain about the certificate used by the webserver. You must install the CA if you want to use a personal client certificate because it will otherwise be invalid.
Post Reply