initial commit of ca-scripts devel work

This commit is contained in:
Alex Bramley 2009-10-12 22:46:50 +01:00
commit 348a0df638
19 changed files with 1121 additions and 0 deletions

169
bin/ca-create-cert Executable file
View file

@ -0,0 +1,169 @@
#! /bin/bash
. "/home/alex/code/ca-scripts/lib/ca-functions"
ALT_NAMES=()
TPL_ONLY=0
CSR_ONLY=0
CRT_ONLY=0
MAKE_P12=0
# XXX: in the ca_extension_policy section of ca-config.tpl it states that the
# C= and O= DN values in a CSR have to match those of the CA
# should we have options here to change them when it will cause breakage?
usage() {
cat <<__EOT__
Usage:
$PROGNAME -t server [options] <hostname>
$PROGNAME -t client [options] <hostname>
$PROGNAME -t user [options] <username>
Options:
-h, --help Print this helpful message!
-c, --encrypt Encrypt certificate private key with Triple-DES
-f, --config FILE Use config file instead of $CONFFILE
-t, --type Certificate type: "server", "client" or "user"
-n, --alt-name Alternative host name (can be provided multiple times)
-p, --pkcs12 Create PKCS#12 certificate archive from generated cert
-r, --csr-only Only generate CSR, don't sign it
-s, --crt-only Only sign certificate, requires CSR in place
-x, --tpl-only Only generate templates, do not create CSR or sign CRT
--country Certificate DN -- C
--state Certificate DN -- ST
--loc Certificate DN -- L
--org Certificate DN -- O
--org-unit Certificate DN -- OU
--email Certificate DN -- E
--comment Certificate nsComment field
__EOT__
}
short='hcf:t:n:prsx'
long='help,encrypt.config:,type:,alt-name:,csr-only,crt-only,tpl-only,pkcs12'
long="$long,country:,state:,loc:,org:,org-unit:,email:,comment:"
opts=$( getopt -o "$short" -l "$long" -n "$PROGNAME" -- "$@" )
if [ 0 -ne $? ]; then echo; usage; exit 1; fi
eval set -- "$opts";
while :; do
case "$1" in
-h|--help) usage; exit 0;;
-c|--encrypt) CRYPTKEY=""; shift;;
-f|--config) shift; CONFFILE="$1"; shift;;
-t|--type) shift; CA_CRT_TYPE="$1"; shift;;
-n|--alt-name) shift; ALT_NAMES+=("$1"); shift;;
-p|--pkcs12) MAKE_P12=1; shift;;
-r|--csr-only) CSR_ONLY=1; shift;;
-s|--crt-only) CRT_ONLY=1; shift;;
-x|--tpl-only) TPL_ONLY=1; shift;;
--country) shift; CA_CRT_C="$1"; shift;;
--state) shift; CA_CRT_ST="$1"; shift;;
--location) shift; CA_CRT_L="$1"; shift;;
--org) shift; CA_CRT_O="$1"; shift;;
--org-unit) shift; CA_CRT_OU="$1"; shift;;
--email) shift; CA_CRT_E="$1"; shift;;
--comment) shift; CA_CRT_COMMENT="$1"; shift;;
--) shift; break;;
*) echo "Unknown value '$1'"; exit 1;;
esac
done
CA_CRT_CN="$1";
# load up the configuration file
ca_load_conf
# parameter checking fun -- we need a type and a cn (either user or host name)
if [ -z "$CA_CRT_CN" ]; then
error "The host or username parameter is mandatory!"
fi
if [ 1 -eq "$CSR_ONLY" -a 1 -eq "$CRT_ONLY" ]; then
error "Options --csr-only and --crt-only are mutually exclusive."
fi
if [ "$CA_CRT_TYPE" = "user" ]; then
# append @$CA_DOMAIN to user CN if it's not already there
if [ "${CA_CRT_CN%%@*}" = "$CA_CRT_CN" ]; then
CA_CRT_CN="$CA_CRT_CN@$CA_DOMAIN";
fi
else
# fully qualify server or client CN with $CA_DOMAIN if it's not already
if [ "${CA_CRT_CN%%.*}" = "$CA_CRT_CN" ]; then
# however we may also want the unqualified one as an alt-name
ALT_NAMES+=("$CA_CRT_CN")
CA_CRT_CN="$CA_CRT_CN.$CA_DOMAIN"
fi
fi
CNF_NAME=$( echo -n "$CA_CRT_CN" | tr -c '[:alnum:]@-' _ )".$CA_CRT_TYPE";
# if they've provided a comment, reformat it correctly
if [ -n "$CA_CRT_COMMENT" ]; then
CA_CRT_COMMENT="$( tr -d\" <<< $CA_CRT_COMMENT )"
CA_CRT_COMMENT="nsComment = \"$CA_CRT_COMMENT\"\n"
else
CA_CRT_COMMENT=""
fi
CA_CRT_ALT_NAMES=""
# generate a list of alternative DNS names for server certificates
if [ "$CA_CRT_TYPE" = "server" ]; then
i=1
for ALT_NAME in "$CA_CRT_CN" "${ALT_NAMES[@]}"; do
# also fully-qualify unqualified alt-names too (see below)
if [ "${ALT_NAME%%.*}" = "$ALT_NAME" ]; then
CA_CRT_ALT_NAMES="${CA_CRT_ALT_NAMES}DNS.$i=$ALT_NAME.$CA_DOMAIN\n"
i=$(( $i+1 ))
fi
CA_CRT_ALT_NAMES="${CA_CRT_ALT_NAMES}DNS.$i=$ALT_NAME\n"
i=$(( $i+1 ))
done
fi
if [ 1 -ne "$CRT_ONLY" ]; then
if [ 1 -eq "$TPL_ONLY" -o "$CSR_ONLY" -eq "$TPL_ONLY" ]; then
# dirty logic here that probably needs commenting!
# generate a *new* certificate request configuration if...
# a) --tpl-only is set, i.e. we only want to generate a config
# b) both --tpl-only and --csr-only are unset, i.e.
# we're just generating a csr/crt as per usual
# c) both --tpl-only and --csr-only are set, i.e.
# we're just generating the config and not the csr itself
ca_template "req-config" "$CA_HOME/cnf/$CNF_NAME.req.cnf"
fi
if [ 1 -ne "$TPL_ONLY" ]; then
if [ ! -f "$CA_HOME/cnf/$CNF_NAME.req.cnf" ]; then
error "Couldn't find CSR config $CA_HOME/cnf/$CNF_NAME.req.cnf!"
fi
# the above logic means that if you pass --csr-only but not
# --tpl-only, you can re-use a pre-existing config to generate
# a new csr, should you wish to do so...
openssl req -new $CRYPTKEY -config "$CA_HOME/cnf/$CNF_NAME.req.cnf" \
-keyout "$CA_HOME/key/$CNF_NAME.key" \
-out "$CA_HOME/csr/$CNF_NAME.csr"
fi
fi
if [ 1 -ne "$CSR_ONLY" ]; then
if [ 1 -eq "$TPL_ONLY" -o "$CRT_ONLY" -eq "$TPL_ONLY" ]; then
# same logic above applies here, but for generating the extensions
# configuration file and signed certificate instead
ca_template "$CA_CRT_TYPE-ext" "$CA_HOME/cnf/$CNF_NAME.ext.cnf"
fi
if [ 1 -ne "$TPL_ONLY" ]; then
# ensure relevant files are in place before continuing...
if [ ! -f "$CA_HOME/csr/$CNF_NAME.csr" ]; then
error "CSR not present in $CA_HOME/csr/$CNF_NAME.csr"
fi
if [ ! -f "$CA_HOME/cnf/$CNF_NAME.ext.cnf" ]; then
error "Couldn't find extensions in $CA_HOME/cnf/$CNF_NAME.ext.cnf"
fi
openssl ca -config "$CA_HOME/cnf/$CA_NAME.ca.cnf" \
-extfile "$CA_HOME/cnf/$CNF_NAME.ext.cnf" -batch \
-out "$CA_HOME/crt/$CNF_NAME.crt" \
-in "$CA_HOME/csr/$CNF_NAME.csr"
fi
fi
if [ 1 -eq "$MAKE_P12" ]; then
ca_gen_p12 "$CNF_NAME"
fi

