Skip to content

Instantly share code, notes, and snippets.

@mmontuori
Last active May 30, 2026 19:48
Show Gist options
  • Select an option

  • Save mmontuori/b35867a1f344f3230b6129350dff476b to your computer and use it in GitHub Desktop.

Select an option

Save mmontuori/b35867a1f344f3230b6129350dff476b to your computer and use it in GitHub Desktop.
Certificate Authority with Openssl
#!/usr/bin/env bash
CA_DIR="$HOME/ca-private"
CA_PRIVATE_DIR="$CA_DIR/private"
CA_NEWCERTS_DIR="$CA_DIR/newcerts"
CA_CERTS_DIR="$CA_DIR/certs"
CA_CRL_DIR="$CA_DIR/crl"
ORGNAME="The Org Name"
COUNTRY="The two letter country code (US)"
STATE="The state or region"
CITY="The city"
function usage {
echo "usage: `basename $0` [options]"
echo " -h display this help message"
echo " -a create CA Key and Certificate"
echo " -g generate a certificate signing request (must be used with -n)"
echo " -n common name for the certificate signing request (must be used with -g)"
echo " -s sign a certificate request"
echo " -v view content of certificate (must be used with -r)"
echo " -f the file to use with sign, or view operations"
echo " Example: `basename $0` -a # Create CA root key and certificate"
echo " Example: `basename $0` -g -n myserver.local # Generate a certificate signing request for myserver.local"
echo " Example: `basename $0` -s -f myserver.local.csr.pem # Sign the certificate request and create myserver.local.cert.pem"
echo " Example: `basename $0` -v -f myserver.local.cert.pem # View the content of the certificate"
}
while getopts ":h:asvn:gf:" opt; do
case $opt in
a)
create_ca=true
;;
g)
generate_request=true
;;
n)
common_name="$OPTARG"
;;
s)
sign_request=true
;;
f)
file="$OPTARG"
;;
h)
usage
exit
;;
v)
view_cert=true
;;
*)
usage
exit
;;
esac
done
shift $((OPTIND -1))
for ca_dir in "$CA_PRIVATE_DIR" "$CA_NEWCERTS_DIR" "$CA_CERTS_DIR" "$CA_CRL_DIR"; do
if ! test -d "$ca_dir"; then
echo "Creating directory: $ca_dir"
mkdir -p "$ca_dir"
fi
done
if [ "$create_ca" = true ] && ! test -f "${CA_PRIVATE_DIR}/cakey.pem" && ! test -f "${CA_DIR}/cacert.pem"; then
echo "Creating Initial Root CA key and certificate..."
if ! openssl genrsa -aes256 -out ${CA_PRIVATE_DIR}/cakey.pem 4096; then
echo "Failed to create CA private key."
exit 1
fi
if ! openssl req -x509 -new -nodes -key ${CA_PRIVATE_DIR}/cakey.pem -sha256 -days 3560 -out ${CA_DIR}/cacert.pem -subj "/CN=${ORGNAME} CA/C=${COUNTRY}/ST=${STATE}/L=${CITY}/O=${ORGNAME}"; then
echo "Failed to create CA certificate."
exit 1
fi
echo "CA key and certificate created in $CA_DIR."
echo "Secure the CA key and certificate, and do not share the CA key with anyone."
else
if [ "$create_ca" = true ]; then
echo "CA key or certificate already exists at ${CA_DIR}. Skipping creation."
fi
fi
if [ "$generate_request" = true ]; then
if [ -z "$common_name" ]; then
echo "Common name (-n) is required when generating a certificate signing request."
usage
exit 1
fi
if test -f "${CA_CERTS_DIR}/${common_name}.csr"; then
echo "Certificate signing request for common name $common_name already exists at ${CA_CERTS_DIR}/${common_name}.csr. Skipping generation."
echo "If you want to re-generate the certificate signing request, please remove the existing file at ${CA_CERTS_DIR}/${common_name}.csr and try again."
exit 1
fi
echo "Generating certificate signing request for common name: $common_name"
if ! openssl req -new -nodes -out ${CA_CERTS_DIR}/${common_name}.csr -newkey rsa:4096 -keyout ${CA_CERTS_DIR}/${common_name}.key -subj "/CN=$common_name/C=${COUNTRY}/ST=${STATE}/L=${CITY}/O=${ORGNAME}"; then
echo "Failed to generate certificate signing request for common name: $common_name"
exit 1
fi
# create a v3 ext file for SAN properties
cat > ${CA_CERTS_DIR}/${common_name}.v3.ext << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${common_name}
DNS.2 = ${common_name}.local
IP.1 = 192.168.1.1
IP.2 = 192.168.2.1
EOF
echo "Certificate signing request generated: ${CA_CERTS_DIR}/${common_name}.csr"
fi
if [ "$sign_request" = true ]; then
if [ -z "$common_name" ]; then
echo "Common name (-n) is required when generating a certificate signing request."
usage
exit 1
fi
if test -f "${CA_CERTS_DIR}/${common_name}_signed.crt"; then
echo "Signed certificate for common name $common_name already exists at ${CA_CERTS_DIR}/${common_name}_signed.crt. Skipping signing."
echo "If you want to re-sign the certificate, please remove the existing signed certificate at ${CA_CERTS_DIR}/${common_name}_signed.crt and try again."
exit 1
fi
if ! test -f "${CA_CERTS_DIR}/${common_name}.csr"; then
echo "File ${CA_CERTS_DIR}/${common_name}.csr does not exist."
exit 1
fi
echo "Signing certificate request: ${CA_CERTS_DIR}/${common_name}.csr"
if ! openssl x509 -req -in ${CA_CERTS_DIR}/${common_name}.csr -CA ${CA_DIR}/cacert.pem -CAkey ${CA_PRIVATE_DIR}/cakey.pem -CAcreateserial -out ${CA_CERTS_DIR}/${common_name}_signed.crt -days 365 -sha256 -extfile ${CA_CERTS_DIR}/${common_name}.v3.ext; then
echo "Failed to sign certificate request: ${CA_CERTS_DIR}/${common_name}.csr"
exit 1
fi
cat ${CA_DIR}/cacert.pem ${CA_CERTS_DIR}/${common_name}_signed.crt > ${CA_CERTS_DIR}/${common_name}_signed.chain
fi
if [ "$view_cert" = true ]; then
if [ -z "$file" ]; then
echo "File (-f) is required when viewing a certificate."
usage
exit 1
fi
if ! test -f "$file"; then
echo "File $file does not exist."
exit 1
fi
echo "Viewing certificate: $file"
openssl x509 -in "$file" -text -noout
fi
# Import certificate in ubuntu
# Windows: Import ca.cert.pem into the Trusted Root Certification Authorities store.
# macOS: Import it via Keychain Access and double-click the certificate to set its trust settings to Always Trust.
# Linux (Ubuntu/Debian): Copy the file to /usr/local/share/ca-certificates/ca.crt and run sudo update-ca-certificates.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment