#!/bin/bash source $(dirname $(readlink -f $0))/../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] 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"; CONFFILECLI=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