From 1d3892f3b022399524887108094bc4c89dd8ea16 Mon Sep 17 00:00:00 2001
From: Someone <someone@somenet.org>
Date: Thu, 5 Feb 2015 00:25:44 +0100
Subject: [PATCH] support creating a CA and using a previously generated ca to
 sign new certs.

---
 certgen.data.example | 12 +++++++++---
 certgen.sh           | 39 +++++++++++++++++++++++++++++++++------
 openssl.cnf          | 25 ++++++++++++++++++++++++-
 3 files changed, 66 insertions(+), 10 deletions(-)

diff --git a/certgen.data.example b/certgen.data.example
index c0d515d..626551d 100644
--- a/certgen.data.example
+++ b/certgen.data.example
@@ -1,14 +1,20 @@
 # Certgen config file
-# Format: <CRT/CSR> <domain name> <crt-subject> <alt dns names ","-separated, NOT SPACES!>
+# Format: <SGN/CRT/CSR> <domain name> <crt-subject> <alt dns names ","-separated, NOT SPACES!>
+# SGN: use CA in ./ca to sign the new cert.
+# CRT: selfsign the new cert
+# CST: create a csr only.
 
 # For failing services ;)
 CRT localhost /OU=CertGen/O=YouFailed;)/emailAddress=contact@somenet.org/ 
 
+# Setup a CA-system
+CA SomeNet /OU=CertGenCA/O=somenet.org/emailAddress=contact@somenet.org/CN=SomeNet/ 
+
 # ircd
-CRT a.irc.somenet.org /OU=CertGen/O=somenet.org/emailAddress=contact@somenet.org/ irc.somenet.org
+SGN irc.somenet.org /OU=CertGen/O=somenet.org/emailAddress=contact@somenet.org/ a.irc.somenet.org
 
 # apache stuff
-CRT www.somenet.org /OU=CertGen/O=somenet.org/emailAddress=contact@somenet.org/ somenet.org,irc2go.somenet.org
+SGN www.somenet.org /OU=CertGen/O=somenet.org/emailAddress=contact@somenet.org/ somenet.org,irc2go.somenet.org
 
 # we have xmpp too :/
 CSR xmpp.somenet.org /OU=CertGen/O=somenet.org/emailAddress=contact@somenet.org/ conference.xmpp.somenet.org
diff --git a/certgen.sh b/certgen.sh
index ed4d81e..ad0095b 100755
--- a/certgen.sh
+++ b/certgen.sh
@@ -1,11 +1,12 @@
 #!/bin/bash
 
 MYPWD=$(pwd)
