updates on train!

This commit is contained in:
Alex Bramley 2009-10-16 18:37:11 +01:00
parent 348a0df638
commit 07f8c26d5d
4 changed files with 136 additions and 81 deletions

View File

@ -3,7 +3,7 @@
. "/home/alex/code/ca-scripts/lib/ca-functions" . "/home/alex/code/ca-scripts/lib/ca-functions"
ALT_NAMES=() ALT_NAMES=()
TPL_ONLY=0 CNF_ONLY=0
CSR_ONLY=0 CSR_ONLY=0
CRT_ONLY=0 CRT_ONLY=0
MAKE_P12=0 MAKE_P12=0
@ -23,11 +23,12 @@ Options:
-c, --encrypt Encrypt certificate private key with Triple-DES -c, --encrypt Encrypt certificate private key with Triple-DES
-f, --config FILE Use config file instead of $CONFFILE -f, --config FILE Use config file instead of $CONFFILE
-t, --type Certificate type: "server", "client" or "user" -t, --type Certificate type: "server", "client" or "user"
-d, --days DAYS Certificate is valid for DAYS days instead of 365
-n, --alt-name Alternative host name (can be provided multiple times) -n, --alt-name Alternative host name (can be provided multiple times)
-p, --pkcs12 Create PKCS#12 certificate archive from generated cert -p, --pkcs12 Create PKCS#12 certificate archive from generated cert
-r, --csr-only Only generate CSR, don't sign it -r, --csr-only Only generate CSR, don't sign it
-s, --crt-only Only sign certificate, requires CSR in place -s, --crt-only Only sign certificate, requires CSR in place
-x, --tpl-only Only generate templates, do not create CSR or sign CRT -x, --cnf-only Only generate templates, do not create CSR or sign CRT
--country Certificate DN -- C --country Certificate DN -- C
--state Certificate DN -- ST --state Certificate DN -- ST
--loc Certificate DN -- L --loc Certificate DN -- L
@ -40,7 +41,7 @@ __EOT__
} }
short='hcf:t:n:prsx' short='hcf:t:n:prsx'
long='help,encrypt.config:,type:,alt-name:,csr-only,crt-only,tpl-only,pkcs12' long='help,encrypt,config:,type:,alt-name:,pkcs12,csr-only,crt-only,cnf-only'
long="$long,country:,state:,loc:,org:,org-unit:,email:,comment:" long="$long,country:,state:,loc:,org:,org-unit:,email:,comment:"
opts=$( getopt -o "$short" -l "$long" -n "$PROGNAME" -- "$@" ) opts=$( getopt -o "$short" -l "$long" -n "$PROGNAME" -- "$@" )
if [ 0 -ne $? ]; then echo; usage; exit 1; fi if [ 0 -ne $? ]; then echo; usage; exit 1; fi
@ -52,11 +53,12 @@ while :; do
-c|--encrypt) CRYPTKEY=""; shift;; -c|--encrypt) CRYPTKEY=""; shift;;
-f|--config) shift; CONFFILE="$1"; shift;; -f|--config) shift; CONFFILE="$1"; shift;;
-t|--type) shift; CA_CRT_TYPE="$1"; shift;; -t|--type) shift; CA_CRT_TYPE="$1"; shift;;
-d|--days) shift; CA_CRT_DAYS="-days $1"; shift;;
-n|--alt-name) shift; ALT_NAMES+=("$1"); shift;; -n|--alt-name) shift; ALT_NAMES+=("$1"); shift;;
-p|--pkcs12) MAKE_P12=1; shift;; -p|--pkcs12) MAKE_P12=1; shift;;
-r|--csr-only) CSR_ONLY=1; shift;; -r|--csr-only) CSR_ONLY=1; shift;;
-s|--crt-only) CRT_ONLY=1; shift;; -s|--crt-only) CRT_ONLY=1; shift;;
-x|--tpl-only) TPL_ONLY=1; shift;; -x|--cnf-only) CNF_ONLY=1; shift;;
--country) shift; CA_CRT_C="$1"; shift;; --country) shift; CA_CRT_C="$1"; shift;;
--state) shift; CA_CRT_ST="$1"; shift;; --state) shift; CA_CRT_ST="$1"; shift;;
--location) shift; CA_CRT_L="$1"; shift;; --location) shift; CA_CRT_L="$1"; shift;;
@ -111,7 +113,10 @@ if [ "$CA_CRT_TYPE" = "server" ]; then
i=1 i=1
for ALT_NAME in "$CA_CRT_CN" "${ALT_NAMES[@]}"; do for ALT_NAME in "$CA_CRT_CN" "${ALT_NAMES[@]}"; do
# also fully-qualify unqualified alt-names too (see below) # also fully-qualify unqualified alt-names too (see below)
if [ "${ALT_NAME%%.*}" = "$ALT_NAME" ]; then # NOTE: except when it's the previously-fully-qualified CN...
# XXX: maybe we should uniq the alt-names? hmm.
if [ "${ALT_NAME%%.*}" = "$ALT_NAME" \
-a "${CA_CRT_CN%%.*}" != "$ALT_NAME" ]; then
CA_CRT_ALT_NAMES="${CA_CRT_ALT_NAMES}DNS.$i=$ALT_NAME.$CA_DOMAIN\n" CA_CRT_ALT_NAMES="${CA_CRT_ALT_NAMES}DNS.$i=$ALT_NAME.$CA_DOMAIN\n"
i=$(( $i+1 )) i=$(( $i+1 ))
fi fi
@ -121,7 +126,7 @@ if [ "$CA_CRT_TYPE" = "server" ]; then
fi fi
if [ 1 -ne "$CRT_ONLY" ]; then if [ 1 -ne "$CRT_ONLY" ]; then
if [ 1 -eq "$TPL_ONLY" -o "$CSR_ONLY" -eq "$TPL_ONLY" ]; then if [ 1 -eq "$CNF_ONLY" -o "$CSR_ONLY" -eq "$CNF_ONLY" ]; then
# dirty logic here that probably needs commenting! # dirty logic here that probably needs commenting!
# generate a *new* certificate request configuration if... # generate a *new* certificate request configuration if...
# a) --tpl-only is set, i.e. we only want to generate a config # a) --tpl-only is set, i.e. we only want to generate a config
@ -131,7 +136,7 @@ if [ 1 -ne "$CRT_ONLY" ]; then
# we're just generating the config and not the csr itself # we're just generating the config and not the csr itself
ca_template "req-config" "$CA_HOME/cnf/$CNF_NAME.req.cnf" ca_template "req-config" "$CA_HOME/cnf/$CNF_NAME.req.cnf"
fi fi
if [ 1 -ne "$TPL_ONLY" ]; then if [ 1 -ne "$CNF_ONLY" ]; then
if [ ! -f "$CA_HOME/cnf/$CNF_NAME.req.cnf" ]; then if [ ! -f "$CA_HOME/cnf/$CNF_NAME.req.cnf" ]; then
error "Couldn't find CSR config $CA_HOME/cnf/$CNF_NAME.req.cnf!" error "Couldn't find CSR config $CA_HOME/cnf/$CNF_NAME.req.cnf!"
fi fi
@ -144,12 +149,12 @@ if [ 1 -ne "$CRT_ONLY" ]; then
fi fi
fi fi
if [ 1 -ne "$CSR_ONLY" ]; then if [ 1 -ne "$CSR_ONLY" ]; then
if [ 1 -eq "$TPL_ONLY" -o "$CRT_ONLY" -eq "$TPL_ONLY" ]; then if [ 1 -eq "$CNF_ONLY" -o "$CRT_ONLY" -eq "$CNF_ONLY" ]; then
# same logic above applies here, but for generating the extensions # same logic above applies here, but for generating the extensions
# configuration file and signed certificate instead # configuration file and signed certificate instead
ca_template "$CA_CRT_TYPE-ext" "$CA_HOME/cnf/$CNF_NAME.ext.cnf" ca_template "$CA_CRT_TYPE-ext" "$CA_HOME/cnf/$CNF_NAME.ext.cnf"
fi fi
if [ 1 -ne "$TPL_ONLY" ]; then if [ 1 -ne "$CNF_ONLY" ]; then
# ensure relevant files are in place before continuing... # ensure relevant files are in place before continuing...
if [ ! -f "$CA_HOME/csr/$CNF_NAME.csr" ]; then if [ ! -f "$CA_HOME/csr/$CNF_NAME.csr" ]; then
error "CSR not present in $CA_HOME/csr/$CNF_NAME.csr" error "CSR not present in $CA_HOME/csr/$CNF_NAME.csr"
@ -157,7 +162,7 @@ if [ 1 -ne "$CSR_ONLY" ]; then
if [ ! -f "$CA_HOME/cnf/$CNF_NAME.ext.cnf" ]; then if [ ! -f "$CA_HOME/cnf/$CNF_NAME.ext.cnf" ]; then
error "Couldn't find extensions in $CA_HOME/cnf/$CNF_NAME.ext.cnf" error "Couldn't find extensions in $CA_HOME/cnf/$CNF_NAME.ext.cnf"
fi fi
openssl ca -config "$CA_HOME/cnf/$CA_NAME.ca.cnf" \ openssl ca -config "$CA_HOME/cnf/$CA_NAME.ca.cnf" $CA_CRT_DAYS \
-extfile "$CA_HOME/cnf/$CNF_NAME.ext.cnf" -batch \ -extfile "$CA_HOME/cnf/$CNF_NAME.ext.cnf" -batch \
-out "$CA_HOME/crt/$CNF_NAME.crt" \ -out "$CA_HOME/crt/$CNF_NAME.crt" \
-in "$CA_HOME/csr/$CNF_NAME.csr" -in "$CA_HOME/csr/$CNF_NAME.csr"