72
bin/ca-init Executable file
View file

@ -0,0 +1,72 @@
#! /bin/bash
. "/home/alex/code/ca-scripts/lib/ca-functions"
usage() {
cat <<__EOT__
Usage: $PROGNAME [options]
Options:
-h, --help Print this helpful message!
-c, --encrypt Encrypt CA private key with Triple-DES
-f, --config FILE Use config file instead of $CONFFILE
-i, --template FILE Use alternative index.html template
-o, --output FILE Generate CA index.html in FILE
__EOT__
}
short='hcf:i:o:'
long='help,encrypt,config:,template:,output:'
opts=$( getopt -o "$short" -l "$long" -n "$PROGNAME" -- "$@" )
if [ 0 -ne $? ]; then echo; usage; exit 1; fi
eval set -- "$opts";
while :; do
case "$1" in
-h|--help) usage; exit 0;;
-c|--encrypt) CRYPTKEY=""; shift;;
-f|--config) shift; CONFFILE="$1"; shift;;
-i|--template) shift; INDEXTPL="$1"; shift;;
-o|--output) shift; INDEXOUT="$1"; shift;;
--) shift; break;;
*) echo "Unknown value '$1'"; exit 1;;
esac
done
# load up the configuration file
CA_CRT_TYPE="ca"
ca_load_conf
# create the directory structure that'll be populated by the scripts
mkdir -p $CA_HOME/{cnf,crl,crt,csr,db,idx,key,p12}
echo "01" > $CA_HOME/db/crlnumber
touch $CA_HOME/db/index.txt
touch $CA_HOME/db/.rand
chmod 600 $CA_HOME/db/.rand
chmod 700 $CA_HOME/key
# generate an openssl configuration for this CA
ca_template ca-config "$CA_HOME/cnf/$CA_NAME.ca.cnf"
# generate a self-signed cert that is valid for 10 years, with
# ... the private key in $CA_HOME/key/$CA_NAME.ca.key
# ... the certificate in $CA_HOME/crt/$CA_NAME.ca.crt
# ... using the config in $CA_HOME/cnf/$CA_NAME.ca.cnf
openssl req -new $CRYPTKEY -config "$CA_HOME/cnf/$CA_NAME.ca.cnf" \
-keyout "$CA_HOME/key/$CA_NAME.ca.key" \
-out "$CA_HOME/csr/$CA_NAME.ca.csr"
openssl ca -create_serial -selfsign -days 3652 -batch \
-name ca_scripts -extensions ca_x509_extensions \
-config "$CA_HOME/cnf/$CA_NAME.ca.cnf" \
-in "$CA_HOME/csr/$CA_NAME.ca.csr" \
-keyfile "$CA_HOME/key/$CA_NAME.ca.key" \
-out "$CA_HOME/crt/$CA_NAME.ca.crt"
# generate an initial CRL too (yes it will be empty, but we should serve it)
ca_gen_crl
if [ -n "$INDEXOUT" ]; then
ca_checksum
ca_template $INDEXTPL $INDEXOUT
fi

80
bin/ca-renew-cert Executable file
View file

