snippets/ca.sh

617 lines
17 KiB
Bash

#!/bin/bash
# Source: https://jamielinux.com/docs/openssl-certificate-authority/index.html
if [ -z "$1" ]; then
echo "=x=> Path for your CA root is required!"
exit 1
fi
CA_ROOT=$(realpath "$1")
# Intermediate directory
INTERMEDIATE_ROOT="$CA_ROOT/intermediate"
# Static config blanks
read -r -d '' OPENSSL_CA_CFG << EOM
# OpenSSL root CA configuration file.
# Auto-generated.
[ ca ]
# \`man ca\`
default_ca = CA_default
[ CA_default ]
# Directory and file locations.
dir = $CA_ROOT
certs = \$dir/certs
crl_dir = \$dir/crl
new_certs_dir = \$dir/newcerts
database = \$dir/index.txt
serial = \$dir/serial
RANDFILE = \$dir/private/.rand
# The root key and root certificate.
private_key = \$dir/private/ca.key.pem
certificate = \$dir/certs/ca.cert.pem
# For certificate revocation lists.
crlnumber = \$dir/crlnumber
crl = \$dir/crl/ca.crl.pem
crl_extensions = crl_ext
default_crl_days = 30
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
name_opt = ca_default
cert_opt = ca_default
default_days = 375
preserve = no
policy = policy_strict
[ policy_strict ]
# The root CA should only sign intermediate certificates that match.
# See the POLICY FORMAT section of \`man ca\`.
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ policy_loose ]
# Allow the intermediate CA to sign a more diverse range of certificates.
# See the POLICY FORMAT section of the \`ca\` man page.
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
# Options for the \`req\` tool (\`man req\`).
default_bits = 4096
distinguished_name = req_distinguished_name
string_mask = utf8only
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
# Extension to add when the -x509 option is used.
x509_extensions = v3_ca
[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
countryName = Country Name (2 letter code)
stateOrProvinceName = State or Province Name
localityName = Locality Name
0.organizationName = Organization Name
organizationalUnitName = Organizational Unit Name
commonName = Common Name
emailAddress = Email Address
[ v3_ca ]
# Extensions for a typical CA (\`man x509v3_config\`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ v3_intermediate_ca ]
# Extensions for a typical intermediate CA (\`man x509v3_config\`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ usr_cert ]
# Extensions for client certificates (\`man x509v3_config\`).
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "OpenSSL Generated Client Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection
[ server_cert ]
# Extensions for server certificates (\`man x509v3_config\`).
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
[ crl_ext ]
# Extension for CRLs (\`man x509v3_config\`).
authorityKeyIdentifier=keyid:always
[ ocsp ]
# Extension for OCSP signing certificates (\`man ocsp\`).
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, OCSPSigning
EOM
read -r -d '' OPENSSL_INTERMEDIATE_CFG << EOM
# OpenSSL intermediate CA configuration file.
# Auto-generated.
[ ca ]
# \`man ca\`
default_ca = CA_default
[ CA_default ]
# Directory and file locations.
dir = $INTERMEDIATE_ROOT
certs = \$dir/certs
crl_dir = \$dir/crl
new_certs_dir = \$dir/newcerts
database = \$dir/index.txt
serial = \$dir/serial
RANDFILE = \$dir/private/.rand
# The root key and root certificate.
private_key = \$dir/private/intermediate.key.pem
certificate = \$dir/certs/intermediate.cert.pem
# For certificate revocation lists.
crlnumber = \$dir/crlnumber
crl = \$dir/crl/intermediate.crl.pem
crl_extensions = crl_ext
default_crl_days = 30
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
name_opt = ca_default
cert_opt = ca_default
default_days = 375
preserve = no
policy = policy_loose
# Allow copying extensions from CSRs
copy_extensions = copy
[ policy_strict ]
# The root CA should only sign intermediate certificates that match.
# See the POLICY FORMAT section of \`man ca\`.
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ policy_loose ]
# Allow the intermediate CA to sign a more diverse range of certificates.
# See the POLICY FORMAT section of the \`ca\` man page.
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
# Options for the \`req\` tool (\`man req\`).
default_bits = 4096
distinguished_name = req_distinguished_name
string_mask = utf8only
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
# Extension to add when the -x509 option is used.
x509_extensions = v3_intermediate_ca
[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
countryName = Country Name (2 letter code)
stateOrProvinceName = State or Province Name
localityName = Locality Name
0.organizationName = Organization Name
organizationalUnitName = Organizational Unit Name
commonName = Common Name
emailAddress = Email Address
[ v3_ca ]
# Extensions for a typical CA (\`man x509v3_config\`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ v3_intermediate_ca ]
# Extensions for a typical intermediate CA (\`man x509v3_config\`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ usr_cert ]
# Extensions for client certificates (\`man x509v3_config\`).
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "OpenSSL Generated Client Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection
[ server_cert ]
# Extensions for server certificates (\`man x509v3_config\`).
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
[ crl_ext ]
# Extension for CRLs (\`man x509v3_config\`).
authorityKeyIdentifier=keyid:always
[ ocsp ]
# Extension for OCSP signing certificates (\`man ocsp\`).
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, OCSPSigning
EOM
deployFiles () {
echo "Deploying a Certificate Authority to $CA_ROOT"
echo ''
if [ ! -d "$CA_ROOT" ]; then
# Create directory
mkdir -p "$CA_ROOT"
# Create sub-directories
mkdir -p "$CA_ROOT/private" "$CA_ROOT/csr" "$CA_ROOT/certs" "$CA_ROOT/newcerts"
touch "$CA_ROOT/index.txt"
echo 1000 > "$CA_ROOT/serial"
chmod 700 "$CA_ROOT/private"
echo "$OPENSSL_CA_CFG" > "$CA_ROOT/openssl.cfg"
fi
if [ ! -d "$INTERMEDIATE_ROOT" ]; then
mkdir -p "$INTERMEDIATE_ROOT/private" "$INTERMEDIATE_ROOT/csr" "$INTERMEDIATE_ROOT/certs" "$INTERMEDIATE_ROOT/newcerts"
touch "$INTERMEDIATE_ROOT/index.txt"
echo 1000 > "$INTERMEDIATE_ROOT/serial"
chmod 700 "$INTERMEDIATE_ROOT/private"
echo "$OPENSSL_INTERMEDIATE_CFG" > "$INTERMEDIATE_ROOT/openssl.cfg"
fi
}
generateRootPair () {
############################
# GENERATING ROOT KEY PAIR #
############################
echo '==> Starting the generation of the root key pair.'
echo '==> Please fill in the appropriate information asked.'
echo ''
# Set CWD
cd "$CA_ROOT"
# CA Root Private Key
if [ ! -e "$CA_ROOT/private/ca.key.pem" ]; then
echo '=> Generating CA key.. Please enter a strong passphrase.'
echo ''
openssl genrsa -aes256 -out private/ca.key.pem 4096
else
echo '=> Root CA key exists, skipping..'
fi
if [ ! -e "$CA_ROOT/private/ca.key.pem" ]; then
echo '=x=> Root CA key was not generated!'
return 1
fi
chmod 400 private/ca.key.pem
# CA Root Certificate
if [ ! -e "$CA_ROOT/certs/ca.cert.pem" ]; then
echo '=> Generating CA certificate..'
echo ''
openssl req -config "$CA_ROOT/openssl.cfg" \
-key private/ca.key.pem \
-new -x509 -days 7300 -sha256 -extensions v3_ca \
-out certs/ca.cert.pem
else
echo '=> Root certificate exists, skipping..'
fi
if [ ! -e "$CA_ROOT/certs/ca.cert.pem" ]; then
echo '=x=> Root certificate was not generated!'
return 1
fi
chmod 444 certs/ca.cert.pem
# Print info
openssl x509 -noout -text -in certs/ca.cert.pem
echo '==> Root key pair generated.'
echo ''
}
generateIntermediate () {
if [ ! -e "$CA_ROOT/certs/ca.cert.pem" ]; then
echo '=x=> Could not generate intermediate certificate.'
echo '=x=> CA certificate is missing!'
return 1
fi
####################################
# GENERATING INTERMEDIATE KEY PAIR #
####################################
cd "$CA_ROOT"
echo '==> Starting the generation of the intermediate key pair.'
echo '==> This is used to sign server and user certificates.'
echo '==> Please fill in the appropriate information asked.'
echo ''
if [ ! -e "$CA_ROOT/intermediate/private/intermediate.key.pem" ]; then
# Key
echo '=> Generating intermediate key.. Please enter a strong passphrase.'
echo ''
openssl genrsa -aes256 \
-out intermediate/private/intermediate.key.pem 4096
else
echo '=> Intermediate certificate key exists, skipping..'
fi
if [ ! -e "$CA_ROOT/intermediate/private/intermediate.key.pem" ]; then
echo '=x=> Intermediate certificate key was not generated!'
return 1
fi
chmod 400 intermediate/private/intermediate.key.pem
if [ -e "$CA_ROOT/intermediate/csr/intermediate.csr.pem" ]; then
rm -f intermediate/csr/intermediate.csr.pem
rm -f intermediate/certs/intermediate.cert.pem
fi
# Certificate Signing Request (CSR)
echo '=> Generating intermediate CSR..'
echo ''
openssl req -config "$INTERMEDIATE_ROOT/openssl.cfg" -new -sha256 \
-key intermediate/private/intermediate.key.pem \
-out intermediate/csr/intermediate.csr.pem
if [ ! -e "$CA_ROOT/intermediate/csr/intermediate.csr.pem" ]; then
echo '=x=> Intermediate signing request was not generated!'
return 1
fi
echo '=> Singing the intermediate certificate. Please answer yes in order to continue.'
echo ''
openssl ca -config "$CA_ROOT/openssl.cfg" -extensions v3_intermediate_ca \
-days 3650 -notext -md sha256 \
-in intermediate/csr/intermediate.csr.pem \
-out intermediate/certs/intermediate.cert.pem
if [ ! -e "$CA_ROOT/intermediate/certs/intermediate.cert.pem" ]; then
echo '=x=> Intermediate certificate was not signed!'
return 1
fi
chmod 444 intermediate/certs/intermediate.cert.pem
# Print info
openssl x509 -noout -text \
-in intermediate/certs/intermediate.cert.pem
openssl verify -CAfile certs/ca.cert.pem \
intermediate/certs/intermediate.cert.pem
# Generate Certificate Chain
cat intermediate/certs/intermediate.cert.pem \
certs/ca.cert.pem > intermediate/certs/ca-chain.cert.pem
chmod 444 intermediate/certs/ca-chain.cert.pem
echo '==> Intermediate key pair generated.'
echo "==> Your CA (Certificate Authority) is \"$INTERMEDIATE_ROOT/certs/ca-chain.cert.pem\""
}
createCertificate() {
if [ ! -e "$CA_ROOT" ]; then
echo "=x=> Certificate root was not found."
return 1
elif [ ! -e "$INTERMEDIATE_ROOT/openssl.cfg" ]; then
echo "=x=> Intermediate certificate configuration was not found."
return 1
fi
if [ -z "$1" ]; then
echo "=x=> Please provide a name for this certificate."
return 1
fi
if [ -z "$2" ] || [ "$2" != "server_cert" ] && [ "$2" != "usr_cert" ]; then
echo "=x=> Type must be one of server_cert or usr_cert. If you're unsure about this, use 'usr_cert'."
return 1
fi
cd "$CA_ROOT"
# Custom configuration file
local CSRCFG="$INTERMEDIATE_ROOT/openssl.cfg"
if [ -n $3 ]; then
CSRCFG="$3"
fi
# Generate the key if it does not exist
if [ ! -e "$INTERMEDIATE_ROOT/private/$1.key.pem" ]; then
echo "==> Generating key for \"$1\""
echo ''
openssl genrsa -out "$INTERMEDIATE_ROOT/private/$1.key.pem" 4096
chmod 400 "$INTERMEDIATE_ROOT/private/$1.key.pem"
if [ ! -e "$INTERMEDIATE_ROOT/private/$1.key.pem" ]; then
echo "=x=> Key was not created!"
return 1
fi
fi
echo "==> Generating CSR for \"$1\""
echo ''
openssl req -config "$CSRCFG" \
-key "$INTERMEDIATE_ROOT/private/$1.key.pem" \
-new -sha256 -out "$INTERMEDIATE_ROOT/csr/$1.csr.pem"
if [ ! -e "$INTERMEDIATE_ROOT/csr/$1.csr.pem" ]; then
echo "=x=> CSR was not created!"
return 1
fi
echo "==> Generating certificate for \"$1\""
echo ''
openssl ca -config "$INTERMEDIATE_ROOT/openssl.cfg" \
-extensions "$2" -days 375 -notext -md sha256 \
-in "$INTERMEDIATE_ROOT/csr/$1.csr.pem" \
-out "$INTERMEDIATE_ROOT/certs/$1.cert.pem"
if [ ! -e "$INTERMEDIATE_ROOT/certs/$1.cert.pem" ]; then
echo "=x=> Certificate was not created!"
return 1
fi
chmod 444 "$INTERMEDIATE_ROOT/certs/$1.cert.pem"
echo "==> Done."
echo "==> Key: $INTERMEDIATE_ROOT/private/$1.key.pem"
echo "==> Cert: $INTERMEDIATE_ROOT/certs/$1.cert.pem"
}
revokeCertificate() {
if [ ! -e "$CA_ROOT" ]; then
echo "=x=> Certificate root was not found."
return 1
elif [ ! -e "$INTERMEDIATE_ROOT/openssl.cfg" ]; then
echo "=x=> Intermediate certificate configuration was not found."
return 1
fi
if [ -z "$1" ]; then
echo "=x=> Please provide a name for a certificate to revoke."
return 1
fi
cd "$CA_ROOT"
echo "==> Revoking certificate"
openssl ca -config "$INTERMEDIATE_ROOT/openssl.cfg" \
-revoke "$INTERMEDIATE_ROOT/certs/$1.cert.pem"
echo "==> Done."
}
printInfo () {
if [ ! -e "$CA_ROOT" ]; then
echo "=x=> Certificate root was not found."
return 1
elif [ ! -e "$INTERMEDIATE_ROOT/openssl.cfg" ]; then
echo "=x=> Intermediate certificate configuration was not found."
return 1
fi
if [ -n "$1" ]; then
openssl x509 -noout -text -in "$CA_ROOT/intermediate/certs/$1.cert.pem"
return 0
fi
echo "=> Root certificate"
openssl x509 -noout -text -in "$CA_ROOT/certs/ca.cert.pem"
echo "=> Intermediate certificate"
openssl x509 -noout -text \
-in "$CA_ROOT/intermediate/certs/intermediate.cert.pem"
openssl verify -CAfile "$CA_ROOT/certs/ca.cert.pem" \
"$CA_ROOT/intermediate/certs/intermediate.cert.pem"
echo ""
echo "==> Paths"
echo "Root: $CA_ROOT"
echo "Intermediate: $CA_ROOT/intermediate"
echo ""
echo "Root Key: $CA_ROOT/private/ca.key.pem"
echo "Root Cert: $CA_ROOT/certs/ca.cert.pem"
echo "CA Key: $CA_ROOT/intermediate/private/intermediate.key.pem"
echo "Chain: $CA_ROOT/intermediate/certs/ca-chain.cert.pem"
echo ""
}
printHelp () {
echo "Usage: ./ca <CA Path> info | wizard | root | intm | revoke [<name>] | new [<name>] [ server_cert | usr_cert ] [ openssl.cfg ]"
echo -e " info\t- Print information about your CA"
echo -e " wizard\t- Create a new CA"
echo -e " root\t- Generate root certificate. For use when wizard fails."
echo -e " intm\t- Generate intermediate certificate. For use when wizard fails."
echo -e " new \t- Create a new certificate signed with your CA. Can also be used for renewal."
echo -e " revoke\t- Revoke a certificate by name"
}
set -e
case "$2" in
"info")
printInfo $3
;;
"wizard" | "generate" | "newca")
echo "==> Proceeding with full generation for path $1"
deployFiles
generateRootPair
generateIntermediate
printInfo
;;
"files")
deployFiles
;;
"rootpair" | "root")
generateRootPair
;;
"intermediate" | "intm")
generateIntermediate
;;
"certificate" | "cert" | "new" | "renew")
createCertificate $3 $4 $5
;;
"revoke")
revokeCertificate $3
;;
"help")
printHelp
;;
*)
echo "Usage: ./ca <CA Path> info | wizard | root | intm | revoke [<name>] | new [<name>] [ server_cert | usr_cert ] [ openssl.cfg ]"
;;
esac