107 lines
2.7 KiB
Bash
107 lines
2.7 KiB
Bash
|
#!/bin/sh
|
||
|
|
||
|
# Generate CA + wildcard cert for any hostname
|
||
|
|
||
|
CA_name='local CA'
|
||
|
|
||
|
helptext='Usage:
|
||
|
./CA.sh <host> Generate a CA and sign a wildacrd cert for <host> with it.'
|
||
|
|
||
|
set -o errexit # (-e) exit immediately if any command has a non-zero exit status
|
||
|
set -o nounset # (-u) don't accept undefined variables
|
||
|
#set -o xtrace # for debugging
|
||
|
|
||
|
# Check input.
|
||
|
if [ "$#" -lt 1 ] || [ "$#" -gt 1 ]; then
|
||
|
>&2 echo "$helptext"
|
||
|
exit 1
|
||
|
fi
|
||
|
|
||
|
host="$1"
|
||
|
|
||
|
# Check if host is an IP address.
|
||
|
ip=0
|
||
|
# If `ipcalc-ng` is available, use it.
|
||
|
if command -v ipcalc-ng >/dev/null; then
|
||
|
if ipcalc-ng -sc "$host"; then
|
||
|
ip=1
|
||
|
fi
|
||
|
# Else if `ip` is available, use it.
|
||
|
# Note: a simple number also passes this check,
|
||
|
# but it's good enough for our purposes.
|
||
|
elif command -v ip >/dev/null; then
|
||
|
if ip route get "$host" >/dev/null 2>&1; then
|
||
|
ip=1
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
# If host is an IDN, convert it to punycode.
|
||
|
# Use the `idn` command, if available.
|
||
|
if command -v idn >/dev/null; then
|
||
|
host=$(echo "$host" | idn)
|
||
|
fi
|
||
|
|
||
|
# Create CA directory, if it does not exist.
|
||
|
mkdir -p CA
|
||
|
cd CA
|
||
|
|
||
|
# Generate CA.
|
||
|
if [ ! -f "ca.key" ] && [ ! -f "ca.crt" ]; then
|
||
|
openssl genrsa -out ca.key 2048
|
||
|
openssl req -new -x509 -key ca.key -out ca.crt -subj "/CN=$CA_name" -days 30
|
||
|
openssl x509 -in ca.crt -noout -text
|
||
|
|
||
|
# Copy the CA cert under a new name, allowing it to be accepted in the
|
||
|
# Android trust store (/system/etc/security/cacerts/).
|
||
|
mkdir -p android
|
||
|
filename=$(openssl x509 -inform PEM -subject_hash_old -in ca.crt | head -1)
|
||
|
cp ca.crt "android/$filename.0"
|
||
|
fi
|
||
|
|
||
|
# Generate wildcard cert.
|
||
|
if [ ! -f "$host.key" ] && [ ! -f "$host.crt" ]; then
|
||
|
|
||
|
# Set the Subject Alternative Name based on whether
|
||
|
# the host is an IP address or not.
|
||
|
if [ $ip -eq 1 ]; then
|
||
|
SAN1="IP:$host"
|
||
|
SAN2="IP = $host"
|
||
|
else
|
||
|
SAN1="DNS:*.$host, DNS:$host"
|
||
|
SAN2="DNS.1 = *.$host
|
||
|
DNS.2 = $host"
|
||
|
fi
|
||
|
|
||
|
openssl genrsa -out "$host.key" 2048
|
||
|
openssl req -new -key "$host.key" -out "$host.csr" -subj "/CN=$host" \
|
||
|
-addext "subjectAltName = $SAN1"
|
||
|
#openssl req -in "$host.csr" -noout -text
|
||
|
|
||
|
# Prepare X.509 extensions file.
|
||
|
echo "basicConstraints=CA:FALSE
|
||
|
subjectAltName=@my_subject_alt_names
|
||
|
subjectKeyIdentifier = hash
|
||
|
|
||
|
[ my_subject_alt_names ]
|
||
|
$SAN2" > ext.conf
|
||
|
|
||
|
# Sign the cert.
|
||
|
openssl x509 -req -in "$host.csr" -out "$host.crt" -days 30 \
|
||
|
-CA ca.crt -CAkey ca.key -extfile ext.conf -CAcreateserial
|
||
|
openssl x509 -in "$host.crt" -noout -text
|
||
|
rm "$host.csr"
|
||
|
|
||
|
# Remove X.509 extensions file.
|
||
|
rm ext.conf
|
||
|
fi
|
||
|
|
||
|
# Create bundle (probably not be required).
|
||
|
#cat "$host.crt" ca.crt > "$host.bundle.crt"
|
||
|
|
||
|
# Show generated files.
|
||
|
if command -v tree >/dev/null; then
|
||
|
cd ..
|
||
|
echo '=== Generated files ==='
|
||
|
tree CA
|
||
|
fi
|