@ -0,0 +1,80 @@
#! /bin/bash
. "/home/alex/code/ca-scripts/lib/ca-functions"
usage() {
cat <<__EOT__
Usage: $PROGNAME -t <type> [options] <hostname|username|certpath>
Options:
-h, --help Print this helpful message!
-f, --config FILE Use config file instead of $CONFFILE
-t, --type Certificate type: "server", "client" or "user"
__EOT__
}
short='hf:t:'
long='help,config:,type:'
opts=$( getopt -o "$short" -l "$long" -n "$PROGNAME" -- "$@" )
if [ 0 -ne $? ]; then echo; usage; exit 1; fi
eval set -- "$opts";
while :; do
case "$1" in
-h|--help) usage; exit 0;;
-f|--config) shift; CONFFILE="$1"; shift;;
-t|--type) shift; CA_CRT_TYPE="$1"; shift;;
--) shift; break;;
*) echo "Unknown value '$1'"; exit 1;;
esac
done
CNF_NAME="$1"
ca_load_conf
CNF_NAME=$( ca_find_cnf "$CNF_NAME" )
CRT="$CA_HOME/crt/$CNF_NAME.crt"
# make sure that configuration files are present as expected
if [ ! -f "$CA_HOME/cnf/$CNF_NAME.ext.cnf" ]; then
error "Couldn't find extensions in $CA_HOME/cnf/$CNF_NAME-ext.cnf"
fi
# according to the below URL we should create the new CRT using the old CSR
# and with the same serial as the previous certificate.
# http://blog.fupps.com/2007/11/30/x509ssl-certificate-prolongation/
# After some fun googling, I found the following URL which tells us how...
# http://ca.dutchgrid.nl/info/CA_gymnastics.html
# XXX: this is only *really* relevant for certs that have been used for code
# or e-mail encryption. should we regenerate client/server certs entirely?
# ... for the moment there's always the revoke/recreate route for people.
# acquire required info from old certificate
ENDDATE=$( openssl x509 -in "$CRT" -noout -enddate | cut -d= -f2 )
SERIAL=$( openssl x509 -in "$CRT" -noout -serial | cut -d= -f2 )
# work out new expiry date based on expiry date of current cert + 1 year
# these dates are "<year> <day of year>"
export TZ=UTC
NOWYEAR=$( date +%Y )
NOWDAYS=$( date +%j )
ENDYEAR=$( date +%Y -d "$ENDDATE + 1 year" )
ENDDAYS=$( date +%j -d "$ENDDATE + 1 year" )
CERTDATE=$( date +%Y-%m-%d -d "$ENDDATE" )
# and this does the maths to work out how many days there are from now
# (when we're creating the new cert) to the new expiry date
DAYS=$(( ($ENDYEAR-$NOWYEAR)*365 + ($ENDDAYS-$NOWDAYS) ))
# Now perform required CA gymnastics ;p
openssl x509 -req -set_serial "0x$SERIAL" -days "$DAYS" \
-CA "$CA_HOME/crt/$CA_NAME.ca.crt" \
-CAkey "$CA_HOME/key/$CA_NAME.ca.key" \
-extfile "$CA_HOME/cfg/$CNF_NAME.ext.cnf" \
-out "$CA_HOME/crt/$CNF_NAME.crt" \
-in "$CA_HOME/csr/$CNF_NAME.csr"
# This doesn't update the original certificate in the index, so let's do that
mv "$CA_HOME/idx/$SERIAL.pem" "$CA_HOME/idx/$SERIAL.$CERTDATE.pem"
cp "$CA_HOME/crt/$CNF_NAME.crt" "$CA_HOME/idx/$SERIAL.pem"

51
bin/ca-revoke-cert Executable file
View file

@ -0,0 +1,51 @@
#! /bin/sh
. "/home/alex/code/ca-scripts/lib/ca-functions"
usage() {
cat <<__EOT__
Usage: $PROGNAME -t <type> [options] <hostname|username|certpath>
Options:
-h, --help Print this helpful message!
-f, --config FILE Use config file instead of $CONFFILE
-t, --type Certificate type: "server", "client" or "user"
-i, --template FILE Use alternative index.html template
-o, --output FILE Generate CA index.html in FILE
__EOT__
}
short='hf:t:i:o:'
long='help,config:,type:,template:,output:'
opts=$( getopt -o "$short" -l "$long" -n "$PROGNAME" -- "$@" )
if [ 0 -ne $? ]; then echo; usage; exit 1; fi
eval set -- "$opts";
while :; do
case "$1" in
-h|--help) usage; exit 0;;
-f|--config) shift; CONFFILE="$1"; shift;;
-t|--type) shift; CA_CRT_TYPE="$1"; shift;;
-i|--template) shift; INDEXTPL="$1"; shift;;
-o|--output) shift; INDEXOUT="$1"; shift;;
--) shift; break;;
*) echo "Unknown value '$1'"; exit 1;;
esac
done
CNF_NAME="$1"
ca_load_conf
CNF_NAME=$( ca_find_cnf "$CNF_NAME" "$TYPE" )
CRT="$CA_HOME/crt/$CNF_NAME.crt"
openssl ca -config $CA_HOME/cnf/$CA_NAME.ca.cnf \
-revoke $CRT -crl_reason superseded
ca_gen_crl
if [ -n "$INDEXOUT" ]; then
ca_checksum
ca_template $INDEXTPL $INDEXOUT
fi

45
ca-scripts.conf Normal file
View file

@ -0,0 +1,45 @@
# example ca-scripts configuration file
# REQUIRED: CA_HOME provides the path to the root of the CA directory tree
# this directory must exist and be writeable
#CA_HOME="/etc/ssl/ca"
CA_HOME="/tmp/ca"
# REQUIRED: CA_DOMAIN provides a template for other optional variables and
# the filenames that are generated within the directory tree
CA_DOMAIN="example.com"
# OPTIONAL: CA_NAME is the internal templating variable for filenames etc
# Defaults to:
# CA_NAME="$( echo $CA_DOMAIN | tr 'A-Z' 'a-z' | tr -c '-a-z0-9' '_' )"
# REQUIRED: CA_DN_* configures the Distinguished Name fields present in the
# CA certificate generated by ca-init
CA_DN_C="GB"
CA_DN_ST="London"
CA_DN_L="Example House, Mayfair"
CA_DN_O="Example Security Services Ltd."
CA_DN_OU="Example Internet Encryption Division"
CA_DN_CN="Example Security Services Root Certificate Authority"
# OPTIONAL: CA_DESC configures a single-line description for your CA
# using the CN= or O= line from your DN is recommended
# Default value:
# CA_DESC="$CA_DN_CN"
# OPTIONAL: CA_EMAIL provides an e-mail address that is embedded into all
# generated certificates as a point-of-contact
# Default value:
# CA_EMAIL="ca@$CA_DOMAIN"
# OPTIONAL: CA_CRT_URI and CA_CRL_URI provide locations where the CA
# certificate and revocation lists can be found
# Default value:
# CA_CRT_URI="http://$CA_DOMAIN/ca/$CA_NAME.ca.crt"
# CA_CRL_URI="http://$CA_DOMAIN/ca/$CA_NAME.ca.crl"
# OPTIONAL: CA_PATHLEN sets the maximum number of intermediate CA certificates
# that can be in the chain of authority between the root CA and the
# final certificate.
# Default value:
# CA_PATHLEN=0

61
doc/README Normal file
View file

@ -0,0 +1,61 @@
1. Creating a Certificate Authority.
To fully understand it's contents you're unfortunately going to need to read ca(1ssl),
req(1ssl), x509(1ssl), config(5ssl), and x509v3_config(5ssl). Particularly
important are the x509v3 extensions present in the certificate, which are
defined in the "stglab_x509_ca_extensions" section of the config file.
The ca-cert script configures some important files in db/, then creates a
certificate request and signs it. It also generates an initial (empty)
revocation list, then substitutes the correct fingerprints into the html
template for serving the CA certificate and CRL to the intranet.
2. Creating a certificate.
The create-cert script can generate three "types" of certificate -- server
certificates for securing a service with SSL/TLS, client certificates for
authenticating a client to these services, and user certificates for
authentication, S/MIME e-mail signing or encryption, and code signing. There
are minor but important differences in the extensions present in these
different certificate types, but these are set in the *-ext.tpl files in tpl/
and thus you shouldn't need to worry about them.
The create-cert script takes a number of arguments, of which the hostname or
username and the type are mandatory. It is also a very good idea to supply a
number of alternative DNS names when generating a server certificate, because
while the script will happily append "stglab.manchester.uk.ibm.com" to an
un-qualified host name, it won't append "transitives.com" and for the moment we
probably need that.
You should also provide a team name for the organisational unit, e.g.
"Manchester STG Lab Test", an e-mail address (preferably for the team rather
than an individual for server/client certificates), and a comment that reflects
the usage of the certificate, e.g. "Soak Infrastructure Live Server". Reasonable
defaults are provided for all of these for our team's use.
3. Renewing a certificate.
The renew-cert script does some painful certificate manipulation that is not
strictly necessary in most cases, and may in fact decrease SSL security
slightly. This is done because the normal renewal process re-generates the
certificate signing request and thus creates a new public/private keypair.
If the certificates are used for S/MIME encryption or code signing, this
renders all the encrypted e-mail unreadable and requires you to re-sign the
code with your new private key. The code in renew-cert re-signs the old
certificate request with a new expiry date and the extensions generated when
the original certificate was created, and avoids this problem.
Renewing a certificate is done by giving the hostname, username or path to
the certificate to renew-cert.sh.
4. Revoking a certificate.
Revoking a certificate is done by giving the hostname, username or path to
the certificat to revoke-cert.sh. This script also regenerates a new CRL in
both PEM and DER encodings (firefox prefers the latter while IE and other
browsers work better with the former), and re-generates the html file with the
new fingerprints.

17
doc/ca-cert.txt Normal file
View file

@ -0,0 +1,17 @@
# a brief man-page for ca-cert.sh
# $Id: ca-cert.txt 2660 2009-07-24 18:49:52Z alexeb $
NAME
ca-cert.sh - generate a CA cert and perform initial db setup
SYNOPSIS
ca-cert.sh
DESCRIPTION
This script generates a CSR and signs it to turn it into a root
certificate authority. It also sets up some important files in the CA
database directory, generates an initial empty revocation list, and
creates index.html from the template.
OPTIONS
This script takes no options.

97
doc/create-cert.txt Normal file
View file

@ -0,0 +1,97 @@
# a brief man-page for create-cert.sh
# $Id: create-cert.txt 2660 2009-07-24 18:49:52Z alexeb $
NAME
create-cert.sh - generate a signed X.509 certificate
SYNOPSIS
create-cert.sh -t server [options] <hostname>
create-cert.sh -t client [options] <hostname>
create-cert.sh -t user [options] <username>
DESCRIPTION
The create-cert.sh script creates the configuration files necessary
for generating a signed X.509 certificate, creates a certificate
signing request using these configuration files, and signs that request
using the root CA key so that it is trusted by anything that has
imported the CA certificate.
OPTIONS
-h, --help
Prints out a short synopsis of the arguments that this script takes.
-t, --type {server|client|user}
This argument is mandatory. create-cert.sh can create three types of
X.509 certificate: server, client, and user. These differ in the
X.509v3 extensions present, and in the uses the certificate is trusted
for.
Server certificates are used for securing SSL/TLS services, such as
TLS-encrypted LDAP connections or SSL HTTP. In this case the <hostname>
argument is used for the Common Name in the certificate, and any
additional alternative names supplied by -n are added to the X.509v3
"SubjectAltName" extension.
Client certificates are used for authenticating to SSL/TLS services.
For the most part they will be used by automated systems to identify
and authenticate to services they interact with.
User certificates are for individuals to authenticate themselves to
SSL/TLS services in the same manner as client certificates, but they
may also be used for S/MIME e-mail encryption and code signing.
-c, --comment "COMMENT"
This argument sets the "Netscape Comment" X.509 extension.
-n, --alt-name HOSTNAME
This argument adds an alternative hostname to the "SubjectAltName"
X.509v3 extension. It may be supplied multiple times to add more than
one additional hostname.
-l, --location LOCATION
This argument sets the "Location" field of the certificate's
distinguished name. Syggested values are "Maybrook House" and
"Jackson House", but the field is freeform text.
-o, --org-unit TEAMNAME
This argument sets the "Organisational Unit" field of the certificate's
distinguished name. Ideally this should begin with "Manchester STG Lab"
for consistency's sake, for example:
Manchester STG Lab Systems and Network Infrastructure
Manchester STG Lab Testing
Manchester STG Lab Starlight Development
-e, --email EMAIL
This argument sets the "E-Mail Address" field of the certificate's
distinguished name. As per current X.509 standards this is actually
removed from the DN of the CSR and placed into the "SubjectAltName"
extension in the signed certificate. In general it should be a team
alias rather than an individual's address for server and client certs.
-r, --csr-only
This argument causes create-cert.sh to only generate a new CSR. It will
not generate the request configuration files in cfg/ unless --tpl-only
is also passed; in this case it will just create the configuration
files instead. This allows you to re-generate a CSR after manually
tweaking the configuration files.
-s, --crt-only
This argument causes create-cert.sh to only sign an existing CSR. As
with --csr-only, it will not generate extension configuration files
unless --tpl-only is also passed; again in this case it will just
create the configuration files so that you can re-sign the same CSR
with new extensions.
-t, --tpl-only
This argument modifies the behaviour of the previous two options when
passed with them, as described above. On it's own it causes
create-cert.sh to generate both sets of configuration files, but
not generate either the signing request or the signed certificate.
DEFAULTS
* The LOCATION defaults to "Maybrook House"
* The TEAM defaults to "Manchester STG Lab Systems and Network Infrastructure"
* The EMAIL defaults to "mcr_lab_lsni@wwpdl.vnet.ibm.com"
* There is no COMMENT set by default

17
doc/create-p12.txt Normal file
View file

@ -0,0 +1,17 @@
# a brief man-page for create-p12.sh
# $Id: create-p12.txt 2660 2009-07-24 18:49:52Z alexeb $
NAME
create-p12.sh - create a PKCS#12 archive of a certificate and key
SYNOPSIS
create-p12.sh /path/to/certificate
create-p12.sh <username>
DESCRIPTION
This script exports a PKCS#12 archive containing a user's certificate,
private key, and the CA certificate. It will prompt for a password to
lock the archive with, and then place it in p12/.
OPTIONS
This script takes no options.

20
doc/renew-cert.txt Normal file
View file

@ -0,0 +1,20 @@
# a brief man-page for renew-cert.sh
# $Id: renew-cert.txt 2660 2009-07-24 18:49:52Z alexeb $
NAME
renew-cert.sh - renew a previously generated cert for another year
SYNOPSIS
renew-cert.sh /path/to/certificate
renew-cert.sh <hostname>
renew-cert.sh <username>
DESCRIPTION
This script renews a certificate for another 365 days from it's current
end-date. It does some interesting hackery to re-sign the certificate
request generated when the certificate was initially signed, using the
same key-pair and the same serial, so that S/MIME encrypted e-mail and
previously signed code does not become unusable.
OPTIONS
This script takes no options.

18
doc/revoke-cert.txt Normal file
View file

@ -0,0 +1,18 @@
# a brief man-page for revoke-cert.sh
# $Id: revoke-cert.txt 2660 2009-07-24 18:49:52Z alexeb $
NAME
revoke-cert.sh - revoke a certificate and generate revocation list
SYNOPSIS
revoke-cert.sh /path/to/certificate
revoke-cert.sh <hostname>
revoke-cert.sh <username>
DESCRIPTION
This script revokes the provided certificate and updates the revocation
list. It generates both a PEM and a DER encoded version of the CRL for
different browsers, and updates the html page with the new fingerprints.
OPTIONS
This script takes no options.

198
lib/ca-functions Normal file
View file

