#!/bin/sh # Easy-RSA 3 -- A Shell-based CA Utility # # Copyright (C) 2018 by the Open-Source OpenVPN development community. # A full list of contributors can be found in the ChangeLog. # # This code released under version 2 of the GNU GPL; see COPYING and the # Licensing/ directory of this project for full licensing details. # Help/usage output to stdout usage() { # command help: print " Easy-RSA 3 usage and overview USAGE: easyrsa [options] COMMAND [command-options] A list of commands is shown below. To get detailed usage and help for a command, run: ./easyrsa help COMMAND For a listing of options that can be supplied before the command, use: ./easyrsa help options Here is the list of commands available with a short syntax reminder. Use the 'help' command above to get full usage details. init-pki build-ca [ cmd-opts ] gen-dh gen-req [ cmd-opts ] sign-req build-client-full [ cmd-opts ] build-server-full [ cmd-opts ] revoke [cmd-opts] renew [cmd-opts] build-serverClient-full [ cmd-opts ] gen-crl update-db show-req [ cmd-opts ] show-cert [ cmd-opts ] show-ca [ cmd-opts ] import-req export-p7 [ cmd-opts ] export-p12 [ cmd-opts ] set-rsa-pass [ cmd-opts ] set-ec-pass [ cmd-opts ] " # collect/show dir status: err_source="Not defined: vars autodetect failed and no value provided" work_dir="${EASYRSA:-$err_source}" pki_dir="${EASYRSA_PKI:-$err_source}" print "\ DIRECTORY STATUS (commands would take effect on these locations) EASYRSA: $work_dir PKI: $pki_dir " } # => usage() # Detailed command help # When called with no args, calls usage(), otherwise shows help for a command cmd_help() { text="" opts="" case "$1" in init-pki|clean-all) text=" init-pki [ cmd-opts ] Removes & re-initializes the PKI dir for a clean PKI" ;; build-ca) text=" build-ca [ cmd-opts ] Creates a new CA" opts=" nopass - do not encrypt the CA key (default is encrypted) subca - create a sub-CA keypair and request (default is a root CA)" ;; gen-dh) text=" gen-dh Generates DH (Diffie-Hellman) parameters" ;; gen-req) text=" gen-req [ cmd-opts ] Generate a standalone keypair and request (CSR) This request is suitable for sending to a remote CA for signing." opts=" nopass - do not encrypt the private key (default is encrypted)" ;; sign|sign-req) text=" sign-req Sign a certificate request of the defined type. must be a known type such as 'client', 'server', 'serverClient', or 'ca' (or a user-added type.) This request file must exist in the reqs/ dir and have a .req file extension. See import-req below for importing reqs from other sources." ;; build|build-client-full|build-server-full|build-serverClient-full) text=" build-client-full [ cmd-opts ] build-server-full [ cmd-opts ] build-serverClient-full [ cmd-opts ] Generate a keypair and sign locally for a client and/or server This mode uses the as the X509 CN." opts=" nopass - do not encrypt the private key (default is encrypted)" ;; revoke) text=" revoke [reason] Revoke a certificate specified by the filename_base, with an optional revocation reason that is one of: unspecified keyCompromise CACompromise affiliationChanged superseded cessationOfOperation certificateHold";; renew) text=" renew [ cmd-opts ] Renew a certificate specified by the filename_base" opts=" nopass - do not encrypt the private key (default is encrypted)" ;; gen-crl) text=" gen-crl Generate a CRL" ;; update-db) text=" update-db Update the index.txt database This command will use the system time to update the status of issued certificates." ;; show-req|show-cert) text=" show-req [ cmd-opts ] show-cert [ cmd-opts ] Shows details of the req or cert referenced by filename_base Human-readable output is shown, including any requested cert options when showing a request." opts=" full - show full req/cert info, including pubkey/sig data" ;; show-ca) text=" show-ca [ cmd-opts ] Shows details of the CA cert Human-readable output is shown." opts=" full - show full cert info, including pubkey/sig data" ;; import-req) text=" import-req Import a certificate request from a file This will copy the specified file into the reqs/ dir in preparation for signing. The is the filename base to create. Example usage: import-req /some/where/bob_request.req bob" ;; export-p12) text=" export-p12 [ cmd-opts ] Export a PKCS#12 file with the keypair specified by " opts=" noca - do not include the ca.crt file in the PKCS12 output nokey - do not include the private key in the PKCS12 output" ;; export-p7) text=" export-p7 [ cmd-opts ] Export a PKCS#7 file with the pubkey specified by " opts=" noca - do not include the ca.crt file in the PKCS7 output" ;; set-rsa-pass|set-ec-pass) text=" set-rsa-pass [ cmd-opts ] set-ec-pass [ cmd-opts ] Set a new passphrase on an RSA or EC key for the listed ." opts=" nopass - use no password and leave the key unencrypted file - (advanced) treat the file as a raw path, not a short-name" ;; altname|subjectaltname|san) text=" --subject-alt-name=SAN_FORMAT_STRING This global option adds a subjectAltName to the request or issued certificate. It MUST be in a valid format accepted by openssl or req/cert generation will fail. Note that including multiple such names requires them to be comma-separated; further invocations of this option will REPLACE the value. Examples of the SAN_FORMAT_STRING shown below: DNS:alternate.example.net DNS:primary.example.net,DNS:alternate.example.net IP:203.0.113.29 email:alternate@example.net" ;; options) opt_usage ;; "") usage ;; *) text=" Unknown command: '$1' (try without commands for a list of commands)" ;; esac # display the help text print "$text" [ -n "$opts" ] && print " cmd-opts is an optional set of command options from this list: $opts" } # => cmd_help() # Options usage opt_usage() { print " Easy-RSA Global Option Flags The following options may be provided before the command. Options specified at runtime override env-vars and any 'vars' file in use. Unless noted, non-empty values to options are mandatory. General options: --batch : set automatic (no-prompts when possible) mode --pki-dir=DIR : declares the PKI directory --vars=FILE : define a specific 'vars' file to use for Easy-RSA config Certificate & Request options: (these impact cert/req field values) --days=# : sets the signing validity to the specified number of days --digest=ALG : digest to use in the requests & certificates --dn-mode=MODE : DN mode to use (cn_only or org) --keysize=# : size in bits of keypair to generate --req-cn=NAME : default CN to use --subca-len=# : path length of signed sub-CA certs; must be >= 0 if used --subject-alt-name : Add a subjectAltName. For more info and syntax, see: ./easyrsa help altname --use-algo=ALG : crypto alg to use: choose rsa (default) or ec --curve=NAME : for elliptic curve, sets the named curve to use --copy-ext : Copy included request X509 extensions (namely subjAltName Organizational DN options: (only used with the 'org' DN mode) (values may be blank for org DN options) --req-c=CC : country code (2-letters) --req-st=NAME : State/Province --req-city=NAME : City/Locality --req-org=NAME : Organization --req-email=NAME : Email addresses --req-ou=NAME : Organizational Unit Deprecated features: --ns-cert=YESNO : yes or no to including deprecated NS extensions --ns-comment=COMMENT : NS comment to include (value may be blank) " } # => opt_usage() # Wrapper around printf - clobber print since it's not POSIX anyway # shellcheck disable=SC1117 print() { printf "%s\n" "$*"; } # Exit fatally with a message to stderr # present even with EASYRSA_BATCH as these are fatal problems die() { print " Easy-RSA error: $1" 1>&2 clean_temp; prog_exit "${2:-1}" } # => die() # non-fatal warning output warn() { [ ! "$EASYRSA_BATCH" ] && \ print " $1" 1>&2 } # => warn() # informational notices to stdout notice() { [ ! "$EASYRSA_BATCH" ] && \ print " $1" } # => notice() # yes/no case-insensitive match (operates on stdin pipe) # Returns 0 when input contains yes, 1 for no, 2 for no match # If both strings are present, returns 1; first matching line returns. awk_yesno() { #shellcheck disable=SC2016 awkscript=' BEGIN {IGNORECASE=1; r=2} { if(match($0,"no")) {r=1; exit} if(match($0,"yes")) {r=0; exit} } END {exit r}' awk "$awkscript" } # => awk_yesno() # intent confirmation helper func # returns without prompting in EASYRSA_BATCH confirm() { [ "$EASYRSA_BATCH" ] && return prompt="$1" value="$2" msg="$3" input="" print " $msg Type the word '$value' to continue, or any other input to abort." printf %s " $prompt" #shellcheck disable=SC2162 read input [ "$input" = "$value" ] && return notice "Aborting without confirmation." exit 9 } # => confirm() # remove temp files clean_temp() { for f in "$EASYRSA_TEMP_CONF" "$EASYRSA_TEMP_EXT" \ "$EASYRSA_TEMP_FILE_2" "$EASYRSA_TEMP_FILE_3" "$EASYRSA_TEMP_FILE_4" do [ -f "$f" ] && rm "$f" 2>/dev/null done } # => clean_temp() prog_exit() { ESTAT=0 [ -n "$1" ] && ESTAT=$1 (stty echo 2>/dev/null) || set -o echo echo "" # just to get a clean line exit "$ESTAT" } # => prog_exit() # Make LibreSSL safe config file from OpenSSL config file make_ssl_config() { sed \ -e "s\`ENV::EASYRSA\`EASYRSA\`g" \ -e "s\`\$dir\`$EASYRSA_PKI\`g" \ -e "s\`\$EASYRSA_PKI\`$EASYRSA_PKI\`g" \ -e "s\`\$EASYRSA_CERT_EXPIRE\`$EASYRSA_CERT_EXPIRE\`g" \ -e "s\`\$EASYRSA_CRL_DAYS\`$EASYRSA_CRL_DAYS\`g" \ -e "s\`\$EASYRSA_DIGEST\`$EASYRSA_DIGEST\`g" \ -e "s\`\$EASYRSA_KEY_SIZE\`$EASYRSA_KEY_SIZE\`g" \ -e "s\`\$EASYRSA_DIGEST\`$EASYRSA_DIGEST\`g" \ -e "s\`\$EASYRSA_DN\`$EASYRSA_DN\`g" \ -e "s\`\$EASYRSA_REQ_COUNTRY\`$EASYRSA_REQ_COUNTRY\`g" \ -e "s\`\$EASYRSA_REQ_PROVINCE\`$EASYRSA_REQ_PROVINCE\`g" \ -e "s\`\$EASYRSA_REQ_CITY\`$EASYRSA_REQ_CITY\`g" \ -e "s\`\$EASYRSA_REQ_ORG\`$EASYRSA_REQ_ORG\`g" \ -e "s\`\$EASYRSA_REQ_OU\`$EASYRSA_REQ_OU\`g" \ -e "s\`\$EASYRSA_REQ_CN\`$EASYRSA_REQ_CN\`g" \ -e "s\`\$EASYRSA_REQ_EMAIL\`$EASYRSA_REQ_EMAIL\`g" \ "$EASYRSA_SSL_CONF" > "$EASYRSA_SAFE_CONF" || die "\ Failed to update $EASYRSA_SAFE_CONF" } # => make_ssl_config() vars_source_check() { # Check for defined EASYRSA_PKI [ -n "$EASYRSA_PKI" ] || die "\ EASYRSA_PKI env-var undefined" } # => vars_source_check() # Verify supplied curve exists and generate curve file if needed verify_curve() { if ! "$EASYRSA_OPENSSL" ecparam -name "$EASYRSA_CURVE" > /dev/null; then die "\ Curve $EASYRSA_CURVE not found. Run openssl ecparam -list_curves to show a list of supported curves." fi # Check that the ecparams dir exists [ -d "$EASYRSA_EC_DIR" ] || mkdir "$EASYRSA_EC_DIR" || die "\ Failed creating ecparams dir (permissions?) at: $EASYRSA_EC_DIR" # Check that the required ecparams file exists out="$EASYRSA_EC_DIR/${EASYRSA_CURVE}.pem" [ -f "$out" ] && return 0 "$EASYRSA_OPENSSL" ecparam -name "$EASYRSA_CURVE" -out "$out" || die "\ Failed to generate ecparam file (permissions?) when writing to: $out" # Explicitly return success for caller return 0 } verify_ssl_lib () { # make safessl-easyrsa.cnf make_ssl_config # Verify EASYRSA_OPENSSL command gives expected output if [ -z "$EASYRSA_SSL_OK" ]; then val="$("$EASYRSA_OPENSSL" version)" case "${val%% *}" in OpenSSL|LibreSSL) notice "\ Using SSL: $EASYRSA_OPENSSL $("$EASYRSA_OPENSSL" version)" ;; *) die "\ Missing or invalid OpenSSL Expected to find openssl command at: $EASYRSA_OPENSSL" ;; esac fi EASYRSA_SSL_OK=1 # Verify EASYRSA_SSL_CONF file exists [ -f "$EASYRSA_SSL_CONF" ] || die "\ The OpenSSL config file cannot be found. Expected location: $EASYRSA_SSL_CONF" } # => verify_ssl_lib () # Basic sanity-check of PKI init and complain if missing verify_pki_init() { help_note="Run easyrsa without commands for usage and command help." # check that the pki dir exists vars_source_check [ -d "$EASYRSA_PKI" ] || die "\ EASYRSA_PKI does not exist (perhaps you need to run init-pki)? Expected to find the EASYRSA_PKI at: $EASYRSA_PKI $help_note" # verify expected dirs present: for i in private reqs; do [ -d "$EASYRSA_PKI/$i" ] || die "\ Missing expected directory: $i (perhaps you need to run init-pki?) $help_note" done # verify ssl lib verify_ssl_lib } # => verify_pki_init() # Verify core CA files present verify_ca_init() { help_note="Run without commands for usage and command help." # First check the PKI has been initialized verify_pki_init # verify expected files present: for i in serial index.txt ca.crt private/ca.key; do if [ ! -f "$EASYRSA_PKI/$i" ]; then [ "$1" = "test" ] && return 1 die "\ Missing expected CA file: $i (perhaps you need to run build-ca?) $help_note" fi done # When operating in 'test' mode, return success. # test callers don't care about CA-specific dir structure [ "$1" = "test" ] && return 0 # verify expected CA-specific dirs: for i in issued certs_by_serial \ revoked/certs_by_serial revoked/private_by_serial revoked/reqs_by_serial \ renewed/certs_by_serial renewed/private_by_serial renewed/reqs_by_serial ; do [ -d "$EASYRSA_PKI/$i" ] || die "\ Missing expected CA dir: $i (perhaps you need to run build-ca?) $help_note" done # explicitly return success for callers return 0 } # => verify_ca_init() # init-pki backend: init_pki() { # If EASYRSA_PKI exists, confirm before we rm -rf (skiped with EASYRSA_BATCH) if [ -e "$EASYRSA_PKI" ]; then confirm "Confirm removal: " "yes" " WARNING!!! You are about to remove the EASYRSA_PKI at: $EASYRSA_PKI and initialize a fresh PKI here." # now remove it: rm -rf "$EASYRSA_PKI" || die "Removal of PKI dir failed. Check/correct errors above" fi # new dirs: for i in private reqs; do mkdir -p "$EASYRSA_PKI/$i" || die "Failed to create PKI file structure (permissions?)" done if [ ! -f "$EASYRSA_SSL_CONF" ] && [ -f "$EASYRSA/openssl-easyrsa.cnf" ]; then cp "$EASYRSA/openssl-easyrsa.cnf" "$EASYRSA_SSL_CONF" fi notice "\ init-pki complete; you may now create a CA or requests. Your newly created PKI dir is: $EASYRSA_PKI " return 0 } # => init_pki() hide_read_pass() { (stty -echo 2>/dev/null) || set +o echo read -r "$@" (stty echo 2>/dev/null) || set -o echo } # => hide_read_pass() # build-ca backend: build_ca() { opts="" sub_ca="" nopass="" crypto="-aes256" crypto_opts="" while [ -n "$1" ]; do case "$1" in subca) sub_ca=1 ;; nopass) nopass=1 ;; *) warn "Ignoring unknown command option: '$1'" ;; esac shift done verify_pki_init [ "$EASYRSA_ALGO" = "ec" ] && verify_curve # setup for the simpler sub-CA situation and overwrite with root-CA if needed: out_file="$EASYRSA_PKI/reqs/ca.req" out_key="$EASYRSA_PKI/private/ca.key" if [ ! $sub_ca ]; then out_file="$EASYRSA_PKI/ca.crt" opts="$opts -x509 -days $EASYRSA_CA_EXPIRE " fi # Test for existing CA, and complain if already present if verify_ca_init test; then die "\ Unable to create a CA as you already seem to have one set up. If you intended to start a new CA, run init-pki first." fi # If a private key exists here, a sub-ca was created but not signed. # Notify the user and require a signed ca.crt or a init-pki: [ -f "$out_key" ] && \ die "\ A CA private key exists but no ca.crt is found in your PKI dir of: $EASYRSA_PKI Refusing to create a new CA keypair as this operation would overwrite your current CA keypair. If you intended to start a new CA, run init-pki first." # create necessary files and dirs: err_file="Unable to create necessary PKI files (permissions?)" for i in issued certs_by_serial \ revoked/certs_by_serial revoked/private_by_serial revoked/reqs_by_serial \ renewed/certs_by_serial renewed/private_by_serial renewed/reqs_by_serial; do mkdir -p "$EASYRSA_PKI/$i" || die "$err_file" done printf "" > "$EASYRSA_PKI/index.txt" || die "$err_file" print "01" > "$EASYRSA_PKI/serial" || die "$err_file" # Default CN only when not in global EASYRSA_BATCH mode: # shellcheck disable=SC2015 [ "$EASYRSA_BATCH" ] && opts="$opts -batch" || export EASYRSA_REQ_CN="Easy-RSA CA" out_key_tmp="$(mktemp "$out_key.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_2="$out_key_tmp" out_file_tmp="$(mktemp "$out_file.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_3="$out_file_tmp" # Get password from user if necessary if [ ! $nopass ]; then out_key_pass_tmp="$(mktemp)"; EASYRSA_TEMP_FILE_4="$out_key_pass_tmp" echo printf "Enter New CA Key Passphrase: " hide_read_pass kpass echo printf "Re-Enter New CA Key Passphrase: " hide_read_pass kpass2 echo # shellcheck disable=2154 if [ "$kpass" = "$kpass2" ]; then printf "%s" "$kpass" > "$out_key_pass_tmp" else die "Passphrases do not match." fi fi # create the CA key using AES256 [ ! $nopass ] && crypto_opts="$crypto -passout file:$out_key_pass_tmp" if [ "$EASYRSA_ALGO" = "rsa" ]; then #shellcheck disable=SC2086 "$EASYRSA_OPENSSL" genrsa -out "$out_key_tmp" $crypto_opts "$EASYRSA_ALGO_PARAMS" elif [ "$EASYRSA_ALGO" = "ec" ]; then #shellcheck disable=SC2086 "$EASYRSA_OPENSSL" ecparam -in "$EASYRSA_ALGO_PARAMS" -genkey | \ "$EASYRSA_OPENSSL" ec -out "$out_key_tmp" $crypto_opts fi # make safessl-easyrsa.cnf make_ssl_config # create the CA keypair: [ ! $nopass ] && crypto_opts="-passin file:$out_key_pass_tmp" #shellcheck disable=SC2086 "$EASYRSA_OPENSSL" req -utf8 -new -key "$out_key_tmp" \ -config "$EASYRSA_SAFE_CONF" -keyout "$out_key_tmp" -out "$out_file_tmp" $crypto_opts $opts || \ die "Failed to build the CA" mv "$out_key_tmp" "$out_key"; EASYRSA_TEMP_FILE_2= mv "$out_file_tmp" "$out_file"; EASYRSA_TEMP_FILE_3= [ -f "$out_key_pass_tmp" ] && rm "$out_key_pass_tmp" && EASYRSA_TEMP_FILE_4= # Success messages if [ $sub_ca ]; then notice "\ NOTE: Your sub-CA request is at $out_file and now must be sent to your parent CA for signing. Place your resulting cert at $EASYRSA_PKI/ca.crt prior to signing operations. " else notice "\ CA creation complete and you may now import and sign cert requests. Your new CA certificate file for publishing is at: $out_file " fi return 0 } # => build_ca() # gen-dh backend: gen_dh() { verify_pki_init out_file="$EASYRSA_PKI/dh.pem" "$EASYRSA_OPENSSL" dhparam -out "$out_file" "$EASYRSA_KEY_SIZE" || \ die "Failed to build DH params" notice "\ DH parameters of size $EASYRSA_KEY_SIZE created at $out_file " return 0 } # => gen_dh() # gen-req backend: gen_req() { # pull filename base and use as default interactive CommonName: [ -n "$1" ] || die "\ Error: gen-req must have a file base as the first argument. Run easyrsa without commands for usage and commands." key_out="$EASYRSA_PKI/private/$1.key" req_out="$EASYRSA_PKI/reqs/$1.req" [ ! "$EASYRSA_BATCH" ] && EASYRSA_REQ_CN="$1" shift # function opts support opts= while [ -n "$1" ]; do case "$1" in nopass) opts="$opts -nodes" ;; # batch flag supports internal callers needing silent operation batch) EASYRSA_BATCH=1 ;; *) warn "Ignoring unknown command option: '$1'" ;; esac shift done verify_pki_init [ "$EASYRSA_ALGO" = "ec" ] && verify_curve # don't wipe out an existing private key without confirmation [ -f "$key_out" ] && confirm "Confirm key overwrite: " "yes" "\ WARNING!!! An existing private key was found at $key_out Continuing with key generation will replace this key." # When EASYRSA_EXTRA_EXTS is defined, append it to openssl's [req] section: if [ -n "$EASYRSA_EXTRA_EXTS" ]; then # Setup & insert the extra ext data keyed by a magic line extra_exts=" req_extensions = req_extra [ req_extra ] $EASYRSA_EXTRA_EXTS" #shellcheck disable=SC2016 awkscript=' {if ( match($0, "^#%EXTRA_EXTS%") ) { while ( getline<"/dev/stdin" ) {print} next } {print} }' print "$extra_exts" | \ awk "$awkscript" "$EASYRSA_SSL_CONF" \ > "$EASYRSA_TEMP_CONF" \ || die "Copying SSL config to temp file failed" # Use this new SSL config for the rest of this function EASYRSA_SSL_CONF="$EASYRSA_TEMP_CONF" fi # make safessl-easyrsa.cnf make_ssl_config key_out_tmp="$(mktemp "$key_out.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_2="$key_out_tmp" req_out_tmp="$(mktemp "$req_out.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_3="$req_out_tmp" # generate request [ $EASYRSA_BATCH ] && opts="$opts -batch" # shellcheck disable=2086,2148 "$EASYRSA_OPENSSL" req -utf8 -new -newkey "$EASYRSA_ALGO":"$EASYRSA_ALGO_PARAMS" \ -config "$EASYRSA_SAFE_CONF" -keyout "$key_out_tmp" -out "$req_out_tmp" $opts \ || die "Failed to generate request" mv "$key_out_tmp" "$key_out"; EASYRSA_TEMP_FILE_2= mv "$req_out_tmp" "$req_out"; EASYRSA_TEMP_FILE_3= notice "\ Keypair and certificate request completed. Your files are: req: $req_out key: $key_out " return 0 } # => gen_req() # common signing backend sign_req() { crt_type="$1" opts="" req_in="$EASYRSA_PKI/reqs/$2.req" crt_out="$EASYRSA_PKI/issued/$2.crt" # Randomize Serial number i="" serial="" check_serial="" for i in 1 2 3 4 5; do "$EASYRSA_OPENSSL" rand -hex -out "$EASYRSA_PKI/serial" 16 serial="$(cat "$EASYRSA_PKI/serial")" check_serial="$("$EASYRSA_OPENSSL" ca -config "$EASYRSA_SSL_CONF" -status "$serial" 2>&1)" case "$check_serial" in *"not present in db"*) break ;; *) continue ;; esac done # Support batch by internal caller: [ "$3" = "batch" ] && EASYRSA_BATCH=1 verify_ca_init # Check argument sanity: [ -n "$2" ] || die "\ Incorrect number of arguments provided to sign-req: expected 2, got $# (see command help for usage)" # Cert type must exist under the EASYRSA_EXT_DIR [ -r "$EASYRSA_EXT_DIR/$crt_type" ] || die "\ Unknown cert type '$crt_type'" # Request file must exist [ -f "$req_in" ] || die "\ No request found for the input: '$2' Expected to find the request at: $req_in" # Confirm input is a cert req verify_file req "$req_in" || die "\ The certificate request file is not in a valid X509 request format. Offending file: $req_in" # Display the request subject in an easy-to-read format # Confirm the user wishes to sign this request confirm "Confirm request details: " "yes" " You are about to sign the following certificate. Please check over the details shown below for accuracy. Note that this request has not been cryptographically verified. Please be sure it came from a trusted source or that you have verified the request checksum with the sender. Request subject, to be signed as a $crt_type certificate for $EASYRSA_CERT_EXPIRE days: $(display_dn req "$req_in") " # => confirm end # Generate the extensions file for this cert: { # Append first any COMMON file (if present) then the cert-type extensions cat "$EASYRSA_EXT_DIR/COMMON" cat "$EASYRSA_EXT_DIR/$crt_type" # copy req extensions [ "$EASYRSA_CP_EXT" ] && print "copy_extensions = copy" # Support a dynamic CA path length when present: [ "$crt_type" = "ca" ] && [ -n "$EASYRSA_SUBCA_LEN" ] && \ print "basicConstraints = CA:TRUE, pathlen:$EASYRSA_SUBCA_LEN" # Deprecated Netscape extension support, if enabled if print "$EASYRSA_NS_SUPPORT" | awk_yesno; then [ -n "$EASYRSA_NS_COMMENT" ] && \ print "nsComment = \"$EASYRSA_NS_COMMENT\"" case "$crt_type" in serverClient) print "nsCertType = serverClient" ;; server) print "nsCertType = server" ;; client) print "nsCertType = client" ;; ca) print "nsCertType = sslCA" ;; esac fi # If type is server and no subjectAltName was requested, # add one to the extensions file if [ "$crt_type" = 'server' ]; then echo "$EASYRSA_EXTRA_EXTS" | grep -q subjectAltName || default_server_san "$req_in" fi # Add any advanced extensions supplied by env-var: [ -n "$EASYRSA_EXTRA_EXTS" ] && print "$EASYRSA_EXTRA_EXTS" : # needed to keep die from inherting the above test } > "$EASYRSA_TEMP_EXT" || die "\ Failed to create temp extension file (bad permissions?) at: $EASYRSA_TEMP_EXT" # make safessl-easyrsa.cnf make_ssl_config # sign request # shellcheck disable=SC2086 crt_out_tmp="$(mktemp "$crt_out.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_2="$crt_out_tmp" "$EASYRSA_OPENSSL" ca -utf8 -in "$req_in" -out "$crt_out_tmp" -config "$EASYRSA_SAFE_CONF" \ -extfile "$EASYRSA_TEMP_EXT" -days "$EASYRSA_CERT_EXPIRE" -batch $opts \ || die "signing failed (openssl output above may have more detail)" mv "$crt_out_tmp" "$crt_out"; EASYRSA_TEMP_FILE_2= notice "\ Certificate created at: $crt_out " return 0 } # => sign_req() # common build backend # used to generate+sign in 1 step build_full() { verify_ca_init # pull filename base: [ -n "$2" ] || die "\ Error: didn't find a file base name as the first argument. Run easyrsa without commands for usage and commands." crt_type="$1" name="$2" req_out="$EASYRSA_PKI/reqs/$2.req" key_out="$EASYRSA_PKI/private/$2.key" crt_out="$EASYRSA_PKI/issued/$2.crt" shift 2 # function opts support req_opts= while [ -n "$1" ]; do case "$1" in nopass) req_opts="$req_opts nopass" ;; *) warn "Ignoring unknown command option: '$1'" ;; esac shift done # abort on existing req/key/crt files err_exists="\ file already exists. Aborting build to avoid overwriting this file. If you wish to continue, please use a different name or remove the file. Matching file found at: " [ -f "$req_out" ] && die "Request $err_exists $req_out" [ -f "$key_out" ] && die "Key $err_exists $key_out" [ -f "$crt_out" ] && die "Certificate $err_exists $crt_out" # create request EASYRSA_REQ_CN="$name" #shellcheck disable=SC2086 gen_req "$name" batch $req_opts # Sign it sign_req "$crt_type" "$name" batch } # => build_full() # revoke backend revoke() { verify_ca_init # pull filename base: [ -n "$1" ] || die "\ Error: didn't find a file base name as the first argument. Run easyrsa without commands for usage and command help." crt_in="$EASYRSA_PKI/issued/$1.crt" opts="" if [ "$2" ]; then opts="$opts -crl_reason $2" fi verify_file x509 "$crt_in" || die "\ Unable to revoke as the input file is not a valid certificate. Unexpected input in file: $crt_in" # confirm operation by displaying DN: confirm "Continue with revocation: " "yes" " Please confirm you wish to revoke the certificate with the following subject: $(display_dn x509 "$crt_in") " # => confirm end # referenced cert must exist: [ -f "$crt_in" ] || die "\ Unable to revoke as no certificate was found. Certificate was expected at: $crt_in" # make safessl-easyrsa.cnf make_ssl_config # shellcheck disable=SC2086 "$EASYRSA_OPENSSL" ca -utf8 -revoke "$crt_in" -config "$EASYRSA_SAFE_CONF" $opts || die "\ Failed to revoke certificate: revocation command failed." # move revoked files so we can reissue certificates with the same name move_revoked "$1" notice "\ IMPORTANT!!! Revocation was successful. You must run gen-crl and upload a CRL to your infrastructure in order to prevent the revoked cert from being accepted. " # => notice end return 0 } #= revoke() # move-revoked # moves revoked certificates to an alternative folder # allows reissuing certificates with the same name move_revoked() { verify_ca_init [ -n "$1" ] || die "\ Error: didn't find a file base name as the first argument. Run easyrsa without commands for usage and command help." crt_in="$EASYRSA_PKI/issued/$1.crt" key_in="$EASYRSA_PKI/private/$1.key" req_in="$EASYRSA_PKI/reqs/$1.req" verify_file x509 "$crt_in" || die "\ Unable to move revoked input file. The file is not a valid certificate. Unexpected input in file: $crt_in" verify_file req "$req_in" || die "\ Unable to move request. The file is not a valid request. Unexpected input in file: $req_in" # get the serial number of the certificate -> serial=XXXX cert_serial="$("$EASYRSA_OPENSSL" x509 -in "$crt_in" -noout -serial)" # remove the serial= part -> we only need the XXXX part cert_serial=${cert_serial##*=} crt_by_serial="$EASYRSA_PKI/certs_by_serial/$cert_serial.pem" crt_by_serial_revoked="$EASYRSA_PKI/revoked/certs_by_serial/$cert_serial.crt" key_by_serial_revoked="$EASYRSA_PKI/revoked/private_by_serial/$cert_serial.key" req_by_serial_revoked="$EASYRSA_PKI/revoked/reqs_by_serial/$cert_serial.req" # move crt, key and req file to revoked folders mv "$crt_in" "$crt_by_serial_revoked" mv "$req_in" "$req_by_serial_revoked" # only move the key if we have it if [ -e "$key_in" ] then mv "$key_in" "$key_by_serial_revoked" fi # move the rest of the files (p12, p7, ...) # shellcheck disable=SC2231 for file in $EASYRSA_PKI/private/$1\.??? do # get file extension file_ext="${file##*.}" [ -f "$file" ] && mv "$file" "$EASYRSA_PKI/revoked/private_by_serial/$cert_serial.$file_ext" done # remove the dublicate certificate in the certs_by_serial folder rm "$crt_by_serial" return 0 } #= move_revoked() # renew backend renew() { verify_ca_init # pull filename base: [ -n "$1" ] || die "\ Error: didn't find a file base name as the first argument. Run easyrsa without commands for usage and command help." crt_in="$EASYRSA_PKI/issued/$1.crt" opts="" if [ "$2" ]; then opts="$2" fi verify_file x509 "$crt_in" || die "\ Unable to renew as the input file is not a valid certificate. Unexpected input in file: $crt_in" # confirm operation by displaying DN: confirm "Continue with renew: " "yes" " Please confirm you wish to renew the certificate with the following subject: $(display_dn x509 "$crt_in") " # => confirm end # referenced cert must exist: [ -f "$crt_in" ] || die "\ Unable to renew as no certificate was found. Certificate was expected at: $crt_in" # make safessl-easyrsa.cnf make_ssl_config # Check if old cert is expired or expires within 30 days expire_date=$( "$EASYRSA_OPENSSL" x509 -in "$crt_in" -noout -enddate | sed 's/^notAfter=//' ) case $(uname) in "Darwin"|*"BSD") expire_date=$(date -j -f '%b %d %T %Y %Z' "$expire_date" +%s) allow_renew_date=$(date -j -v"+${EASYRSA_CERT_RENEW}d" +%s) ;; *) # This works on Windows, too, since uname doesn't exist and this is catch-all expire_date=$(date -d "$expire_date" +%s) allow_renew_date=$(date -d "+${EASYRSA_CERT_RENEW}day" +%s) ;; esac [ "$expire_date" -lt "$allow_renew_date" ] || die "\ Certificate expires in more than $EASYRSA_CERT_RENEW days. Renewal not allowed." # Extract certificate usage from old cert cert_ext_key_usage=$( "$EASYRSA_OPENSSL" x509 -in "$crt_in" -noout -text | sed -n "/X509v3 Extended Key Usage:/{n;s/^ *//g;p;}" ) case $cert_ext_key_usage in "TLS Web Client Authentication") cert_type=client ;; "TLS Web Server Authentication") cert_type=server ;; "TLS Web Server Authentication, TLS Web Client Authentication") cert_type=serverClient ;; esac # Use SAN from --subject-alt-name if set else use SAN from old cert echo "$EASYRSA_EXTRA_EXTS" | grep -q subjectAltName || \ { san=$( "$EASYRSA_OPENSSL" x509 -in "$crt_in" -noout -text | sed -n "/X509v3 Subject Alternative Name:/{n;s/ //g;p;}" ) [ -n "$san" ] && export EASYRSA_EXTRA_EXTS="\ $EASYRSA_EXTRA_EXTS subjectAltName = $san" } # move renewed files so we can reissue certificate with the same name # FIXME: Modify revoke() to also work on the renewed certs subdir move_renewed "$1" # renew certificate # shellcheck disable=SC2086 build_full $cert_type $1 $opts || die "\ Failed to renew certificate: renew command failed." notice "\ IMPORTANT!!! Renew was successful. You may want to revoke the old certificate once the new one has been deployed. " # => notice end return 0 } #= renew() # move-renewed # moves renewed certificates to an alternative folder # allows reissuing certificates with the same name move_renewed() { verify_ca_init [ -n "$1" ] || die "\ Error: didn't find a file base name as the first argument. Run easyrsa without commands for usage and command help." crt_in="$EASYRSA_PKI/issued/$1.crt" key_in="$EASYRSA_PKI/private/$1.key" req_in="$EASYRSA_PKI/reqs/$1.req" verify_file x509 "$crt_in" || die "\ Unable to move renewed input file. The file is not a valid certificate. Unexpected input in file: $crt_in" verify_file req "$req_in" || die "\ Unable to move request. The file is not a valid request. Unexpected input in file: $req_in" # get the serial number of the certificate -> serial=XXXX cert_serial="$("$EASYRSA_OPENSSL" x509 -in "$crt_in" -noout -serial)" # remove the serial= part -> we only need the XXXX part cert_serial=${cert_serial##*=} crt_by_serial="$EASYRSA_PKI/certs_by_serial/$cert_serial.pem" crt_by_serial_renewed="$EASYRSA_PKI/renewed/certs_by_serial/$cert_serial.crt" key_by_serial_renewed="$EASYRSA_PKI/renewed/private_by_serial/$cert_serial.key" req_by_serial_renewed="$EASYRSA_PKI/renewed/reqs_by_serial/$cert_serial.req" # move crt, key and req file to renewed folders mv "$crt_in" "$crt_by_serial_renewed" mv "$req_in" "$req_by_serial_renewed" # only move the key if we have it if [ -e "$key_in" ] then mv "$key_in" "$key_by_serial_renewed" fi # move the rest of the files (p12, p7, ...) # shellcheck disable=SC2231 for file in $EASYRSA_PKI/private/$1\.??? do # get file extension file_ext="${file##*.}" [ -f "$file" ] && mv "$file" "$EASYRSA_PKI/renewed/private_by_serial/$cert_serial.$file_ext" done # remove the duplicate certificate in the certs_by_serial folder rm "$crt_by_serial" return 0 } #= move_renewed() # gen-crl backend gen_crl() { verify_ca_init # make safessl-easyrsa.cnf make_ssl_config out_file="$EASYRSA_PKI/crl.pem" out_file_tmp="$(mktemp "$out_file.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_2="$out_file_tmp" "$EASYRSA_OPENSSL" ca -utf8 -gencrl -out "$out_file_tmp" -config "$EASYRSA_SAFE_CONF" || die "\ CRL Generation failed. " mv "$out_file_tmp" "$out_file"; EASYRSA_TEMP_FILE_2= notice "\ An updated CRL has been created. CRL file: $out_file " return 0 } # => gen_crl() # import-req backend import_req() { verify_pki_init # pull passed paths in_req="$1" short_name="$2" out_req="$EASYRSA_PKI/reqs/$2.req" [ -n "$short_name" ] || die "\ Unable to import: incorrect command syntax. Run easyrsa without commands for usage and command help." verify_file req "$in_req" || die "\ The input file does not appear to be a certificate request. Aborting import. Offending file: $in_req" # destination must not exist [ -f "$out_req" ] && die "\ Unable to import the request as the destination file already exists. Please choose a different name for your imported request file. Existing file at: $out_req" # now import it cp "$in_req" "$out_req" notice "\ The request has been successfully imported with a short name of: $short_name You may now use this name to perform signing operations on this request. " return 0 } # => import_req() # export pkcs#12 or pkcs#7 export_pkcs() { pkcs_type="$1" shift [ -n "$1" ] || die "\ Unable to export p12: incorrect command syntax. Run easyrsa without commands for usage and command help." short_name="$1" crt_in="$EASYRSA_PKI/issued/$1.crt" key_in="$EASYRSA_PKI/private/$1.key" crt_ca="$EASYRSA_PKI/ca.crt" shift verify_pki_init # opts support want_ca=1 want_key=1 while [ -n "$1" ]; do case "$1" in noca) want_ca="" ;; nokey) want_key="" ;; *) warn "Ignoring unknown command option: '$1'" ;; esac shift done pkcs_opts= if [ $want_ca ]; then verify_file x509 "$crt_ca" || die "\ Unable to include CA cert in the $pkcs_type output (missing file, or use noca option.) Missing file expected at: $crt_ca" pkcs_opts="$pkcs_opts -certfile $crt_ca" fi # input files must exist verify_file x509 "$crt_in" || die "\ Unable to export $pkcs_type for short name '$short_name' without the certificate. Missing cert expected at: $crt_in" case "$pkcs_type" in p12) pkcs_out="$EASYRSA_PKI/private/$short_name.p12" if [ $want_key ]; then [ -f "$key_in" ] || die "\ Unable to export p12 for short name '$short_name' without the key (if you want a p12 without the private key, use nokey option.) Missing key expected at: $key_in" else pkcs_opts="$pkcs_opts -nokeys" fi # export the p12: # shellcheck disable=SC2086 "$EASYRSA_OPENSSL" pkcs12 -in "$crt_in" -inkey "$key_in" -export \ -out "$pkcs_out" $pkcs_opts || die "\ Export of p12 failed: see above for related openssl errors." ;; p7) pkcs_out="$EASYRSA_PKI/issued/$short_name.p7b" # export the p7: # shellcheck disable=SC2086 "$EASYRSA_OPENSSL" crl2pkcs7 -nocrl -certfile "$crt_in" \ -out "$pkcs_out" $pkcs_opts || die "\ Export of p7 failed: see above for related openssl errors." ;; esac notice "\ Successful export of $pkcs_type file. Your exported file is at the following location: $pkcs_out " return 0 } # => export_pkcs() # set-pass backend set_pass() { verify_pki_init # key type, supplied internally from frontend command call (rsa/ec) key_type="$1" # values supplied by the user: raw_file="$2" file="$EASYRSA_PKI/private/$raw_file.key" [ -n "$raw_file" ] || die "\ Missing argument to 'set-$key_type-pass' command: no name/file supplied. See help output for usage details." # parse command options shift 2 crypto="-aes256" while [ -n "$1" ]; do case "$1" in nopass) crypto="" ;; file) file="$raw_file" ;; *) warn "Ignoring unknown command option: '$1'" ;; esac shift done [ -f "$file" ] || die "\ Missing private key: expected to find the private key component at: $file" notice "\ If the key is currently encrypted you must supply the decryption passphrase. ${crypto:+You will then enter a new PEM passphrase for this key.$NL}" EASYRSA_TEMP_FILE_2="$file.temp" "$EASYRSA_OPENSSL" "$key_type" -in "$file" -out "$EASYRSA_TEMP_FILE_2" $crypto || die "\ Failed to change the private key passphrase. See above for possible openssl error messages." mv "$EASYRSA_TEMP_FILE_2" "$file" || die "\ Failed to change the private key passphrase. See above for error messages." notice "Key passphrase successfully changed" } # => set_pass() # update-db backend update_db() { verify_ca_init "$EASYRSA_OPENSSL" ca -utf8 -updatedb -config "$EASYRSA_SSL_CONF" || die "\ Failed to perform update-db: see above for related openssl errors." return 0 } # => update_db() # display cert DN info on a req/X509, passed by full pathname display_dn() { format="$1" path="$2" print "$("$EASYRSA_OPENSSL" "$format" -in "$path" -noout -subject -nameopt multiline)" } # => display_dn() # generate default SAN from req/X509, passed by full pathname default_server_san() { path="$1" cn=$( "$EASYRSA_OPENSSL" req -in "$path" -noout -subject -nameopt sep_multiline | awk -F'=' '/^ *CN=/{print $2}' ) echo "$cn" | grep -E -q '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$' #shellcheck disable=SC2181 if [ $? -eq 0 ]; then print "subjectAltName = IP:$cn" else print "subjectAltName = DNS:$cn" fi } # => default_server_san() # verify a file seems to be a valid req/X509 verify_file() { format="$1" path="$2" "$EASYRSA_OPENSSL" "$format" -in "$path" -noout 2>/dev/null || return 1 return 0 } # => verify_file() # show-* command backend # Prints req/cert details in a readable format show() { type="$1" name="$2" in_file="" format="" [ -n "$name" ] || die "\ Missing expected filename_base argument. Run easyrsa without commands for usage help." shift 2 # opts support opts="-${type}opt no_pubkey,no_sigdump" while [ -n "$1" ]; do case "$1" in full) opts="" ;; *) warn "Ignoring unknown command option: '$1'" ;; esac shift done # Determine cert/req type if [ "$type" = "cert" ]; then verify_ca_init in_file="$EASYRSA_PKI/issued/${name}.crt" format="x509" else verify_pki_init in_file="$EASYRSA_PKI/reqs/${name}.req" format="req" fi # Verify file exists and is of the correct type [ -f "$in_file" ] || die "\ No such $type file with a basename of '$name' is present. Expected to find this file at: $in_file" verify_file $format "$in_file" || die "\ This file is not a valid $type file: $in_file" notice "\ Showing $type details for '$name'. This file is stored at: $in_file " "$EASYRSA_OPENSSL" $format -in "$in_file" -noout -text\ -nameopt multiline $opts || die "\ OpenSSL failure to process the input" } # => show() # show-ca command backend # Prints CA cert details in a readable format show_ca() { # opts support opts="-certopt no_pubkey,no_sigdump" while [ -n "$1" ]; do case "$1" in full) opts= ;; *) warn "Ignoring unknown command option: '$1'" ;; esac shift done verify_ca_init in_file="$EASYRSA_PKI/ca.crt" format="x509" # Verify file exists and is of the correct type [ -f "$in_file" ] || die "\ No such $type file with a basename of '$name' is present. Expected to find this file at: $in_file" verify_file $format "$in_file" || die "\ This file is not a valid $type file: $in_file" notice "\ Showing $type details for 'ca'. This file is stored at: $in_file " "$EASYRSA_OPENSSL" $format -in "$in_file" -noout -text\ -nameopt multiline $opts || die "\ OpenSSL failure to process the input" } # => show_ca() # vars setup # Here sourcing of 'vars' if present occurs. If not present, defaults are used # to support running without a sourced config format vars_setup() { # Try to locate a 'vars' file in order of location preference. # If one is found, source it vars= # set up program path prog_vars="${0%/*}/vars" # set up PKI path pki_vars="${EASYRSA_PKI:-$PWD/pki}/vars" # command-line path: if [ -f "$EASYRSA_VARS_FILE" ]; then vars="$EASYRSA_VARS_FILE" # PKI location, if present: elif [ -f "$pki_vars" ]; then vars="$pki_vars" # EASYRSA, if defined: elif [ -n "$EASYRSA" ] && [ -f "$EASYRSA/vars" ]; then vars="$EASYRSA/vars" # program location: elif [ -f "$prog_vars" ]; then vars="$prog_vars" fi # If a vars file was located, source it # If $EASYRSA_NO_VARS is defined (not blank) this is skipped if [ -z "$EASYRSA_NO_VARS" ] && [ -n "$vars" ]; then #shellcheck disable=SC2034 EASYRSA_CALLER=1 # shellcheck disable=SC1090 . "$vars" notice "\ Note: using Easy-RSA configuration from: $vars" fi # Set defaults, preferring existing env-vars if present set_var EASYRSA "${0%/*}" set_var EASYRSA_OPENSSL openssl set_var EASYRSA_PKI "$PWD/pki" set_var EASYRSA_DN cn_only set_var EASYRSA_REQ_COUNTRY "US" set_var EASYRSA_REQ_PROVINCE "California" set_var EASYRSA_REQ_CITY "San Francisco" set_var EASYRSA_REQ_ORG "Copyleft Certificate Co" set_var EASYRSA_REQ_EMAIL me@example.net set_var EASYRSA_REQ_OU "My Organizational Unit" set_var EASYRSA_ALGO rsa set_var EASYRSA_KEY_SIZE 2048 set_var EASYRSA_CURVE secp384r1 set_var EASYRSA_EC_DIR "$EASYRSA_PKI/ecparams" set_var EASYRSA_CA_EXPIRE 3650 set_var EASYRSA_CERT_EXPIRE 1080 # new default of 36 months set_var EASYRSA_CERT_RENEW 30 set_var EASYRSA_CRL_DAYS 180 set_var EASYRSA_NS_SUPPORT no set_var EASYRSA_NS_COMMENT "Easy-RSA (v3.0.6) Generated Certificate" set_var EASYRSA_TEMP_CONF "$EASYRSA_PKI/openssl-easyrsa.temp" set_var EASYRSA_TEMP_EXT "$EASYRSA_PKI/extensions.temp" set_var EASYRSA_TEMP_FILE_2 "" set_var EASYRSA_TEMP_FILE_3 "" set_var EASYRSA_REQ_CN ChangeMe set_var EASYRSA_DIGEST sha256 set_var EASYRSA_SSL_CONF "$EASYRSA_PKI/openssl-easyrsa.cnf" set_var EASYRSA_SAFE_CONF "$EASYRSA_PKI/safessl-easyrsa.cnf" # Same as above for the x509-types extensions dir if [ -d "$EASYRSA_PKI/x509-types" ]; then set_var EASYRSA_EXT_DIR "$EASYRSA_PKI/x509-types" else #TODO: This should be removed. Not really suitable for packaging. set_var EASYRSA_EXT_DIR "$EASYRSA/x509-types" fi # EASYRSA_ALGO_PARAMS must be set depending on selected algo if [ "ec" = "$EASYRSA_ALGO" ]; then EASYRSA_ALGO_PARAMS="$EASYRSA_EC_DIR/${EASYRSA_CURVE}.pem" elif [ "rsa" = "$EASYRSA_ALGO" ]; then EASYRSA_ALGO_PARAMS="${EASYRSA_KEY_SIZE}" else die "Alg '$EASYRSA_ALGO' is invalid: must be 'rsa' or 'ec'" fi # Setting OPENSSL_CONF prevents bogus warnings (especially useful on win32) export OPENSSL_CONF="$EASYRSA_SAFE_CONF" } # vars_setup() # variable assignment by indirection when undefined; merely exports # the variable when it is already defined (even if currently null) # Sets $1 as the value contained in $2 and exports (may be blank) set_var() { var=$1 shift value="$*" eval "export $var=\"\${$var-$value}\"" } #=> set_var() ######################################## # Invocation entry point: NL=' ' # Be secure with a restrictive umask [ -z "$EASYRSA_NO_UMASK" ] && umask 077 # Parse options while :; do # Separate option from value: opt="${1%%=*}" val="${1#*=}" empty_ok="" # Empty values are not allowed unless excepted case "$opt" in --days) export EASYRSA_CERT_EXPIRE="$val" export EASYRSA_CA_EXPIRE="$val" export EASYRSA_CRL_DAYS="$val" ;; --pki-dir) export EASYRSA_PKI="$val" ;; --use-algo) export EASYRSA_ALGO="$val" ;; --keysize) export EASYRSA_KEY_SIZE="$val" ;; --curve) export EASYRSA_CURVE="$val" ;; --dn-mode) export EASYRSA_DN="$val" ;; --req-cn) export EASYRSA_REQ_CN="$val" ;; --digest) export EASYRSA_DIGEST="$val" ;; --req-c) empty_ok=1 export EASYRSA_REQ_COUNTRY="$val" ;; --req-st) empty_ok=1 export EASYRSA_REQ_PROVINCE="$val" ;; --req-city) empty_ok=1 export EASYRSA_REQ_CITY="$val" ;; --req-org) empty_ok=1 export EASYRSA_REQ_ORG="$val" ;; --req-email) empty_ok=1 export EASYRSA_REQ_EMAIL="$val" ;; --req-ou) empty_ok=1 export EASYRSA_REQ_OU="$val" ;; --ns-cert) export EASYRSA_NS_SUPPORT="$val" ;; --ns-comment) empty_ok=1 export EASYRSA_NS_COMMENT="$val" ;; --batch) empty_ok=1 export EASYRSA_BATCH=1 ;; --subca-len) export EASYRSA_SUBCA_LEN="$val" ;; --vars) export EASYRSA_VARS_FILE="$val" ;; --copy-ext) empty_ok=1 export EASYRSA_CP_EXT=1 ;; --subject-alt-name) export EASYRSA_EXTRA_EXTS="\ $EASYRSA_EXTRA_EXTS subjectAltName = $val" ;; *) break ;; esac # fatal error when no value was provided if [ ! $empty_ok ] && { [ "$val" = "$1" ] || [ -z "$val" ]; }; then die "Missing value to option: $opt" fi shift done # Intelligent env-var detection and auto-loading: vars_setup # Register clean_temp and prog_exit on SIGHUP, SIGINT, SIGQUIT, and SIGABRT trap "clean_temp; prog_exit 1" 1 trap "clean_temp; prog_exit 2" 2 trap "clean_temp; prog_exit 3" 3 trap "clean_temp; prog_exit 6" 6 trap "clean_temp; prog_exit 15" 15 # determine how we were called, then hand off to the function responsible cmd="$1" [ -n "$1" ] && shift # scrape off command case "$cmd" in init-pki|clean-all) init_pki "$@" ;; build-ca) build_ca "$@" ;; gen-dh) gen_dh ;; gen-req) gen_req "$@" ;; sign|sign-req) sign_req "$@" ;; build-client-full) build_full client "$@" ;; build-server-full) build_full server "$@" ;; build-serverClient-full) build_full serverClient "$@" ;; gen-crl) gen_crl ;; revoke) revoke "$@" ;; renew) renew "$@" ;; import-req) import_req "$@" ;; export-p12) export_pkcs p12 "$@" ;; export-p7) export_pkcs p7 "$@" ;; set-rsa-pass) set_pass rsa "$@" ;; set-ec-pass) set_pass ec "$@" ;; update-db) update_db ;; show-req) show req "$@" ;; show-cert) show cert "$@" ;; show-ca) show_ca "$@" ;; ""|help|-h|--help|--usage) cmd_help "$1" exit 0 ;; *) die "Unknown command '$cmd'. Run without commands for usage help." ;; esac # vim: ft=sh nu ai sw=8 ts=8 noet