View File

@ -12,12 +12,14 @@ Options:
-f, --config FILE Use config file instead of $CONFFILE -f, --config FILE Use config file instead of $CONFFILE
-i, --template FILE Use alternative index.html template -i, --template FILE Use alternative index.html template
-o, --output FILE Generate CA index.html in FILE -o, --output FILE Generate CA index.html in FILE
-s, --crt-only Only generate CA cert/key, use pre-created config
-x, --cnf-only Only generate CA config file, don't create CA cert/key
__EOT__ __EOT__
} }
short='hcf:i:o:' short='hcf:i:o:sx'
long='help,encrypt,config:,template:,output:' long='help,encrypt,config:,template:,output:,crt-only,tpl-only'
opts=$( getopt -o "$short" -l "$long" -n "$PROGNAME" -- "$@" ) opts=$( getopt -o "$short" -l "$long" -n "$PROGNAME" -- "$@" )
if [ 0 -ne $? ]; then echo; usage; exit 1; fi if [ 0 -ne $? ]; then echo; usage; exit 1; fi
eval set -- "$opts"; eval set -- "$opts";
@ -29,6 +31,8 @@ while :; do
-f|--config) shift; CONFFILE="$1"; shift;; -f|--config) shift; CONFFILE="$1"; shift;;
-i|--template) shift; INDEXTPL="$1"; shift;; -i|--template) shift; INDEXTPL="$1"; shift;;
-o|--output) shift; INDEXOUT="$1"; shift;; -o|--output) shift; INDEXOUT="$1"; shift;;
-s|--crt-only) CRT_ONLY=1; shift;;
-x|--cnf-only) CNF_ONLY=1; shift;;
--) shift; break;; --) shift; break;;
*) echo "Unknown value '$1'"; exit 1;; *) echo "Unknown value '$1'"; exit 1;;
esac esac
@ -38,35 +42,43 @@ done
CA_CRT_TYPE="ca" CA_CRT_TYPE="ca"
ca_load_conf ca_load_conf
# create the directory structure that'll be populated by the scripts if [ 1 -eq "$CRT_ONLY" -a 1 -eq "$CNF_ONLY" ]; then
mkdir -p $CA_HOME/{cnf,crl,crt,csr,db,idx,key,p12} error "The --crt-only and --cnf-only options are mutually exclusive."
echo "01" > $CA_HOME/db/crlnumber fi
touch $CA_HOME/db/index.txt
touch $CA_HOME/db/.rand if [ 1 -ne "$CRT_ONLY" ]; then
chmod 600 $CA_HOME/db/.rand # create the directory structure that'll be populated by the scripts
chmod 700 $CA_HOME/key mkdir -p $CA_HOME/{cnf,crl,crt,csr,db,idx,key,p12}
echo "01" > $CA_HOME/db/crlnumber
# generate an openssl configuration for this CA touch $CA_HOME/db/index.txt
ca_template ca-config "$CA_HOME/cnf/$CA_NAME.ca.cnf" touch $CA_HOME/db/.rand
chmod -R 640 $CA_HOME
# generate a self-signed cert that is valid for 10 years, with chmod 600 $CA_HOME/db/.rand
# ... the private key in $CA_HOME/key/$CA_NAME.ca.key chmod 700 $CA_HOME/key
# ... the certificate in $CA_HOME/crt/$CA_NAME.ca.crt
# ... using the config in $CA_HOME/cnf/$CA_NAME.ca.cnf # generate an openssl configuration for this CA
openssl req -new $CRYPTKEY -config "$CA_HOME/cnf/$CA_NAME.ca.cnf" \ ca_template ca-config "$CA_HOME/cnf/$CA_NAME.ca.cnf"
-keyout "$CA_HOME/key/$CA_NAME.ca.key" \ fi
-out "$CA_HOME/csr/$CA_NAME.ca.csr" if [ 1 -ne "$CNF_ONLY" ]; then
# generate a self-signed cert that is valid for 10 years, with
openssl ca -create_serial -selfsign -days 3652 -batch \ # ... the private key in $CA_HOME/key/$CA_NAME.ca.key
-name ca_scripts -extensions ca_x509_extensions \ # ... the certificate in $CA_HOME/crt/$CA_NAME.ca.crt
-config "$CA_HOME/cnf/$CA_NAME.ca.cnf" \ # ... using the config in $CA_HOME/cnf/$CA_NAME.ca.cnf
-in "$CA_HOME/csr/$CA_NAME.ca.csr" \ openssl req -new $CRYPTKEY -config "$CA_HOME/cnf/$CA_NAME.ca.cnf" \
-keyfile "$CA_HOME/key/$CA_NAME.ca.key" \ -keyout "$CA_HOME/key/$CA_NAME.ca.key" \
-out "$CA_HOME/crt/$CA_NAME.ca.crt" -out "$CA_HOME/csr/$CA_NAME.ca.csr"
# generate an initial CRL too (yes it will be empty, but we should serve it) openssl ca -create_serial -selfsign -days 3652 -batch \
ca_gen_crl -name ca_scripts -extensions ca_x509_extensions \
if [ -n "$INDEXOUT" ]; then -config "$CA_HOME/cnf/$CA_NAME.ca.cnf" \
ca_checksum -in "$CA_HOME/csr/$CA_NAME.ca.csr" \
ca_template $INDEXTPL $INDEXOUT -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
fi fi

View File

@ -1,55 +1,89 @@
1. Creating a Certificate Authority. 1. Creating a Certificate Authority.
Before running ca-init(1), a configuration file for the CA scripts must be
created. This configuration file sets up some templating variables that will
be present in certificates created for this CA, such as the domain, CA name,
and the root directory which will be populated with the generated certificates.
An example configuration file is provided with the scripts, and the comments
should be self-explanatory.
By default the CA scripts will read /etc/ca-scripts.conf. This is fine for
creating a single CA serving a single domain with no intermediary certificates,
but for a more complex setup a directory of configuration files will probably
be needed. Some settings are required, namely the CA_HOME, CA_DOMAIN, and
CA_DN_* variables, while others can be inferred from these or have sensible
defaults set. See ca-scripts.conf(5) for more detail on these.
Once the configuration has been created the initial CA setup can be performed
with ca-init(1), but please note that the path set in CA_HOME must exist and be
writeable before it will run correctly. It is recommended (but not in any way
required) to create an unprivileged "ssl" user to run all the scripts as, so
the permissions are correctly set. A number of subdirectories will be set
up underneath this root, and an openssl configuration file, certificate and
private key will be generated. This key can be 3DES encrypted for security.
To fully understand it's contents you're unfortunately going to need to read ca(1ssl), Optionally, it is possible to split the initial setup process so that the
req(1ssl), x509(1ssl), config(5ssl), and x509v3_config(5ssl). Particularly directory structure and openssl configuration generation can be done in a
important are the x509v3 extensions present in the certificate, which are seperate step to the generation of the CA certificates, so that the config can
defined in the "stglab_x509_ca_extensions" section of the config file. be manually edited. To fully understand it's contents you're unfortunately
going to need to read ca(1ssl), req(1ssl), x509(1ssl), config(5ssl), and
The ca-cert script configures some important files in db/, then creates a x509v3_config(5ssl). Particularly important are the x509v3 extensions present
certificate request and signs it. It also generates an initial (empty) in the certificate, which are defined in the "ca_x509_extensions" section of
revocation list, then substitutes the correct fingerprints into the html the config file.
template for serving the CA certificate and CRL to the intranet.
2. Creating a certificate. 2. Creating a certificate.
The create-cert script can generate three "types" of certificate -- server The ca-create-cert(1) script can generate three "types" of certificate:
certificates for securing a service with SSL/TLS, client certificates for server certificates for securing a service with SSL/TLS; client certificates
authenticating a client to these services, and user certificates for for authenticating a client to these services; and user certificates for
authentication, S/MIME e-mail signing or encryption, and code signing. There authentication, S/MIME e-mail signing or encryption, and code signing. There
are minor but important differences in the extensions present in these are minor but important differences in the key usage extensions present in
different certificate types, but these are set in the *-ext.tpl files in tpl/ these different certificate types, details can be found in the extension
and thus you shouldn't need to worry about them. templates provided with the scripts.
The create-cert script takes a number of arguments, of which the hostname or ca-create-cert(1) takes a number of options to customise the generated
username and the type are mandatory. It is also a very good idea to supply a certificate. The --type option is mandatory, and for server certs it is very
number of alternative DNS names when generating a server certificate, because likely that the --alt-name option will be useful to set x509v3 SubjectAltName
while the script will happily append "stglab.manchester.uk.ibm.com" to an DNS records for other hostnames for the server. Both the server hostname and
un-qualified host name, it won't append "transitives.com" and for the moment we any alternative names will be fully-qualified to CA_DOMAIN if they do not
probably need that. contain any dots, but if unqualified names are passed in they are also
preserved as alternative DNS names in the certificate. The private key may be
encrypted with 3DES, and optionally the certificate, key, and CA certificate
can be bundled together into a PKCS#12 format certificate archive. By default
certificates are valid for 365 days from signing, but this may be changed with
the --days option.
You should also provide a team name for the organisational unit, e.g. The certificate's DN can be completely changed from the defaults provided by
"Manchester STG Lab Test", an e-mail address (preferably for the team rather ca-scripts.conf(5), but be wary as by default the generated openssl config file
than an individual for server/client certificates), and a comment that reflects requires that the country (C) and organisation (O) fields match those of the CA
the usage of the certificate, e.g. "Soak Infrastructure Live Server". Reasonable certificate. A comment may also be set that will show up in user browsers when
defaults are provided for all of these for our team's use. they click on their padlock icons to examine the certificate's properties. As
with the CA setup, the steps to generate the certificate can be split up so
that configurations that are created from templates can be edited beforehand.
3. Renewing a certificate. 3. Renewing a certificate.
The renew-cert script does some painful certificate manipulation that is not Certificates are renewed using ca-renew-cert(1). This script currently does
strictly necessary in most cases, and may in fact decrease SSL security some painful certificate manipulation that is not strictly necessary in most
slightly. This is done because the normal renewal process re-generates the cases, and may in fact decrease SSL security slightly. This is done because
certificate signing request and thus creates a new public/private keypair. the normal renewal process re-generates the certificate signing request and
If the certificates are used for S/MIME encryption or code signing, this thus creates a new public/private keypair. If the certificates are used for
renders all the encrypted e-mail unreadable and requires you to re-sign the S/MIME encryption or code signing, this renders all the encrypted e-mail
code with your new private key. The code in renew-cert re-signs the old unreadable and requires you to re-sign the code with your new private key.
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 To avoid this, ca-renew-cert(1) re-signs the old certificate request with a
the certificate to renew-cert.sh. a new expiry date using the extensions generated when the old certificate was
signed. In the future it is possible (even likely) that this renewal method
will only be used on "user" type certificates, and the "server" and "client"
types will be renewed normally. If the current renewal method doesn't provide
sufficient security, the current certificate should be revoked and a new one
generated that is valid for the correct period of time using the --days option
to ca-create-cert(1).
As with the certificate creation script the --type option is mandatory for
ca-renew-cert(1), but the argument may be either a hostname, a username or a
path to a certificate. Internally this will be resolved to the correct
information required for certificate renewal.
4. Revoking a certificate. 4. Revoking a certificate.

View File

@ -149,7 +149,11 @@ ca_cnf_name() {
crt="$1" crt="$1"
# work out what configuration files we should be using from the cert's CN # 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 \ echo $( openssl x509 -in "$crt" -noout -nameopt sep_multiline,use_quote \
-subject | grep "CN=" | cut -d= -f2 | tr -c '[:alnum:]@-' _ ) -subject | awk -F= '/CN=/{ gsub("[^A-Za-z0-9@-]", "_", $2); print $2}' )
# grep "CN=" | cut -d= -f2 | tr -c '[:alnum:]@-' _ )
# originally did this like the above but tried awk instead. awk(1) seems
# to lie about it's egrep(1) support though as no matter what I tried the
# tr(1) regex didn't work in the gsub() call above.
} }
ca_find_cnf() { ca_find_cnf() {