@ -0,0 +1,198 @@
#! /bin/bash
# common functions for ca-scripts
PROGNAME=$( basename $0 )
CONFFILE="/etc/ca-scripts.conf"
SHAREDIR="/home/alex/code/ca-scripts/tpl"
CRYPTKEY="-nodes"
INDEXTPL="index-html"
INDEXOUT=""
error() {
usage >&2
echo -e "ERROR: $1\n" >&2
exit 1
}
ca_check_var() {
local varname vartest
varname="$1"
vartest="$2"
eval "if [ ! $vartest \"\$$varname\" ]; then
echo '$varname value \"\$$varname\" failed \"$vartest\" test'
fi"
}
ca_set_default() {
local varname vardef
varname="$1"
vardef="$2"
eval "if [ -z \"\$$varname\" ]; then
$varname=\"$vardef\";
fi"
}
ca_load_conf() {
local varname vartest varerr vardef error ca_name
if [ ! -r "$CONFFILE" ]; then
error "Unable to find $CONFFILE."
fi
# XXX: seems like . <file> doesn't work if it's not relative to a directory
# look this up on the internet sometime to work out why...
if [ "$CONFFILE" = "$( basename $CONFFILE )" ]; then
CONFFILE="./$CONFFILE"
fi
. "$CONFFILE"
error=""
while read vartest varname; do
varerr=$( ca_check_var "$varname" "$vartest" )
if [ -n "$varerr" ]; then
error="$error\n $varerr"
fi
done <<__TESTS__
-d CA_HOME
-n CA_DOMAIN
-n CA_DN_C
-n CA_DN_ST
-n CA_DN_L
-n CA_DN_O
-n CA_DN_OU
-n CA_DN_CN
__TESTS__
if [ -n "$error" ]; then
error "Parsing config file $CONFFILE failed:\n$error"
fi
case "$CA_CRT_TYPE" in
server|client|user|ca) :;;
'') error "The type option is mandatory!";;
*) error "Unrecognised type '$CA_CRT_TYPE'!";;
esac
# we need to do these first to use them in other default defs
# NOTE: bash's here-string syntax appends \n which tr turns to _ :(
ca_set_default CA_NAME "$( echo -n "$CA_DOMAIN" | tr -c '[:alnum:]@-' _ )"
ca_set_default CA_EMAIL "ca@$CA_DOMAIN"
while read varname vardef; do
ca_set_default "$varname" "$vardef"
done <<__DEFAULTS__
CA_DESC $CA_DN_CN
CA_CRT_URI http://$CA_DOMAIN/ca/$CA_NAME.ca.crt
CA_CRL_URI http://$CA_DOMAIN/ca/$CA_NAME.ca.crl
CA_PATHLEN 0
CA_CRT_C $CA_DN_C
CA_CRT_ST $CA_DN_ST
CA_CRT_L $CA_DN_L
CA_CRT_O $CA_DN_O
CA_CRT_OU $CA_DN_OU
CA_CRT_E $CA_EMAIL
__DEFAULTS__
}
ca_sed_cmd() {
# MD5 in CA_CR[TL]_MD5_FP has a non alphabetic character :(
# XXX: pretty sure this is a dirty and wrong way of templating vars
set | awk -F\= '/^CA_[A-Z5_]*=/{print $1}' | while read ca_var; do
echo "s#%$ca_var%#${!ca_var}#;"
done
}
ca_template() {
local template dest
if [ -r "$1" ]; then
template="$1"
elif [ -r "$SHAREDIR/$1.tpl" ]; then
template="$SHAREDIR/$1.tpl"
else
error "Could not read from template $1"
fi
dest="$2"
sed -e "$(ca_sed_cmd)" <"$template" >"$dest"
}
ca_gen_crl() {
openssl ca -config "$CA_HOME/cnf/$CA_NAME.ca.cnf" \
-gencrl -out "$CA_HOME/crl/$CA_NAME.ca.crl" -md sha1
openssl crl -in "$CA_HOME/crl/$CA_NAME.ca.crl" \
-out "$CA_HOME/crl/$CA_NAME.ca.crl.der" -outform DER
}
ca_gen_p12() {
local cnf_name
cnf_name="$1"
openssl pkcs12 -export -descert -out $CA_HOME/p12/$cnf_name.p12 \
-in $CA_HOME/crt/$cnf_name.crt \
-inkey $CA_HOME/key/$cnf_name.key \
-certfile $CA_HOME/crt/$CA_NAME.ca.crt
}
ca_checksum() {
CA_CRT_MD5_FP="$( openssl x509 -in $CA_HOME/crt/$CA_NAME.ca.crt \
-noout -md5 -fingerprint | cut -d= -f2 )"
CA_CRT_SHA_FP="$( openssl x509 -in $CA_HOME/crt/$CA_NAME.ca.crt \
-noout -sha1 -fingerprint | cut -d= -f2 )"
CA_CRL_MD5_FP="$( openssl crl -in $CA_HOME/crl/$CA_NAME.ca.crl \
-noout -md5 -fingerprint | cut -d= -f2 )"
CA_CRL_SHA_FP="$( openssl crl -in $CA_HOME/crl/$CA_NAME.ca.crl \
-noout -sha1 -fingerprint | cut -d= -f2 )"
}
ca_cnf_name() {
local crt
crt="$1"
# work out what configuration files we should be using from the cert's CN
echo $( openssl x509 -in "$crt" -noout -nameopt sep_multiline,use_quote \
-subject | grep "CN=" | cut -d= -f2 | tr -c '[:alnum:]@-' _ )
}
ca_find_cnf() {
local name _name
name="$1"
if [ -f "$name" ]; then
if ! grep -q "$CA_CRT_TYPE" <<<"$name"; then
error "Certificate '$name' does not appear to be of type '$CA_CRT_TYPE'"
else
echo "$(ca_cnf_name $name).$CA_CRT_TYPE"
fi
return
fi
_name=$( echo -n "$name" | tr -c '[:alnum:]@-' _ )
if [ "$CA_CRT_TYPE" = "user" ]; then
# user names may have dots etc. in, so use munged version in match
# check if name is "user@domain", append $CA_DOMAIN if not
if [ "${_name%%@*}" = "$_name" \
-a -f "$CA_HOME/crt/${_name}@$CA_NAME.$CA_CRT_TYPE.crt" ];
then
# name is not fully-qualified, but a cert exists for it
echo "${_name}@$CA_NAME.$CA_CRT_TYPE"
elif [ -f "$CA_HOME/crt/$_name.$CA_CRT_TYPE.crt" ]; then
# name was fully-qualified and a cert exists
echo "$_name.$CA_CRT_TYPE"
else
error "Could not find $CA_CRT_TYPE certificate configuration matching '$name'"
fi
else
# check if name is fully-qualified -- contains more than 1 dot
# NOTE: we have to do this test with the unmunged name ...
if [ "${name%%.*}" = "$name" \
-a -f "$CA_HOME/crt/${_name}_$CA_NAME.$CA_CRT_TYPE.crt" ];
then
# name is not fully-qualified, but a cert exists for it
echo "${_name}_$CA_NAME.$CA_CRT_TYPE"
elif [ -f "$CA_HOME/crt/$_name.$CA_CRT_TYPE.crt" ]; then
# name was fully-qualified and a cert exists
echo "$_name.$CA_CRT_TYPE"
else
error "Could not find $CA_CRT_TYPE certificate configuration matching '$name'"
fi
fi
}

160
tpl/ca-config.tpl Normal file
View file

@ -0,0 +1,160 @@
# CA configuration file template
# ---------------------------------------------------------------------------- #
# This defines the CA configuration to use
[ ca ]
default_ca = ca_scripts
# ---------------------------------------------------------------------------- #
# This defines our CA configuration
[ ca_scripts ]
# interpolation variables defining the directories to use
dir = %CA_HOME% # root data directory of CA
db_dir = $dir/db # database files are kept here
csr_dir = $dir/csr # generated CSRs are kept here
crt_dir = $dir/crt # signed CRTs are kept here
key_dir = $dir/key # generated KEYs are kept here
crl_dir = $dir/crl # generated CRL is kept here
new_certs_dir = $dir/idx # default place for new CRTs
# required settings
database = $db_dir/index.txt # database index file
serial = $db_dir/serial # serial number index file
certificate = $crt_dir/%CA_NAME%.ca.crt # CA certificate
private_key = $key_dir/%CA_NAME%.ca.key # CA private key
crl = $crl_dir/%CA_NAME%.ca.crl # current CRL
RANDFILE = $db_dir/.rand # private random number file
# these two CA directives can be commented out so that v1 CRLs are created
crlnumber = $db_dir/crlnumber # crlnumber index file
crl_extensions = ca_crl_extensions # extensions in v2 CRL
# x509v3 certificate extensions and certificate signing policy
x509_extensions = ca_x509_default_extensions
copy_extensions = copy # copy extensions from CSR to CRT
policy = ca_extension_policy # policy on required CSR attributes
# leave these defaults
name_opt = oneline # Subject Name options - x509(1)
cert_opt = ca_default # Certificate field options - x509(1)
default_days = 365 # how long to certify for
default_crl_days= 365 # how long before next CRL
default_md = sha1 # which md to use.
preserve = no # keep passed DN ordering
unique_subject = no # recommended
email_in_dn = no # remove email from CSR DN when signing
# ---------------------------------------------------------------------------- #
# This defines the CA's policy on required CSR attributes.
# It requires:
# the country [C] to be supplied in the CSR and match the CA
# the state or province [ST] to be supplied in the CSR
# the locality [L] to be supplied in the CSR
# the organisation name [O] to be supplied in the CSR and match the CA
# the organisational unit [OU] to be supplied in the CSR
# the server common name [CN] to be supplied in the CSR
# ... and an [emailAddress] may optionally be supplied in the CSR
# XXX: is this too restrictive or not restrictive enough?
# should options for ca-create-cert to change "match" values even exist?
[ ca_extension_policy ]
countryName = match
stateOrProvinceName = supplied
localityName = supplied
organizationName = match
organizationalUnitName = supplied
commonName = supplied
emailAddress = optional
# ---------------------------------------------------------------------------- #
# This defines the default x509 extensions present in a cert signed by the CA.
# These should be replaced by a specific set of extensions per certificate.
[ ca_x509_default_extensions ]
# certificates signed by this CA by default are not CA certificates themselves
basicConstraints = CA:FALSE
# old netscape certificate attributes
nsCertType = server
nsComment = "%CA_DESC% Certificate"
nsRevocationUrl = %CA_CRL_URI%
# key usage restrictions
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, keyAgreement
extendedKeyUsage = serverAuth
issuerAltName = issuer:copy
subjectAltName = URI:%CA_CRT_URI%
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
authorityInfoAccess = caIssuers;URI:%CA_CRT_URI%
crlDistributionPoints = URI:%CA_CRL_URI%
# ---------------------------------------------------------------------------- #
# This defines the x509 extensions present in the generated CA certificate.
[ ca_x509_extensions ]
# this certificate is authoritative and allowed to sign other certificates
# pathlen=1 implies there may be up to one intermediate CA in the chain
# that leads to this root CA certificate.
basicConstraints = critical,CA:TRUE,pathlen:%CA_PATHLEN%
# old netscape certificate attributes
nsCertType = objsign, sslCA, emailCA, objCA
nsComment = "%CA_DESC%"
nsRevocationUrl = %CA_CRL_URI%
nsCaRevocationUrl = %CA_CRL_URI%
# key usage restrictions
keyUsage = critical, cRLSign, keyCertSign
extendedKeyUsage = serverAuth, clientAuth, codeSigning, emailProtection, timeStamping
issuerAltName = @ca_altname
subjectAltName = @ca_altname
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
authorityInfoAccess = caIssuers;URI:%CA_CRT_URI%
crlDistributionPoints = URI:%CA_CRL_URI%
# ---------------------------------------------------------------------------- #
# This is a separate section defining the attributes in the CA's subjectAltName.
[ ca_altname ]
URI=%CA_CRT_URI%
DNS.1=%CA_DOMAIN%
DNS.2=*.%CA_DOMAIN%
email=%CA_EMAIL%
# ---------------------------------------------------------------------------- #
# This defines the extensions present in the CRLs generated by this CA.
[ ca_crl_extensions ]
issuerAltName = issuer:copy
authorityKeyIdentifier = keyid:always, issuer:always
# the below is only supported in the very latest releases of openssl
# issuingDistributionPoint= URI:%CA_CRL_URI%
# ---------------------------------------------------------------------------- #
# This defines the extensions present in the CSRs created by this CA.
[ ca_req_extensions ]
basicConstraints = critical, CA:FALSE
keyUsage = critical, nonRepudiation, keyEncipherment, keyAgreement
extendedKeyUsage = serverAuth
# ---------------------------------------------------------------------------- #
# This defines default settings for certificate requests and CA cert creation.
[ req ]
default_bits = 2048
default_md = sha1
distinguished_name = ca_req_dn
x509_extensions = ca_x509_extensions
req_extensions = ca_req_extensions
string_mask = nombstr
prompt = no
# ---------------------------------------------------------------------------- #
# This defines the DN of the CA certificate.
[ ca_req_dn ]
C = %CA_DN_C%
ST = %CA_DN_ST%
L = %CA_DN_L%
O = %CA_DN_O%
OU = %CA_DN_OU%
CN = %CA_DN_CN%

