#!/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 . 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 . 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 info | wizard | root | intm | revoke [] | new [] [ 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 info | wizard | root | intm | revoke [] | new [] [ server_cert | usr_cert ] [ openssl.cfg ]" ;; esac