ca-scripts/bin/ca-create-cert

183 lines
7.2 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, --req-only Only generate CSR, don't sign it
-s, --sign-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:d:b:n:pqrsx"
long="help,encrypt,config:,type:,days:,bits:,alt-name:"
long="$long,pkcs12,no-qualify,req-only,sign-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|--req-only) CSR_ONLY=1; shift;;
-s|--sign-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