17
tpl/client-ext.tpl Normal file
View file

@ -0,0 +1,17 @@
basicConstraints = critical, CA:FALSE
nsCertType = client
nsRevocationUrl = %CA_CRL_URI%
%CA_CRT_COMMENT%
keyUsage = critical, keyEncipherment, keyAgreement, digitalSignature
extendedKeyUsage = clientAuth, timeStamping
issuerAltName = issuer:copy
subjectAltName = @client_altname
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
authorityInfoAccess = caIssuers;URI:%CA_CRT_URI%
crlDistributionPoints = URI:%CA_CRL_URI%
[ client_altname ]
URI=%CA_CRT_URI%
email=move

27
tpl/index-html.tpl Normal file
View file

@ -0,0 +1,27 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<HTML>
<HEAD><TITLE>%CA_DESC%</TITLE>
<STYLE>
BODY { font-family: arial,sans-serif; }
H1 { font-size: xx-large; margin-left: 50px; }
H3 { font-size: large; margin-top: 50px; margin-left: 50px; }
IMG { border: 0; }
P { width: 700px; margin-left: 100px; }
</STYLE>
</HEAD>
<BODY>
<H1>%CA_DESC%</H1>
<H3>CA Certificate</H3>
<P>The CA certificate can be found
<A href="%CA_CRT_URI%">here</A></P>
<P>MD5 Fingerprint: %CA_CRT_MD5_FP%</P>
<P>SHA1 Fingerprint: %CA_CRT_SHA_FP%</P>
<H3>Certificate Revocation List</H3>
<P>The certificate revocation list can be found
<A href="%CA_CRL_URI%.der">here</A> (DER encoded)
or <A href="%CA_CRL_URI%">here</A> (PEM encoded)</P>
<P>MD5 Fingerprint: %CA_CRL_MD5_FP%</P>
<P>SHA1 Fingerprint: %CA_CRL_SHA_FP%</P>
</BODY>
</HTML>

32
tpl/req-config.tpl Normal file
View file

@ -0,0 +1,32 @@
[ req ]
default_bits = 2048
default_md = sha1
distinguished_name = req_dn
req_extensions = req_%CA_CRT_TYPE%_extensions
string_mask = nombstr
prompt = no
[ req_dn ]
C = %CA_CRT_C%
ST = %CA_CRT_ST%
L = %CA_CRT_L%
O = %CA_CRT_O%
OU = %CA_CRT_OU%
CN = %CA_CRT_CN%
emailAddress = %CA_CRT_E%
[ req_server_extensions ]
basicConstraints = critical, CA:FALSE
keyUsage = critical, keyEncipherment, keyAgreement
extendedKeyUsage = serverAuth
[ req_client_extensions ]
basicConstraints = critical, CA:FALSE
keyUsage = critical, keyEncipherment, keyAgreement, digitalSignature
extendedKeyUsage = clientAuth, timeStamping
[ req_user_extensions ]
basicConstraints = critical, CA:FALSE
keyUsage = critical, keyEncipherment, keyAgreement, digitalSignature, nonRepudiation, dataEncipherment
extendedKeyUsage = clientAuth, codeSigning, emailProtection

19
tpl/server-ext.tpl Normal file
View file

@ -0,0 +1,19 @@
basicConstraints = critical, CA:FALSE
nsCertType = server
nsRevocationUrl = %CA_CRL_URI%
%CA_CRT_COMMENT%
keyUsage = critical, keyEncipherment, keyAgreement
extendedKeyUsage = serverAuth
issuerAltName = issuer:copy
subjectAltName = @server_altname
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
authorityInfoAccess = caIssuers;URI:%CA_CRT_URI%
crlDistributionPoints = URI:%CA_CRL_URI%
[ server_altname ]
URI=%CA_CRT_URI%
email=move
%CA_CRT_ALT_NAMES%

18
tpl/user-ext.tpl Normal file
View file

@ -0,0 +1,18 @@
basicConstraints = critical, CA:FALSE
nsCertType = client, objsign, email
nsRevocationUrl = %CA_CRL_URI%
%CA_CRT_COMMENT%
keyUsage = critical, keyEncipherment, keyAgreement, digitalSignature, nonRepudiation, dataEncipherment
extendedKeyUsage = clientAuth, codeSigning, emailProtection
issuerAltName = issuer:copy
subjectAltName = @user_altname
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
authorityInfoAccess = caIssuers;URI:%CA_CRT_URI%
crlDistributionPoints = URI:%CA_CRL_URI%
[ user_altname ]
URI=%CA_CRT_URI%
email=move

3
vims Normal file
View file

@ -0,0 +1,3 @@
gvim -p bin/* lib/*
gvim -p ca-scripts.conf tpl/*
gvim -p doc/*