|
#!/usr/bin/env bash |
|
|
|
function defaultinput() |
|
{ |
|
local LABEL=$1 |
|
local DEFAULT=$2 |
|
local __return=$3 |
|
echo -n "$LABEL [$DEFAULT] " |
|
read input_a |
|
if [[ -z "$input_a" ]]; then |
|
eval $__return='$DEFAULT' |
|
else |
|
eval $__return='$input_a' |
|
fi |
|
} |
|
|
|
# ~~~~~~~~~~~~~~~~~ inputs: name, full name |
|
|
|
NAME=$1 |
|
|
|
if [ -z "$NAME" ]; then |
|
echo -n 'Domain for your CA [myca.com]: ' |
|
read input_d |
|
NAME=$input_d |
|
if [ -z "$input_d" ]; then |
|
NAME='myca.com' |
|
fi |
|
fi |
|
|
|
shift |
|
FULLNAME="$@" # remaining arugments become fullname |
|
|
|
if [ -z "$FULLNAME" ]; then |
|
echo -n "Displayname [$NAME] " |
|
read input_d |
|
FULLNAME=$input_d |
|
if [ -z "$input_d" ]; then |
|
FULLNAME=$NAME |
|
fi |
|
fi |
|
|
|
# ~~~~~~~~~~~~~~~~~ create folder for ca, using $NAME |
|
|
|
if [ -d "$NAME" ]; then |
|
echo -n "${NAME} exists: Overwrite config files (y/n)? [n] " |
|
read input_d |
|
if [ "$input_d" == 'y' ]; then |
|
echo "{ overwriting }" |
|
else |
|
exit |
|
fi |
|
else |
|
mkdir "$NAME" |
|
fi |
|
|
|
# ~~~~~~~~~~~~~~~~~ folders and misc file stubs |
|
|
|
cd $NAME |
|
mkdir -p cadata/serialized certs config |
|
touch "cadata/database" "makecert" |
|
chmod +x makecert |
|
echo 0001 > "cadata/database_serial" |
|
|
|
# ~~~~~~~~~~~~~~~~~ request header, with distingushed name defaults |
|
# customize to populate your root and subordinate cert requests |
|
# with senisble defaults. These are just defaults for interactive prompts: |
|
# all values can be overidden per certificate |
|
|
|
echo "::: making certificate request template" |
|
|
|
defaultinput "Country?" "US" COUNTRY |
|
defaultinput "State?" "California" STATE |
|
defaultinput "City?" "Los Angeles" CITY |
|
|
|
cat >config/req.header <<EOL |
|
[ req ] |
|
distinguished_name = req_distinguished_name |
|
x509_extensions = req_x509_extensions |
|
|
|
[ req_distinguished_name ] |
|
countryName = $COUNTRY |
|
countryName_default = US |
|
stateOrProvinceName = State |
|
stateOrProvinceName_default = $STATE |
|
localityName = City |
|
localityName_default = $CITY |
|
organizationName = Organization |
|
organizationalUnitName = Unit |
|
organizationName_default = $FULLNAME |
|
EOL |
|
|
|
# ~~~~~~~~~~~~~~~~~ config/req-root.conf |
|
# req-root.conf is used to generate the root certificate. |
|
# Configurations for using OCSP and CPS options are commented out |
|
# but are stubbed into the file. |
|
# * OCSP: Online Certificate Status Protocol |
|
# * CPS: Certificate Policy Statement |
|
|
|
cp config/req.header config/req-root.conf |
|
|
|
cat >>config/req-root.conf <<EOL |
|
organizationalUnitName_default = Internal Certificate Authority |
|
commonName = Common Name |
|
commonName_default = $FULLNAME x509/TLS Signatory |
|
|
|
[ req_x509_extensions ] |
|
basicConstraints = CA:true |
|
subjectKeyIdentifier = hash |
|
authorityKeyIdentifier = keyid:always,issuer:always |
|
# certificatePolicies = @subordinate_certificatePolicies |
|
# authorityInfoAccess = OCSP;URI:http://$NAME/ocsp/ |
|
|
|
# [ subordinate_certificatePolicies ] |
|
# CPS.1 = http://$NAME/cps/ |
|
# userNotice.1 = @subordinate_cp_notice |
|
|
|
# [ subordinate_cp_notice ] |
|
# organization = "$FULLNAME" |
|
# explicitText = "More info at http://$NAME/ca/" |
|
EOL |
|
|
|
# ~~~~~~~~~~~~~~~~~ CA root.key + root.crt |
|
# Create root key/cert. Won't overide previously created. |
|
# Every cert issued by a CA needs the root.key and root.crt. |
|
# If you want to stop creating certs delete the root key. |
|
# If you want to revoke your root.crt check out OCSP. |
|
# Your root.crt is the public key that clients should trust. |
|
|
|
if [ ! -s "root.key" ]; then |
|
# create root key |
|
# - safely managing this key is a high priority |
|
# - if you're key is compromised having a strong password can slow |
|
# and maybe prevent certificate forgers (stronger the better) |
|
openssl genrsa -des3 -out "root.key" 4096 |
|
# create root cert |
|
# - valid for 2 years, a short period of time for a real CA but for |
|
# small CAs YMMV |
|
openssl req -new -x509 -days 730 \ |
|
-config "config/req-root.conf" \ |
|
-key "root.key" \ |
|
-out "root.crt" |
|
fi |
|
|
|
# ~~~~~~~~~~~~~~~~~ makecert script |
|
# Simplifies the creation of issuing certificates per CA |
|
|
|
cat >makecert <<"EOL2" |
|
#!/usr/bin/env bash |
|
|
|
WILDCARD= |
|
OPTIND=1 |
|
while getopts "w" opt; do |
|
case $opt in |
|
w) WILDCARD=1;; |
|
esac |
|
done |
|
shift "$((OPTIND-1))" |
|
|
|
DOMAIN=$1 |
|
if [ -z "$DOMAIN" ]; then |
|
echo -n 'Domain [hello.local]: ' |
|
read input_d |
|
DOMAIN=$input_d |
|
if [ -z "$input_d" ]; then |
|
DOMAIN='hello.local' |
|
fi |
|
else |
|
shift |
|
fi |
|
|
|
OPTIND=1 |
|
while getopts "w" opt; do |
|
case $opt in |
|
w) WILDCARD=1;; |
|
esac |
|
done |
|
shift "$((OPTIND-1))" |
|
|
|
cp config/req.header config/req-$DOMAIN.conf |
|
cat >>config/req-$DOMAIN.conf <<EOL |
|
commonName = Common Name |
|
commonName_default = $DOMAIN |
|
|
|
[ req_x509_extensions ] |
|
basicConstraints = CA:false |
|
subjectKeyIdentifier = hash |
|
authorityKeyIdentifier = keyid:always,issuer:always |
|
EOL |
|
|
|
CONF='config/ca.conf' |
|
if [ "$WILDCARD" ]; then |
|
CONF="config/ca-wildcard-$DOMAIN.conf" |
|
cp config/ca.conf $CONF |
|
cat >>$CONF <<EOL |
|
subjectAltName = @alt_names |
|
[ alt_names ] |
|
DNS.1 = $DOMAIN |
|
DNS.2 = *.$DOMAIN |
|
EOL |
|
fi |
|
|
|
if [ -s certs/$DOMAIN.key ]; then |
|
echo -n "Key found. Overwrite key? [n] " |
|
read input_yn |
|
if [ "$input_yn" == 'y' -o "$input_yn" == 'Y' ]; then |
|
rm certs/$DOMAIN.key |
|
openssl genrsa -out certs/$DOMAIN.key 4096 |
|
fi |
|
else |
|
openssl genrsa -out certs/$DOMAIN.key 4096 |
|
fi |
|
openssl req -new -config config/req-${DOMAIN}.conf -key certs/$DOMAIN.key -out $DOMAIN.csr |
|
openssl ca -batch -notext -config $CONF -in $DOMAIN.csr -out certs/$DOMAIN.crt |
|
rm *csr |
|
EOL2 |
|
|
|
# ~~~~~~~~~~~~~~~~~ config/ca.conf |
|
# stable boilerplate CA configuration file |
|
# used for sigining certs |
|
|
|
cat >"config/ca.conf" <<EOL |
|
[ ca ] |
|
default_ca = myca |
|
|
|
[ myca ] |
|
new_certs_dir = ./cadata/serialized |
|
unique_subject = no |
|
private_key = ./root.key |
|
certificate = ./root.crt |
|
database = ./cadata/database |
|
serial = ./cadata/database_serial |
|
default_days = 365 |
|
default_md = sha1 |
|
policy = subordinate_policy |
|
x509_extensions = subordinate_extensions |
|
|
|
[ subordinate_policy ] |
|
commonName = supplied |
|
stateOrProvinceName = supplied |
|
countryName = supplied |
|
emailAddress = optional |
|
organizationName = supplied |
|
organizationalUnitName = optional |
|
|
|
[ subordinate_extensions ] |
|
basicConstraints = CA:false |
|
subjectKeyIdentifier = hash |
|
authorityKeyIdentifier = keyid:always |
|
keyUsage = digitalSignature,keyEncipherment |
|
extendedKeyUsage = serverAuth |
|
EOL |