#!/bin/bash #------ Settings ------ OPENSSL="openssl" CONFIG="./openssl.cnf" ROOTCADAYS="-days 3650" # 10 years CADAYS="-days 1825" # 5 years #CADAYS="-days 825" # 2 years DAYS="-days 397" # 1 year CONFIGOPTION="-config ${CONFIG}" REQ="${OPENSSL} req ${CONFIGOPTION}" CA="${OPENSSL} ca ${CONFIGOPTION}" VERIFY="${OPENSSL} verify" X509="${OPENSSL} x509" PKCS12="${OPENSSL} pkcs12" CAFOLDER="./CA" CAKEY="./cakey.pem" CAREQ="./careq.pem" CACERT="./cacert.pem" PASSEXTENSION="keyfile" PASSFILE="./${PASSEXTENSION}" TYPEFILE="./type" CHAINFILE="./cachain.pem" C="NL" ST="Noord-Brabant" L="Veldhoven" O="Dierkse DataManagement" OU="Secure Digital Certificate Signing" #CN="CommonName" E="certification@dierkse.nl" #------ Functions ------ execute() { COMMAND=$@ eval ${COMMAND} RETURNVALUE=$? if [ "${RETURNVALUE}" -ne "0" ]; then exit ${RETURNVALUE} fi } writeRandomString() { SIZE=$1 FILE=$2 tr -dc 'A-Za-z0-9!"#$%&'\''()*+,-./:;<=>?@[\]^_`{|}~' < /dev/urandom | head -c ${SIZE} > "${FILE}" } validateType() { TYPE=$1 VALIDTYPE="" if [[ "${TYPE}" == "ROOTCA" || "${TYPE}" == "INTERMEDIATECA" || "${TYPE}" == "VPNCA" || "${TYPE}" == "WEBSERVER" || "${TYPE}" == "WEBCLIENT" || "${TYPE}" == "APPLICATIONCLIENT" || "${TYPE}" == "EMAIL" || "${TYPE}" == "VPN" ]]; then VALIDTYPE=${TYPE} fi } copyPem() { INFILE=$1 OUTFILE=$2 BOUND=$3 FLAG=0 if [ ! -f "${INFILE}" ]; then echo "Missing File ${INFILE}" exit -1 fi if [[ -z "$BOUND" ]]; then echo "Missing Boundary" exit -1 fi exec < "${INFILE}"; while read LINE; do if [ ${FLAG} -eq 1 ]; then echo ${LINE}|grep "^-----END.*${BOUND}" 2>/dev/null 1>/dev/null if [ $? -eq 0 ] ; then echo ${LINE} >> "${OUTFILE}" break else echo ${LINE} >> "${OUTFILE}" fi fi echo ${LINE} | grep "^-----BEGIN.*${BOUND}" 2>/dev/null 1>/dev/null if [ $? -eq 0 ]; then echo ${LINE} > "${OUTFILE}" FLAG=1 fi done } newCertificate() { KEYBITS=2048 if [ ! -d ${CAFOLDER} ]; then echo "Missing CA Folder" exit -1 fi if [[ "${TYPE}" == "WEBSERVER" ]]; then EXTENSIONS="server_cert -nodes" SAN=() if [ ! -z "${CN}" ]; then SAN+=("${CN}") SAN+=("*.${CN}") fi SAN+=("${EXTRASAN[@]}") IP=("${EXTRAIP[@]}") fi if [[ "${TYPE}" == "WEBCLIENT" ]]; then EXTENSIONS="client_cert -nodes" fi if [[ "${TYPE}" == "APPLICATIONCLIENT" ]]; then EXTENSIONS="application_cert -nodes" fi if [[ "${TYPE}" == "EMAIL" ]]; then EXTENSIONS="email_cert -nodes" fi if [[ "${TYPE}" == "VPN" ]]; then EXTENSIONS="vpn_cert -nodes" SAN=() if [ ! -z "${CN}" ]; then SAN+=("${CN}") fi fi if [[ "${TYPE}" == "INTERMEDIATECA" ]]; then KEYBITS=4096 DAYS=${CADAYS} EXTENSIONS="intermediate_extensions" fi COMMAND="${REQ} -new -x509 -newkey rsa:${KEYBITS} -extensions server_cert -keyout \"${FILENAME}.key\" -out \"${FILENAME}.crt\" ${DAYS}" if [ ! -z "${CN}" ]; then SUBJECT="/C=${C}/ST=${ST}/L=${L}/O=${O}/OU=${OU}/CN=${CN}/emailAddress=${E}" COMMAND+=" -subj \"${SUBJECT}\"" fi if [[ "${TYPE}" == "WEBSERVER" || "${TYPE}" == "VPN" || ${#SAN[@]} -ne "0" || ${#IP[@]} -ne "0" ]]; then COMMAND+=" -addext \"subjectAltName=" INDEX=0 for S in "${SAN[@]}" do ((INDEX+=1)) COMMAND+="DNS.${INDEX}: ${S}," done INDEX=0 for I in "${IP[@]}" do ((INDEX+=1)) COMMAND+="IP.${INDEX}: ${I}," done COMMAND=${COMMAND%?} COMMAND+="\"" fi execute ${COMMAND} } newRequest() { KEYBITS=2048 if [ ! -d ${CAFOLDER} ]; then echo "Missing CA Folder" exit -1 fi if [[ "${TYPE}" == "WEBSERVER" ]]; then EXTENSIONS="server_cert -nodes" SAN=() if [ ! -z "${CN}" ]; then SAN+=("${CN}") SAN+=("*.${CN}") fi SAN+=("${EXTRASAN[@]}") IP=("${EXTRAIP[@]}") fi if [[ "${TYPE}" == "WEBCLIENT" ]]; then EXTENSIONS="client_cert -nodes" fi if [[ "${TYPE}" == "APPLICATIONCLIENT" ]]; then EXTENSIONS="application_cert -nodes" fi if [[ "${TYPE}" == "EMAIL" ]]; then EXTENSIONS="email_cert -nodes" fi if [[ "${TYPE}" == "VPN" ]]; then EXTENSIONS="vpn_cert -nodes" SAN=() if [ ! -z "${CN}" ]; then SAN+=("${CN}") fi fi if [[ "${TYPE}" == "INTERMEDIATECA" ]]; then KEYBITS=4096 writeRandomString 128 "${FILENAME}.${PASSEXTENSION}" chmod 000 "${FILENAME}.${PASSEXTENSION}" EXTENSIONS="intermediate_extensions -passout file:\"${FILENAME}.${PASSEXTENSION}\"" fi COMMAND="${REQ} -new -newkey rsa:${KEYBITS} -extensions ${EXTENSIONS} -keyout \"${FILENAME}.key\" -out \"${FILENAME}.csr\"" if [ ! -z "${CN}" ]; then SUBJECT="/C=${C}/ST=${ST}/L=${L}/O=${O}/OU=${OU}/CN=${CN}/emailAddress=${E}" COMMAND+=" -subj \"${SUBJECT}\"" fi if [[ "${TYPE}" == "WEBSERVER" || "${TYPE}" == "VPN" || ${#SAN[@]} -ne "0" || ${#IP[@]} -ne "0" ]]; then COMMAND+=" -addext \"subjectAltName=" INDEX=0 for S in "${SAN[@]}" do ((INDEX+=1)) COMMAND+="DNS.${INDEX}: ${S}," done INDEX=0 for I in "${IP[@]}" do ((INDEX+=1)) COMMAND+="IP.${INDEX}: ${I}," done COMMAND=${COMMAND%?} COMMAND+="\"" fi execute ${COMMAND} } newCertificateAuthority() { if [[ "${TYPE}" == "ROOTCA" ]]; then KEYBITS=8192 EXTENSIONS="CA_root" DAYS=${ROOTCADAYS} SUBTYPE="INTERMEDIATECA" fi if [[ "${TYPE}" == "INTERMEDIATECA" ]]; then KEYBITS=4096 EXTENSIONS="CA_intermediate" DAYS=${CADAYS} fi if [[ "${TYPE}" == "VPNCA" ]]; then KEYBITS=4096 EXTENSIONS="CA_root" DAYS=${ROOTCADAYS} SUBTYPE="VPN" fi if [ -z "${KEYBITS}" ]; then echo "Error: invalid certificate authority type" >&2 exit -1 fi if [ ! -f ${CAFOLDER}/serial ]; then mkdir -p ${CAFOLDER} mkdir -p ${CAFOLDER}/certs mkdir -p ${CAFOLDER}/crl mkdir -p ${CAFOLDER}/newcerts mkdir -p ${CAFOLDER}/private touch ${CAFOLDER}/index.txt echo 1000 > ${CAFOLDER}/crl/crlnumber openssl rand -writerand ${CAFOLDER}/private/.rand writeRandomString 128 ${CAFOLDER}/private/${PASSFILE} chmod 000 ${CAFOLDER}/private/${PASSFILE} if [ ! -z "${SUBTYPE}" ]; then echo "${SUBTYPE}" > ${CAFOLDER}/${TYPEFILE} fi fi if [ ! -f ${CAFOLDER}/private/${CAKEY} ]; then if [[ "${TYPE}" == "INTERMEDIATECA" ]]; then echo "CA certificate filename (or enter to create)" read NAME fi if [ "${NAME}" ]; then if [ -f "./${NAME}" ]; then NAME="./${NAME%.*}" else if [ -f "../${NAME}" ]; then NAME="../${NAME%.*}" else if [ -f "./${NAME}.crt" ]; then NAME="./${NAME}" else if [ -f "../${NAME}.crt" ]; then NAME="../${NAME}" else echo "Certificate not found" echo " ("./${NAME}")" echo " ("../${NAME}")" echo " ("./${NAME}.crt")" echo " ("../${NAME}.crt")" echo "" exit -1 fi fi fi fi if [ -f "${NAME}.csr" ]; then copyPem "${NAME}.csr" ${CAFOLDER}/${CAREQ} CERTIFICATE else echo "CA Request missing (${NAME}.csr)" exit -1 fi if [ -f "${NAME}.key" ]; then copyPem "${NAME}.key" ${CAFOLDER}/private/${CAKEY} PRIVATE else echo "CA Private Key missing (${NAME}.key)" exit -1 fi if [ -f "${NAME}.${PASSEXTENSION}" ]; then cp "${NAME}.${PASSEXTENSION}" ${CAFOLDER}/private/${PASSFILE} chmod 000 ${CAFOLDER}/private/${PASSFILE} else echo "CA Private Key Keyfile missing (${NAME}.${PASSEXTENSION})" fi if [ -f "${NAME}.crt" ]; then copyPem "${NAME}.crt" ${CAFOLDER}/${CACERT} CERTIFICATE else echo "CA Certificate missing (${NAME}.crt)" exit -1 fi if [ -f "${NAME}.pem" ]; then COMMAND="cp \"${NAME}.pem\" ${CAFOLDER}/${CHAINFILE}" execute ${COMMAND} else echo "CA Chain missing (${NAME}.pem)" exit -1 fi if [ ! -f "${CAFOLDER}/serial" ]; then COMMAND="$X509 -in ${CAFOLDER}/${CACERT} -noout -next_serial -out ${CAFOLDER}/serial" execute ${COMMAND} fi else COMMAND="$REQ -new -newkey rsa:${KEYBITS} -passout file:${CAFOLDER}/private/${PASSFILE} -keyout ${CAFOLDER}/private/${CAKEY} -out ${CAFOLDER}/${CAREQ}" if [ ! -z "${CN}" ]; then SUBJECT="/C=${C}/ST=${ST}/L=${L}/O=${O}/OU=${OU}/CN=${CN}/emailAddress=${E}" COMMAND+=" -subj \"${SUBJECT}\"" fi execute ${COMMAND} COMMAND="$CA -create_serial -out ${CAFOLDER}/${CACERT} ${DAYS} -batch -keyfile ${CAFOLDER}/private/${CAKEY} -passin file:${CAFOLDER}/private/${PASSFILE} -selfsign -name ${EXTENSIONS} -infiles ${CAFOLDER}/${CAREQ}" execute ${COMMAND} copyPem ${CAFOLDER}/${CACERT} ${CAFOLDER}/${CACERT}.tmp CERTIFICATE COMMAND="mv ${CAFOLDER}/${CACERT}.tmp ${CAFOLDER}/${CACERT}" execute ${COMMAND} COMMAND="cp ${CAFOLDER}/${CACERT} ${CAFOLDER}/${CHAINFILE}" execute ${COMMAND} fi fi generateCrl } renewCertificate() { revokeCertificate signRequest } renewCertificateAuthorityCertificate() { # TODO if [ ! -d ${CAFOLDER} ]; then echo "Missing CA Folder" exit -1 fi echo "Not implemented" } signCertificate() { if [ ! -d ${CAFOLDER} ]; then echo "Missing CA Folder" exit -1 fi if [ ! -f "${FILENAME}.csr" ]; then echo "Missing Certificate Request" exit -1 fi COMMAND="${X509} -x509toreq -signkey \"${FILENAME}.key\" -in \"${FILENAME}.csr\" -out temporary.pem" execute ${COMMAND} COMMAND="${CA} -policy policy_anything -passin file:${CAFOLDER}/private/${PASSFILE} ${DAYS} -out \"${FILENAME}.crt\" -infiles temporary.pem" execute ${COMMAND} copyPem "${FILENAME}.crt" "${FILENAME}.crt.tmp" CERTIFICATE COMMAND="mv \"${FILENAME}.crt.tmp\" \"${FILENAME}.crt\"" execute ${COMMAND} COMMAND="cat \"${FILENAME}.crt\" ${CAFOLDER}/${CHAINFILE} > \"${FILENAME}.pem\"" execute ${COMMAND} } signRequest() { if [ ! -d ${CAFOLDER} ]; then echo "Missing CA Folder" exit -1 fi if [ ! -f "${FILENAME}.csr" ]; then echo "Missing Certificate Request" exit -1 fi if [[ "${TYPE}" == "WEBSERVER" ]]; then EXTENSIONS="server_cert" fi if [[ "${TYPE}" == "WEBCLIENT" ]]; then EXTENSIONS="client_cert" fi if [[ "${TYPE}" == "APPLICATIONCLIENT" ]]; then EXTENSIONS="application_cert" fi if [[ "${TYPE}" == "EMAIL" ]]; then EXTENSIONS="email_cert" fi if [[ "${TYPE}" == "VPN" ]]; then EXTENSIONS="vpn_cert" fi if [[ "${TYPE}" == "INTERMEDIATECA" ]]; then DAYS=${CADAYS} EXTENSIONS="intermediate_extensions" fi COMMAND="${CA} -policy policy_match -passin file:${CAFOLDER}/private/${PASSFILE} -name CA_intermediate -extensions ${EXTENSIONS} ${DAYS} -out \"${FILENAME}.crt\" -infiles \"${FILENAME}.csr\"" execute ${COMMAND} copyPem "${FILENAME}.crt" "${FILENAME}.crt.tmp" CERTIFICATE COMMAND="mv \"${FILENAME}.crt.tmp\" \"${FILENAME}.crt\"" execute ${COMMAND} COMMAND="cat \"${FILENAME}.crt\" ${CAFOLDER}/${CHAINFILE} > \"${FILENAME}.pem\"" execute ${COMMAND} } signCertificateAuthorityCertificate() { # TODO if [ ! -d ${CAFOLDER} ]; then echo "Missing CA Folder" exit -1 fi # $CA -policy policy_anything -out newcert.pem -extensions v3_ca -infiles newreq.pem # RET=$? echo "Not implemented" } signRequestAnything() { # TODO if [ ! -d ${CAFOLDER} ]; then echo "Missing CA Folder" exit -1 fi # $CA -policy policy_anything -infiles newreq.pem # RET=$? echo "Not implemented" } createCertificatePackage() { if [ ! -d ${CAFOLDER} ]; then echo "Missing CA Folder" exit -1 fi if [ ! -f "${FILENAME}.crt" ]; then echo "Missing Certificate" exit -1 fi if [ ! -f "${FILENAME}.key" ]; then echo "Missing Private Key" exit -1 fi CACERTCN=$(${X509} -noout -subject -nameopt multiline -in ${CAFOLDER}/${CACERT} | sed -n 's/ *commonName *= //p') COMMAND="${PKCS12} -in \"${FILENAME}.crt\" -inkey \"${FILENAME}.key\" -certfile ${CAFOLDER}/${CHAINFILE} -caname \"${CACERTCN}\" -out \"${FILENAME}.p12\" -export -name \"${FILENAME}\"" execute ${COMMAND} } generateCrl() { if [ ! -d ${CAFOLDER} ]; then echo "Missing CA Folder" exit -1 fi COMMAND="${CA} -passin file:${CAFOLDER}/private/${PASSFILE} -gencrl -out ${CAFOLDER}/crl/crl.pem" execute ${COMMAND} COMMAND="cat ${CAFOLDER}/${CHAINFILE} ${CAFOLDER}/crl/crl.pem > ${CAFOLDER}/crl/crl_chain.pem" execute ${COMMAND} } revokeCertificate() { if [ ! -d ${CAFOLDER} ]; then echo "Missing CA Folder" exit -1 fi if [ ! -f "${FILENAME}.crt" ]; then echo "Missing Certificate" exit -1 fi COMMAND="${CA} -passin file:${CAFOLDER}/private/${PASSFILE} -revoke \"${FILENAME}.crt\"" execute ${COMMAND} COMMAND="mv \"${FILENAME}.crt\" \"${FILENAME}.crt.revoked\"" execute ${COMMAND} generateCrl } verifyCertificate() { if [ ! -d ${CAFOLDER} ]; then echo "Missing CA Folder" exit -1 fi if [ ! -f "${FILENAME}.crt" ]; then echo "Missing Certificate" exit -1 fi COMMAND="${VERIFY} -CAfile ${CAFOLDER}/${CHAINFILE} \"${FILENAME}.crt\"" execute ${COMMAND} COMMAND="${VERIFY} -crl_check -CAfile ${CAFOLDER}/crl/crl_chain.pem \"${FILENAME}.crt\"" execute ${COMMAND} } printHelp() { echo "" >&2 echo "usage: $0 -t [--san ] [--ip ] [COMMON NAME]" >&2 echo "" >&2 echo " Certificate Types:" >&2 echo " ROOTCA " >&2 echo " INTERMEDIATECA " >&2 echo " VPNCA " >&2 echo " WEBSERVER " >&2 echo " WEBCLIENT " >&2 echo " APPLICATIONCLIENT " >&2 echo " EMAIL " >&2 echo " VPN " >&2 echo "" >&2 echo " Operations:" >&2 echo " -c|--newcert Create a new certificate and private key" >&2 echo " -n|--newreq Create a new certificate request and private key" >&2 echo " --newca Create a new certificate authority" >&2 echo " -r|--renewcert Renew an existing certificate (requires a certificate request)" >&2 #echo " --renewca Renew the certificate authority certificate" >&2 echo " --signcert Sign a certificate with the certificate authority" >&2 echo " -s|--signreq Create and sign a new certificate with the certificate autority" >&2 #echo " --signca Sign the certificate authority certificate" >&2 #echo " --xsign Sign the certificate request with the certificate authority using the 'policy_anything' option" >&2 echo " -p|--pkcs12 Create pkcs12 package containing the certificate and its private key" >&2 echo " --revoke Revoke a certificate" >&2 echo " --crl Generate CRL file" >&2 echo " -v|--verify Verify a certificate" >&2 echo " -h|--help Display this help message" >&2 echo "" >&2 } #------ Commandline parsing ------ shopt -s nocasematch EXTRASAN=() EXTRAIP=() POSITIONAL=() while [[ $# -gt 0 ]]; do key="$1" case $key in -t|--type) validateType ${2} shift # past argument shift # past value ;; -c|--newcert) OPERATION="newCertificate" shift # past argument ;; -n|--newreq) OPERATION="newRequest" shift # past argument ;; --newca) OPERATION="newCertificateAuthority" shift # past argument ;; -r|--renewcert) OPERATION="renewCertificate" shift # past argument ;; #--renewca) # OPERATION="renewCertificateAuthorityCertificate" # # shift # past argument # ;; --signcert) OPERATION="signCertificate" shift # past argument ;; -s|--signreq) OPERATION="signRequest" shift # past argument ;; #--signca) # OPERATION="signCertificateAuthorityCertificate" # # shift # past argument # ;; #--xsign) # OPERATION="signRequestAnything" # # shift # past argument # ;; -p|--pkcs12) OPERATION="createCertificatePackage" shift # past argument ;; --revoke) OPERATION="revokeCertificate" shift # past argument ;; --crl) OPERATION="generateCrl" shift # past argument ;; -v|--verify) OPERATION="verifyCertificate" shift # past argument ;; --san) EXTRASAN+=("$2") shift # past argument shift # past value ;; --ip) EXTRAIP+=("$2") shift # past argument shift # past value ;; -h|--help) OPERATION="printHelp" shift # past argument ;; *) POSITIONAL+=("$1") shift # past argument ;; esac done FILENAME="newCertificate" if (( ${#POSITIONAL[@]} )); then CN=${POSITIONAL[0]} FILENAME=${CN} fi if [[ -z "${TYPE}" && -f "${CAFOLDER}/${TYPEFILE}" ]]; then TYPE=$(<${CAFOLDER}/${TYPEFILE}) validateType ${TYPE} fi if [[ -z "${TYPE}" && "${OPERATION}" != "revokeCertificate" && "${OPERATION}" != "generateCrl" && "${OPERATION}" != "verifyCertificate" && "${OPERATION}" != "createCertificatePackage" && "${OPERATION}" != "printHelp" ]]; then echo "Error: missing certificate type" >&2 printHelp exit -1 fi if [[ ! -z "${TYPE}" && -z "${VALIDTYPE}" && "${OPERATION}" != "revokeCertificate" && "${OPERATION}" != "generateCrl" && "${OPERATION}" != "verifyCertificate" && "${OPERATION}" != "createCertificatePackage" && "${OPERATION}" != "printHelp" ]]; then echo "Error: invalid certificate type" >&2 printHelp exit -1 fi if [ -z "${OPERATION}" ]; then echo "Error: missing operation" >&2 printHelp exit -1 fi TYPE=${VALIDTYPE} eval ${OPERATION} exit 0