+export CA_PATH="$MYPWD/ca/"
 umask 0027
 
 echo "cleanup previous run..."
 rm -rf output/*
-mkdir output/csr output/crt
+mkdir output/csr output/crt output/sgn
 
 while read cdline ; do
 	if [[ $cdline == "" || $cdline == "#"* ]] ; then
@@ -24,12 +25,25 @@ while read cdline ; do
 
 	mkdir "output/${certdata[1]}"
 	chmod o+x "output/${certdata[1]}"
+	cd "output/${certdata[1]}"
+
+	# Handle "CA" type here.
+	if [[ ${certdata[0]} == "CA" ]] ; then
+		mkdir -m 0700 certs crl newcerts
+		touch index.txt
+		export CA_PATH="./"
+		openssl req -batch -new -newkey rsa:4096 -keyout ca.key -out ca.csr -nodes -subj "${certdata[2]}" -reqexts v3_ca_req -config "${MYPWD}/openssl.cnf"
+		openssl ca -batch -create_serial -out ca.crt -days 3650 -keyfile ca.key -selfsign -extensions v3_ca -config "${MYPWD}/openssl.cnf" -infiles ca.csr
+		continue
+	fi
+
 
+	# Handle non "CA" types here.
 	SUBJECT="${certdata[2]}CN=${certdata[1]}/"
 	DNS_NAMES="${certdata[1]},${certdata[3]}"
 	OLDIFS=$IFS
 	IFS=","
-	cat openssl.cnf > /tmp/certgen.cnf
+	cat "${MYPWD}/openssl.cnf" > /tmp/certgen.cnf
 	COUNTER=0
 	for name in $DNS_NAMES; do
 		if [[ "" == $name ]] ; then
@@ -42,11 +56,22 @@ while read cdline ; do
 	unset OLDIFS
 	unset COUNTER
 	
-	cd "output/${certdata[1]}"
 	openssl genrsa -out "${certdata[1]}.key" 4096 &> /dev/null
 	openssl req -new -key "${certdata[1]}.key" -out "${certdata[1]}.csr" -utf8 -batch -subj "${SUBJECT}" -config /tmp/certgen.cnf
 
-	if [[ ${certdata[0]} == "CRT" ]] ; then
+	if [[ ${certdata[0]} == "SGN" ]] ; then
+		if [[ ! -d "${CA_PATH}"  ]] ; then
+			echo "*** ERROR - NO CA DATA FOUND ***" 1>&2
+			echo "*** maybe generate a CA and move it to ca first ***" 1>&2
+			echo "copy template: mv output/SomeNet ca" 1>&2
+			echo "*** ABORTED ***" 1>&2
+			exit 2
+		fi
+
+		openssl ca -batch -create_serial -out "${certdata[1]}.crt" -days 365 -keyfile "${MYPWD}/ca/ca.key" -extensions v3_ca -config "${MYPWD}/openssl.cnf" -infiles "${certdata[1]}.csr"
+		cat "${MYPWD}/ca/ca.crt" >> "${certdata[1]}.crt"
+
+	elif [[ ${certdata[0]} == "CRT" ]] ; then
 		openssl x509 -req -signkey "${certdata[1]}.key" -in "${certdata[1]}.csr" -out "${certdata[1]}.crt" -extensions v3_req -extfile /tmp/certgen.cnf \
 			-days 365 -sha512 &> /dev/null
 		chmod o+r "${certdata[1]}.crt"
@@ -59,7 +84,9 @@ while read cdline ; do
 	rm /tmp/certgen.cnf
 	cd $MYPWD
 
-	if [[ ${certdata[0]} == "CRT" ]] ; then
+	if [[ ${certdata[0]} == "SGN" ]] ; then
+		mv "output/${certdata[1]}" "output/sgn/${certdata[1]}"
+	elif [[ ${certdata[0]} == "CRT" ]] ; then
 		mv "output/${certdata[1]}" "output/crt/${certdata[1]}"
 	else
 		mv "output/${certdata[1]}" "output/csr/${certdata[1]}"
@@ -68,5 +95,5 @@ while read cdline ; do
 done < certgen.data
 
 echo "*** DONE ***"
-ls -l output/*/ | grep -v "total"
+ls -l "${MYPWD}/output/"*/ | grep -v "total"
 
diff --git a/openssl.cnf b/openssl.cnf
index ac271ab..4c6604a 100644
--- a/openssl.cnf
+++ b/openssl.cnf
@@ -16,8 +16,26 @@ preserve			= no            # keep passed DN ordering
 
 x509_extensions		= ca_extensions     # The extensions to add to the cert
 
+unique_subject	= no
 email_in_dn		= no            # Don't concat the email in the DN
-copy_extensions	= copy          # Required to copy SANs from CSR to cert
+copy_extensions	= copyall          # Required to copy SANs from CSR to cert
+
+dir		= ${ENV::CA_PATH}
+new_certs_dir	= $dir/certs
+database	= $dir/index.txt
+serial		= $dir/serial
+certificate	= $dir/ca.crt
+
+policy		= policy_match
+
+[ policy_match ]
+organizationName	= match
+countryName		= optional
+stateOrProvinceName	= optional
+localityName		= optional
+organizationalUnitName	= optional
+commonName		= supplied
+emailAddress		= optional
 
 ####################################################################
 [ req ]
@@ -54,6 +72,11 @@ subjectKeyIdentifier = hash
 authorityKeyIdentifier = keyid:always,issuer
 basicConstraints = CA:true
 
+####################################################################
+[ v3_ca_req ]
+basicConstraints = CA:true
+keyUsage = keyCertSign, cRLSign
+
 ####################################################################
 
 # ALT_NAMES MUST BE THE LAST LINE.
-- 
2.43.0