Skip to content

Instantly share code, notes, and snippets.

@craigphicks
Last active March 13, 2019 10:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save craigphicks/c9dae527b30441730f62c9c9e9dab5a1 to your computer and use it in GitHub Desktop.
Save craigphicks/c9dae527b30441730f62c9c9e9dab5a1 to your computer and use it in GitHub Desktop.
Create private CA and server/client certs for 2-way auth secure closed networks. See https://github.com/craigphicks/privca
#!/bin/bash
#
# craigphicks copyright 2019 - MIT license
# see https://github.com/craigphicks/privca
set -u
Existing_CA_CN=""
function _get_CA_CN {
shopt -s nullglob
afn1=(./ca/private/*.key)
afn2=(./ca/public/*.crt)
if [[ ${#afn1[@]} -eq 0 ]] && [[ ${#afn2[@]} -eq 0 ]] ; then
Existing_CA_CN=""
return 1
fi
if [[ ${#afn1[@]} -eq 1 ]] && [[ ${#afn2[@]} -eq 1 ]] ; then
cn1=$(basename ${afn1[0]%.*})
cn2=$(basename ${afn2[0]%.*})
if [[ "$cn1" == "$cn2" ]] ; then
Existing_CA_CN=$cn1
return 0
fi
fi
echo "ERROR: illegal CA file combination found" >& 2
exit 111
}
function CreateCA {
! _get_CA_CN || { echo "CA Already exists: $Existing_CA_CN" >&2 ; exit 1; }
CA_CN=${1}
CA_ORG=${2}
CAKeyFN=./ca/private/${CA_CN}.key
CACrtFN=./ca/public/${CA_CN}.crt
if [[ ! -d ./ca/private ]] ; then
mkdir -p ./ca/private || exit 2
sudo chmod 700 ./ca/private || exit 3
fi
if [[ ! -d ./ca/public ]] ; then
mkdir -p ./ca/public || exit 4
fi
#
# Create the Root Key
#
openssl genrsa -out ${CAKeyFN} 2048 || exit 5
#
# Now self-sign this certificate using the root key.
#
# CN: CommonName
# OU: OrganizationalUnit
# O: Organization
# L: Locality
# S: StateOrProvinceName
# C: CountryName
#
openssl req -x509 \
-new \
-nodes \
-key ${CAKeyFN} \
-sha256 \
-days 3650 \
-subj "/C=US/ST=--/L=--/O=${CA_ORG}/OU=--/CN=${CA_CN}" \
-out ${CACrtFN} || exit 6
if [[ ! -d ./export/public ]] ; then mkdir -p ./export/public || exit 8 ; fi
ln -s ${CACrtFN} ./export/public/ || exit 9;
}
function CreateLeaf {
_get_CA_CN || { echo "Must create CA first"; exit 11; }
CA_CN=${Existing_CA_CN}
LEAF_CN=${1}
subjectAltNameArg=${2}
CAKeyFN=./ca/private/${CA_CN}.key
CACrtFN=./ca/public/${CA_CN}.crt
LEAFKeyFN=./private/${LEAF_CN}.key
LEAFCsrFN=./temp/${LEAF_CN}.csr
LEAFCrtFN=./public/${LEAF_CN}.crt
[[ -d ./private ]] || { mkdir ./private || exit 12; }
[[ -d ./public ]] || { mkdir ./public || exit 12; }
chmod 750 ./private || exit 12;
[[ -d ./temp ]] || { mkdir ./temp || exit 12; }
for f in ${LEAFKeyFN} ${LEAFCrtFN} ; do
if [[ -f $f ]] ; then echo "$f already exists"; exit 15; fi
done
#
# Create A Certificate
#
if ! openssl genrsa -out ${LEAFKeyFN} 2048
then exit 16
fi
#
# Now generate the certificate signing request.
#
if ! openssl req -new \
-key ${LEAFKeyFN} \
-subj "/C=US/ST=--/L=--/O=--/OU=--/CN=${LEAF_CN}" \
-out ${LEAFCsrFN}
then exit 17
fi
#
# Now generate the final certificate from the signing request.
#
if ! openssl x509 -req \
-in ${LEAFCsrFN} \
-CA ${CACrtFN} \
-CAkey ${CAKeyFN} \
-CAcreateserial \
-extfile <(printf "subjectAltName=${subjectAltNameArg}") \
-out ${LEAFCrtFN} -days 3650 -sha256
then exit 18
fi
chmod -R 640 ${LEAFKeyFN} || exit 19
}
function MakeForLighttpd {
[[ $# -eq 1 ]] || { echo "one args only: server common name"; exit 31; }
_get_CA_CN || { echo "Must create CA first"; exit 32; }
CA_CN=${Existing_CA_CN}
SRV_CN=${1}
CACrtFN=./ca/public/${CA_CN}.crt
SrvKeyFN=./private/${SRV_CN}.key
SrvCrtFN=./public/${SRV_CN}.crt
mkdir -p ./export/private
mkdir -p ./export/public
chmod 750 ./private || exit 33
ExpSrvKeyCrtFN=./export/private/${SRV_CN}.key-crt.pem
cat ${SrvKeyFN} ${SrvCrtFN} > ${ExpSrvKeyCrtFN} || exit 35;
chmod 640 ${ExpSrvKeyCrtFN} || exit 36;
}
function MakeForClientSideAuth_P12 {
[[ $# -eq 1 ]] || { echo "one args only: client common name"; exit 41; }
_get_CA_CN || { echo "Must create CA first"; exit 42; }
CA_CN=${Existing_CA_CN}
CL_CN=${1}
ExpClientP12FN=./export/private/${CL_CN}.p12
openssl pkcs12 -export -in ./public/${CL_CN}.crt \
-inkey ./private/${CL_CN}.key \
-certfile ./ca/public/${CA_CN}.crt \
-out ${ExpClientP12FN} || exit 44
chmod 640 ${ExpClientP12FN} || exit 46;
}
trap "on_exit" 0
function on_exit {
exitval=$?
if [[ $exitval -eq 0 ]] ;
then echo "SUCCESS"
else echo "FAILED with error $exitval"
fi
$(exit $exitval)
}
function Usage {
thisprog=$(basename $0)
cat <<EOF_
NOTE: NO SPACES ALLOWED IN COMMON NAMES
$thisprog CreateCA <CA Common Name> <CA organization name>
$thisprog CreateServer <Server common name> <subjectAltNames>
$thisprog CreateClient <Client common name>
Examples:
$thisprog CreateCA HomeLan "Self managed private certs"
$thisprog CreateServer Server1 DNS:pihole.home.lan,DNS:pihole,IP:192.168.1.20
$thisprog CreateClient Client1
EOF_
}
if [[ $# -lt 2 ]] ; then Usage ; exit 1; fi
case $1 in
CreateCA)
if [[ $# -eq 3 ]] ; then
CreateCA ${2} "${3}";
else Usage;
fi
;;
CreateServer)
if [[ $# -eq 3 ]] ; then
CreateLeaf ${2} ${3}
MakeForLighttpd ${2}
else Usage;
fi
;;
CreateClient)
if [[ $# -eq 2 ]] ; then
CreateLeaf ${2} "email:${2}"
MakeForClientSideAuth_P12 ${2}
else Usage;
fi
;;
*) Usage ; exit 1;
;;
esac
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment