~poptart/hosaka-pki

8d6f421fbd4ea6b5ac74c4add9e8321b9c370982 — poptart 3 months ago 3732552 master
Updated to begin adding the interactive functions
4 files changed, 206 insertions(+), 55 deletions(-)

M README.md
M config.def
M hosaka-pki.sh
M openssl.cnf.tmpl
M README.md => README.md +2 -0
@@ 10,6 10,8 @@ The goals are to attempt to shorten the nightmare of remember a ton of
PKI commands and to encourage secure defaults. New versions never have
guarentees about compatability.

Fundamentally this project is a thin wrapper around openssl commands
in order to try and alieviate some of the UX nightmare that they are.

SSL/TLS PKI
-----------

M config.def => config.def +5 -0
@@ 16,6 16,11 @@ MDALGORITHM=sha512
#Should we configure an intermediate CA
USEINTERMEDIATE=yes

#Remember passwords for the hosaka-pki in the shell script. Generally
#this is "less secure" and leaves open oppurtunistic attacks, but 
#if it is false prompting can be... excessive.
CACHECREDENTIALS=yes

#If an intermediate was generated and the root CA keys are still
#accessible then emit a warning that that's a bad idea
WARNROOTCERT=yes

M hosaka-pki.sh => hosaka-pki.sh +196 -52
@@ 6,6 6,7 @@ export DAYSVALID=""
export CADAYSVALID=""
export MDALGORITHM=""
export USEINTERMEDIATE=""
export CACHECREDENTIALS=""
export WARNROOTCERT=""
export COUNTRYDEFAULT=""
export STATEDEFAULT=""


@@ 16,6 17,33 @@ export USERCERTCOMMENT=""
export SRVCERTCOMMENT=""
export SSLSERIALDEFAULT=""
export SSHSERIALDEFAULT=""
export SSLFILE=""

