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"
ALT_NAMES=()
TPL_ONLY=0
CNF_ONLY=0
CSR_ONLY=0
CRT_ONLY=0
MAKE_P12=0
@ -23,11 +23,12 @@ Options:
-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"
-d, --days DAYS Certificate is valid for DAYS days instead of 365
-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
-x, --cnf-only Only generate templates, do not create CSR or sign CRT
--country Certificate DN -- C
--state Certificate DN -- ST
--loc Certificate DN -- L
@ -40,7 +41,7 @@ __EOT__
}
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:"
opts=$( getopt -o "$short" -l "$long" -n "$PROGNAME" -- "$@" )
if [ 0 -ne $? ]; then echo; usage; exit 1; fi
@ -52,11 +53,12 @@ while :; do
-c|--encrypt) CRYPTKEY=""; shift;;
-f|--config) shift; CONFFILE="$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;;
-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;;
-x|--cnf-only) CNF_ONLY=1; shift;;
--country) shift; CA_CRT_C="$1"; shift;;
--state) shift; CA_CRT_ST="$1"; shift;;
--location) shift; CA_CRT_L="$1"; shift;;
@ -111,7 +113,10 @@ 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
# 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"
i=$(( $i+1 ))
fi
@ -121,7 +126,7 @@ if [ "$CA_CRT_TYPE" = "server" ]; then
fi
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!
# generate a *new* certificate request configuration if...
# 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
ca_template "req-config" "$CA_HOME/cnf/$CNF_NAME.req.cnf"
fi
if [ 1 -ne "$TPL_ONLY" ]; then
if [ 1 -ne "$CNF_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
@ -144,12 +149,12 @@ if [ 1 -ne "$CRT_ONLY" ]; then
fi
fi
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
# 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
if [ 1 -ne "$CNF_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"
@ -157,7 +162,7 @@ if [ 1 -ne "$CSR_ONLY" ]; then
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" \
openssl ca -config "$CA_HOME/cnf/$CA_NAME.ca.cnf" $CA_CRT_DAYS \
-extfile "$CA_HOME/cnf/$CNF_NAME.ext.cnf" -batch \
-out "$CA_HOME/crt/$CNF_NAME.crt" \
-in "$CA_HOME/csr/$CNF_NAME.csr"

View File

@ -12,12 +12,14 @@ Options:
-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
-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__
}
short='hcf:i:o:'
long='help,encrypt,config:,template:,output:'
short='hcf:i:o:sx'
long='help,encrypt,config:,template:,output:,crt-only,tpl-only'
opts=$( getopt -o "$short" -l "$long" -n "$PROGNAME" -- "$@" )
if [ 0 -ne $? ]; then echo; usage; exit 1; fi
eval set -- "$opts";
@ -29,6 +31,8 @@ while :; do
-f|--config) shift; CONFFILE="$1"; shift;;
-i|--template) shift; INDEXTPL="$1"; shift;;
-o|--output) shift; INDEXOUT="$1"; shift;;
-s|--crt-only) CRT_ONLY=1; shift;;
-x|--cnf-only) CNF_ONLY=1; shift;;
--) shift; break;;
*) echo "Unknown value '$1'"; exit 1;;
esac
@ -38,35 +42,43 @@ done
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
if [ 1 -eq "$CRT_ONLY" -a 1 -eq "$CNF_ONLY" ]; then
error "The --crt-only and --cnf-only options are mutually exclusive."
fi
if [ 1 -ne "$CRT_ONLY" ]; then
# 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 -R 640 $CA_HOME
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"
fi
if [ 1 -ne "$CNF_ONLY" ]; then
# 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
fi

View File

@ -1,55 +1,89 @@
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),
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.
Optionally, it is possible to split the initial setup process so that the
directory structure and openssl configuration generation can be done in a
seperate step to the generation of the CA certificates, so that the config can
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
x509v3_config(5ssl). Particularly important are the x509v3 extensions present
in the certificate, which are defined in the "ca_x509_extensions" section of
the config file.
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
The ca-create-cert(1) 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.
are minor but important differences in the key usage extensions present in
these different certificate types, details can be found in the extension
templates provided with the scripts.
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.
ca-create-cert(1) takes a number of options to customise the generated
certificate. The --type option is mandatory, and for server certs it is very
likely that the --alt-name option will be useful to set x509v3 SubjectAltName
DNS records for other hostnames for the server. Both the server hostname and
any alternative names will be fully-qualified to CA_DOMAIN if they do not
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.
"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.
The certificate's DN can be completely changed from the defaults provided by
ca-scripts.conf(5), but be wary as by default the generated openssl config file
requires that the country (C) and organisation (O) fields match those of the CA
certificate. A comment may also be set that will show up in user browsers when
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.
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.
Certificates are renewed using ca-renew-cert(1). This script currently 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.
Renewing a certificate is done by giving the hostname, username or path to
the certificate to renew-cert.sh.
To avoid this, ca-renew-cert(1) re-signs the old certificate request with a
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.

View File

@ -149,7 +149,11 @@ ca_cnf_name() {
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:]@-' _ )
-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() {