Files
openssl_scripts/certificate.sh
T
2021-10-20 14:29:13 +02:00

788 lines
17 KiB
Bash
Executable File

#!/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 <OPERATION> -t <TYPE> [--san <SAN>] [--ip <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