updates on train!
This commit is contained in:
parent
348a0df638
commit
07f8c26d5d
4 changed files with 136 additions and 81 deletions
|
@ -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"
|
||||
|
|
78
bin/ca-init
78
bin/ca-init
|
@ -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
|
||||
|
|
108
doc/README
108
doc/README
|
@ -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.
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in a new issue