USAGESTRING="Usage: $0 [tls|ssh] [info|help|ca|server|sign|check] [OPTIONS...]"
HELPSTRING=$(cat <<'HEREDOC'
	\e[1mtls\e[0m - TLS PKI management commands
		\e[1minfo\e[0m
			Prints information about the current system and its configuration.
		\e[1mhelp\e[0m
			Prints this help and usage dialoge
		\e[1mca\e[0m
			Configures a certificate authority and establishes an intermediary
			depending on the configuration of hosaka-pki. If a CA or intermed-
			iary exists in the configuration file locations the command will
			exit.
		\e[1mserver\e[0m NAME OUTPUTDIR
			Generates a set of local keys and .csr files to be uploaded for
			signing on the CA system. NAME will be the prefix used on the file
			names; .key.pem and .csr.pem  
		\e[1msign\e[0m [PATH|SHORTNAME]
			Signs the TLS certificate based on the direct PATH or if the certi-
			cate exists in the csr/ directory of the PKI the SHORTNAME can be
			used instead.
		\e[1mcheck\e[0m
			Check the status of the PKI and prints out soon to expire certifi-
			cates. If no certificates are about to expire nothing will be output
HEREDOC
)

readconf() {
	while IFS='=' read -r key val; do


@@ 33,6 61,7 @@ checkvars() {
	[ -z "$CADAYSVALID" ] && printf "Arguments cannot be empty: %s\\n" "$CADAYSVALID" 1>&2 && exit 1
	[ -z "$MDALGORITHM" ] && printf "Arguments cannot be empty: %s\\n" "$MDALGORITHM" 1>&2 && exit 1
	[ -z "$USEINTERMEDIATE" ] && printf "Arguments cannot be empty: %s\\n" "$USEINTERMEDIATE" 1>&2 && exit 1
	[ -z "$CACHECREDENTIALS" ] && printf "Arguments cannot be empty: %s\\n" "$CACHECREDENTIALS" 1>&2 && exit 1
	[ -z "$WARNROOTCERT" ] && printf "Arguments cannot be empty: %s\\n" "$WARNROOTCERT" 1>&2 && exit 1
	[ -z "$COUNTRYDEFAULT" ] && printf "Arguments cannot be empty: %s\\n" "$COUNTRYDEFAULT" 1>&2 && exit 1
	[ -z "$STATEDEFAULT" ] && printf "Arguments cannot be empty: %s\\n" "$STATEDEFAULT" 1>&2 && exit 1


@@ 60,9 89,10 @@ getconf() {
	[ -f "${CONFIGDIR}/config" ] && readconf "${CONFIGDIR}/config"
}

sslgenconf() {
tlsgenconf() {
	#Generate conf for the CA and if an intermediate is in use, generate
	#for that as well
	export SSLFILE="ca"
	sed -e "s%{{SSLDIR}}%${SSL_CA_DIR}%g" \
		-e "s%{{MDALGORITHM}}%${MDALGORITHM}%g" \
		-e "s%{{DAYSVALID}}%${DAYSVALID}%g" \


@@ 75,8 105,10 @@ sslgenconf() {
		-e "s%{{USERCERTCOMMENT}}%${USERCERTCOMMENT}%g" \
		-e "s%{{SERVERCERTCOMMENT}}%${SERVERCERTCOMMENT}%g" \
		-e "s%{{SSLSERIALDEFAULT}}%${SSLSERIALDEFAULT}%g" \
		-e "s%{{SSLFILE}}%${SSLFILE}%g" \
		< "$CONFIGDIR/util/openssl.cnf.tmpl" > "$SSL_CA_DIR/openssl.cnf"
	ssluseintermediate && sed -e "s%{{SSLDIR}}%${SSL_CA_DIR}/intermediate%g" \
	tlsuseintermediate && export SSLFILE="intermediate"
	tlsuseintermediate && sed -e "s%{{SSLDIR}}%${SSL_CA_DIR}/intermediate%g" \
		-e "s%{{MDALGORITHM}}%${MDALGORITHM}%g" \
		-e "s%{{DAYSVALID}}%${DAYSVALID}%g" \
		-e "s%{{CADAYSVALID}}%${CADAYSVALID}%g" \


@@ 88,10 120,11 @@ sslgenconf() {
		-e "s%{{USERCERTCOMMENT}}%${USERCERTCOMMENT}%g" \
		-e "s%{{SERVERCERTCOMMENT}}%${SERVERCERTCOMMENT}%g" \
		-e "s%{{SSLSERIALDEFAULT}}%${SSLSERIALDEFAULT}%g" \
		-e "s%{{SSLFILE}}%${SSLFILE}%g" \
		< "$CONFIGDIR/util/openssl.cnf.tmpl" > "$SSL_CA_DIR/openssl_intermediate.cnf"
}

ssluseintermediate() {
tlsuseintermediate() {
	case "$USEINTERMEDIATE" in
		y*|Y*)
			return 0


@@ 103,7 136,20 @@ ssluseintermediate() {
	return 1
}

sslcreatedirs() {
tlscachecredential() {
	case "$CACHECREDENTIALS" in
		y*|Y*)
			return 0
		;;
		*)
			return 1
		;;
	esac
	return 1
}


tlscreatedirs() {
	mkdir -p "${SSL_CA_DIR}/intermediate" \
		"${SSL_CA_DIR}/certs" \
		"${SSL_CA_DIR}/crl" \


@@ 111,7 157,7 @@ sslcreatedirs() {
		"${SSL_CA_DIR}/private" \
		"${SSL_CA_DIR}/new" && \
	chmod 700 "${SSL_CA_DIR}/private"
	ssluseintermediate && \
	tlsuseintermediate && \
		mkdir -p "${SSL_CA_DIR}/intermediate" \
			"${SSL_CA_DIR}/intermediate/certs" \
			"${SSL_CA_DIR}/intermediate/crl" \


@@ 121,40 167,54 @@ sslcreatedirs() {
		chmod 700 "${SSL_CA_DIR}/intermediate/private"
}

sslgenca() {
	sslcreatedirs
tlsgenca() {
	tlscreatedirs
	_UMASK="$(umask)"
	umask 077
	_CACACHE=""
	if [ -f "${SSL_CA_DIR}/private/ca.key.pem" ]; then
		printf "ca key already exists! bailing...\\n" 1>&2
		exit 2
	fi
	printf "generating ca\\n"
	#TODO support automation ie, symm keys stored on disk
	openssl ecparam -genkey -name secp521r1 | openssl ec -aes-256-cbc \
		-out "${SSL_CA_DIR}/private/ca.key.pem"
	openssl req -config "${SSL_CA_DIR}/openssl.cnf" -x509 -new \
		-md "$MDALGORITHM" -nodes -key "${SSL_CA_DIR}/private/ca.key.pem" -days 3650 \
		-out "${SSL_CA_DIR}/certs/ca.cert.pem"
	if [ "$(tlscachecredential)" -lt 1 ]; then
	    printf "Enter CA key password: " && read -r _CACACHE 		
		printf "%s" "$_CACACHE" >"${SSL_CA_DIR}/private/cachefile"
		openssl ecparam -genkey -name secp521r1 | openssl ec \
			-aes-256-cbc -passout file:"${SSL_CA_DIR}/private/cachefile" \
			-out "${SSL_CA_DIR}/private/ca.key.pem"
		openssl req -config "${SSL_CA_DIR}/openssl.cnf" -x509 -new \
			-"$MDALGORITHM" -nodes -key "${SSL_CA_DIR}/private/ca.key.pem" -days 3650 \
			-passin file:"${SSL_CA_DIR}/private/cachefile" -out "${SSL_CA_DIR}/certs/ca.cert.pem"
	else 
		openssl ecparam -genkey -name secp521r1 | openssl ec -aes-256-cbc \
			-out "${SSL_CA_DIR}/private/ca.key.pem"
		openssl req -config "${SSL_CA_DIR}/openssl.cnf" -x509 -new \
			-"$MDALGORITHM" -nodes -key "${SSL_CA_DIR}/private/ca.key.pem" -days 3650 \
			-out "${SSL_CA_DIR}/certs/ca.cert.pem"
	fi
	touch "${SSL_CA_DIR}/index.txt"
	umask "${_UMASK}"
}

sslgenserver() {
tlsgenserver() {
	#TODO actually check / generate based on cli
	if [ ! -d "$2" ]; then
		printf "%s is not a directory\\n" "$2"
	fi
	_UMASK="$(umask)"
	umask 077
	printf "generating server keys\\n"
	openssl ecparam -genkey -name secp521r1 | openssl ec -aes-256-cbc \
		-out "$2/$1.key.pem"
	openssl req -x509 -config "${SSL_CA_DIR}/openssl.cnf" -new \
		-md "$MDALGORITHM" -nodes -key "$2/$1.key.pem" -days "$DAYSVALID" \
		-nodes -key "$2/$1.key.pem" -days "$DAYSVALID" \
		-out "$2/$1.csr.pem"
	cat "$2/$1.csr.pem"
	umask "${_UMASK}"
}

sslgenintermediate() {
tlsgenintermediate() {
	_UMASK="$(umask)"
	umask 077
	if [ -f "${SSL_CA_DIR}/intermediate/private/intermediate.key.pem" ]; then


@@ 162,71 222,155 @@ sslgenintermediate() {
		exit 2
	fi
	printf "generating intermediate ca\\n"
	#TODO support automation ie, symm keys stored on disk
	openssl ecparam -genkey -name secp521r1 | openssl ec -aes-256-cbc \
		-out "${SSL_CA_DIR}/intermediate/private/intermediate.key.pem"
	touch "${SSL_CA_DIR}/intermediate/index.txt"
	printf "generating intermediate csr\\n"
	openssl req -config "${SSL_CA_DIR}/openssl_intermediate.cnf" -new -md "$MDALGORITHM" \
		-key "${SSL_CA_DIR}/intermediate/private/intermediate.key.pem" \
		-out "${SSL_CA_DIR}/intermediate/csr/intermediate.csr.pem"
	printf "signing intermediate ca\\nenter ca cert password\\n"
	openssl ca -config "${SSL_CA_DIR}/openssl.cnf" \
		-extensions v3_intermediate_ca -days "$CADAYSVALID" -notext \
		-md "$MDALGORITHM" \
		-in "${SSL_CA_DIR}/intermediate/csr/intermediate.csr.pem" \
		-out "${SSL_CA_DIR}/intermediate/certs/intermediate.cert.pem"
	if [ "$(tlscachecredential)" -lt 1 ]; then
	    printf "Enter Intermediate CA key password: " && read -r _ICACACHE 		
		printf "%s" "$_ICACACHE" > "${SSL_CA_DIR}/intermediate/private/cachefile"
		openssl ecparam -genkey -name secp521r1 | openssl ec \
			-aes-256-cbc -passout file:"${SSL_CA_DIR}/intermediate/private/cachefile" \
			-out "${SSL_CA_DIR}/intermediate/private/intermediate.key.pem"
		printf "signing intermediate ca\\n"
		touch "${SSL_CA_DIR}/intermediate/index.txt"
		printf "generating intermediate csr\\n"
		openssl req -config "${SSL_CA_DIR}/openssl_intermediate.cnf" -new \
			-key "${SSL_CA_DIR}/intermediate/private/intermediate.key.pem" \
			-passin file:"${SSL_CA_DIR}/intermediate/private/cachefile" \
			-out "${SSL_CA_DIR}/intermediate/csr/intermediate.csr.pem"
		printf "signing intermediate ca\\nenter ca cert password\\n"
		openssl ca -config "${SSL_CA_DIR}/openssl.cnf" \
			-extensions v3_intermediate_ca -days "$CADAYSVALID" -notext \
			-md "$MDALGORITHM" \
			-passin file:"${SSL_CA_DIR}/private/cachefile" \
			-in "${SSL_CA_DIR}/intermediate/csr/intermediate.csr.pem" \
			-out "${SSL_CA_DIR}/intermediate/certs/intermediate.cert.pem"
	else 
		openssl ecparam -genkey -name secp521r1 | openssl ec -aes-256-cbc \
			-out "${SSL_CA_DIR}/intermediate/private/intermediate.key.pem"
		touch "${SSL_CA_DIR}/intermediate/index.txt"
		printf "generating intermediate csr\\n"
		openssl req -config "${SSL_CA_DIR}/openssl_intermediate.cnf" -new -md "$MDALGORITHM" \
			-key "${SSL_CA_DIR}/intermediate/private/intermediate.key.pem" \
			-out "${SSL_CA_DIR}/intermediate/csr/intermediate.csr.pem"
		printf "signing intermediate ca\\nenter ca cert password\\n"
		openssl ca -config "${SSL_CA_DIR}/openssl.cnf" \
			-extensions v3_intermediate_ca -days "$CADAYSVALID" -notext \
			-md "$MDALGORITHM" \
			-in "${SSL_CA_DIR}/intermediate/csr/intermediate.csr.pem" \
			-out "${SSL_CA_DIR}/intermediate/certs/intermediate.cert.pem"
	fi
	umask "${_UMASK}"
}

sslcheckcaondisk() {
tlscheckcaondisk() {
	[ -f "$SSL_CA_DIR/private/ca.key.pem" ] && printf "CA file is on disk. This is not a secure configuration and the root CA should not be stored with the intermediate and should only be used to generate new intermediates.\\n" 1>&2
}

sslserialgen() {
tlsserialgen() {
	if [ ! -f "${SSL_CA_DIR}/serial" ]; then
		printf "%s\\n" "$SSLSERIALDEFAULT" > "${SSL_CA_DIR}/serial"
		ssluseintermediate && \
		tlsuseintermediate && \
			printf "%s\\n" "$SSLSERIALDEFAULT" > "${SSL_CA_DIR}/intermediate/serial"
	fi
}

sslsignserver(){
tlscacheclean() {
	rm -f "${SSL_CA_DIR}/intermediate/private/cachefile"
	rm -f "${SSL_CA_DIR}/private/cachefile"
}

tlssignserver(){
	if [ $# != 1 ]; then
		printf "signing only works on a single target, and one is required\\n" 1>&2
		exit 3
	fi
	export _CSROUT=""
	if [ -f "${SSL_CA_DIR}/intermediate/csr/$(basename $1).csr.pem" ]; then
		_CSROUT="${SSL_CA_DIR}/intermediate/certs/$(basename "$1").pem"
	elif [ -f "$1" ]; then
		_CSROUT="$1"
	else
		printf "shortname or path for signature is incorrect %s\\n" "$1"
		exit 3
	fi
	#TODO check if file exists and is readable
	printf "signing csr\\n"
	if [ "$(ssluseintermediate)" ]; then
	if [ "$(tlsuseintermediate)" -lt 1 ]; then
		openssl ca -config "${SSL_CA_DIR}/openssl_intermediate.cnf" \
			-extensions v3_intermediate_ca -days "$CADAYSVALID" -notext \
			-md "$MDALGORITHM" \
			-in "$1" \
			-out "${SSL_CA_DIR}/intermediate/certs/$(basename "$1").pem"
			-out "$_CSROUT"
	else
		openssl ca -config "${SSL_CA_DIR}/openssl.cnf" \
			-extensions v3_intermediate_ca -days "$CADAYSVALID" -notext \
			-md "$MDALGORITHM" \
			-in "$1" \
			-out "${SSL_CA_DIR}/intermediate/certs/$(basename "$1").pem"
			-out "$_CSROUT"
	fi
}

if [ $# -lt 1 ]; then
	printf "%s\\n%b\\n" "$USAGESTRING" "$HELPSTRING" 1>&2
	exit 4
fi

case "$1" in
	"tls")
		shift 1
		case "$1" in
			"info")
				shift 1
			;;
			"help")
				printf "%s\\n%b\\n" "$USAGESTRING" "$HELPSTRING"
				exit 0
			;;
			"ca")
				getconf
				checkvars
				tlscheckcaondisk
				tlscreatedirs
				tlsgenconf
				tlsgenca
				tlsserialgen
				tlsgenintermediate
				tlscacheclean
				exit 0
			;;
			"server")
				shift 1
				getconf
				checkvars
				tlscheckcaondisk
				tlscreatedirs
				tlsgenserver "$1" "$2"
			;;
			"sign")
				shift 1
				getconf
				checkvars
				tlscheckcaondisk
				tlscreatedirs
				tlssignserver "$1"
			;;
			"check")
				shift 1
				getconf
				checkvars
				tlscheckcaondisk
				#TODO soon to expire
			;;
			*)
				printf "%s\\n%b\\n" "$USAGESTRING" "$HELPSTRING" 1>&2
                exit 1
			;;
		esac
	;;
	"ssh")
		shift 1
	;;
	*)
		printf "%s\\n%b\\n" "$USAGESTRING" "$HELPSTRING" 1>&2
		exit 1
	;;
esac

#getconf
#sslcheckcaondisk
#sslcreatedirs
#sslgenconf
#sslgenca
#sslserialgen
#sslgenintermediate


#sslsignclient() {
#
#}
#sslcheckexpir() {
#
#}

M openssl.cnf.tmpl => openssl.cnf.tmpl +3 -3
@@ 9,10 9,10 @@ new_certs_dir = $dir/new
database = $dir/index.txt
serial = $dir/serial
RANDFILE = $dir/private/rand
private_key = $dir/private/ca.key.pem
certificate = $dir/certs/ca.cert.pem
private_key = $dir/private/{{SSLFILE}}.key.pem
certificate = $dir/certs/{{SSLFILE}}.cert.pem
crlnumber         = $dir/crlnumber
crl               = $dir/crl/ca.crl.pem
crl               = $dir/crl/{{SSLFILE}}.crl.pem
crl_extensions    = crl_ext
default_crl_days  = 30
default_md        = {{MDALGORITHM}}