cd547f66c0
This also fixes a fairly major bug that could have caused config file options to override command-line ones, with no-doubt confusing consequences ;-)
181 lines
7.1 KiB
Bash
Executable file
181 lines
7.1 KiB
Bash
Executable file
#! /bin/bash
|
|
|
|
. "/home/alex/code/ca-scripts/lib/ca-functions"
|
|
|
|
ALT_NAMES=()
|
|
QUALIFY=1
|
|
CNF_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 [options] <common name>
|
|
|
|
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 TYPE Certificate type: "server" (default), "client" or "user"
|
|
-d, --days DAYS Certificate valid for DAYS days instead of CA_CRT_DAYS
|
|
-b, --bits BITS Generate a BITS bit certificate instead of CA_CRT_BITS
|
|
-n, --alt-name NAME Alternative host name (can be provided multiple times)
|
|
-p, --pkcs12 Create PKCS#12 certificate archive from generated cert
|
|
-q, --no-qualify Don't qualify short (dotless) names with CA_DOMAIN
|
|
-r, --csr-only Only generate CSR, don't sign it
|
|
-s, --crt-only Only sign certificate, requires CSR in place
|
|
-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
|
|
--org Certificate DN -- O
|
|
--ounit Certificate DN -- OU
|
|
--email Certificate DN -- E
|
|
--comment Certificate nsComment field
|
|
|
|
__EOT__
|
|
}
|
|
|
|
short='hcf:t:n:prsx'
|
|
long='help,encrypt,config:,type:,alt-name:,pkcs12,csr-only,crt-only,cnf-only'
|
|
long="$long,country:,state:,loc:,org:,ounit:,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; USER_CA_CRT_TYPE="$1"; shift;;
|
|
-d|--days) shift; USER_CA_CRT_DAYS="$1"; shift;;
|
|
-b|--bits) shift; USER_CA_CRT_BITS="$1"; shift;;
|
|
-n|--alt-name) shift; ALT_NAMES+=("$1"); shift;;
|
|
-p|--pkcs12) MAKE_P12=1; shift;;
|
|
-q|--no-qualify) QUALIFY=0; shift;;
|
|
-r|--csr-only) CSR_ONLY=1; shift;;
|
|
-s|--crt-only) CRT_ONLY=1; shift;;
|
|
-x|--cnf-only) CNF_ONLY=1; shift;;
|
|
--country) shift; USER_CA_CRT_C="$1"; shift;;
|
|
--state) shift; USER_CA_CRT_ST="$1"; shift;;
|
|
--location) shift; USER_CA_CRT_L="$1"; shift;;
|
|
--org) shift; USER_CA_CRT_O="$1"; shift;;
|
|
--ounit) shift; USER_CA_CRT_OU="$1"; shift;;
|
|
--email) shift; USER_CA_CRT_E="$1"; shift;;
|
|
--comment) shift; USER_CA_CRT_COMMENT="$1"; shift;;
|
|
--) shift; break;;
|
|
*) echo "Unknown value '$1'"; exit 1;;
|
|
esac
|
|
done
|
|
|
|
# load up the configuration file
|
|
ca_load_conf
|
|
|
|
# This must be provided on the command line. There's no point setting a
|
|
# "default" certificate CN in the config, it should be different every time.
|
|
CA_CRT_CN="$1"
|
|
|
|
# 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 and -q not set
|
|
if [ 1 -eq "$QUALIFY" -a "${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 [ 1 -eq "$QUALIFY" -a "${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 or client certificates
|
|
if [ "$CA_CRT_TYPE" != "user" ]; then
|
|
i=1
|
|
for ALT_NAME in "$CA_CRT_CN" "${ALT_NAMES[@]}"; do
|
|
# also fully-qualify unqualified alt-names too (see below)
|
|
# NOTE: except when it's the previously-fully-qualified CN...
|
|
# XXX: maybe we should uniq the alt-names? hmm.
|
|
if [ 1 -eq "$QUALIFY" -a "${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
|
|
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 "$CNF_ONLY" -o "$CSR_ONLY" -eq "$CNF_ONLY" ]; then
|
|
# dirty logic here that probably needs commenting!
|
|
# generate a *new* certificate request configuration if...
|
|
# a) --cnf-only is set, i.e. we only want to generate a config
|
|
# b) both --cnf-only and --csr-only are unset, i.e.
|
|
# we're just generating a csr/crt as per usual
|
|
# c) both --cnf-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 "$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
|
|
# the above logic means that if you pass --csr-only but not
|
|
# --cnf-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 "$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 "$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"
|
|
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" \
|
|
-days "$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"
|
|
fi
|
|
fi
|
|
|
|
if [ 1 -eq "$MAKE_P12" ]; then
|
|
ca_gen_p12 "$CNF_NAME"
|
|
fi
|