Last active
May 30, 2026 19:48
-
-
Save mmontuori/b35867a1f344f3230b6129350dff476b to your computer and use it in GitHub Desktop.
Certificate Authority with Openssl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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