Skip to content
Snippets Groups Projects

gencsr tooling

  • Clone with SSH
  • Clone with HTTPS
  • Embed
  • Share
    The snippet can be accessed without any authentication.
    Authored by Wolfspyre Automation

    homegrown tool to make the creation and regeneration of ssl certificates a lil easier

    Edited
    gencsr.sh 9.39 KiB
    #!/usr/bin/env bash
    # v0.5  2023.09.01
    # Retool to consume keyfile to gen CSR.  
    # v0.4
    # put emailAddress back in the [dn] section.
    # v0.3
    # added SAN
    export MIN_OPTS=3
    export _STARTTIME=$(/bin/date +%s)
    
    usage () {
      cat <<EOF
    Expects at least ${MIN_OPTS}
    usage: $0 PARAMS_FILE #SANs #IPs OPTIONAL_KeyFile-to-reuse
    
      PARAMS_FILE should contain:
    C="US"
    ST="Texas"
    L="Austin"
    O="Wolfspyre Labs"
    OU="Public"
    HOST='PRETTY_NAME'
    BITS=2048
    DAYS=1826
    CN='CERT-CN-HERE'
    CONTACT='domains@wolfspaw.com'
    SAN1='san-one'
    SAN2='SAN-two'
    SAN3='you-get-the-drill'
    IP1='1.2.3.4'
    IP2='5.6.7.8'
    IP3='127.0.0.1'
    
    
    
    Debug statements will be output if the variable DEBUG exists; Example:
      DEBUG=true; $0 params.txt 3 3 mykey.key
    EOF
    return -1
    }
    
    runningTime () {
      _NOW=$(/bin/date +%s)
      echo -e "$(( ${_NOW}-${_STARTTIME}  ))"
    }
    
    error () {
      MSG=$1
      _time=$(runningTime)
      echo -e "\033[31m[${_time}s] ERROR\033[0m: $MSG" >>/dev/stderr
    }
    
    warn () {
      MSG=$1
      _time=$(runningTime)
      echo -e "\033[1;33m[${_time}s] WARN\033[0m: $MSG" >>/dev/stderr
    }
    
    info () {
      MSG=$1
      _time=$(runningTime)
      echo -e "\033[32m[${_time}s] INFO\033[0m: $MSG"
    }
    
    debug () {
      MSG=$1
      _time=$(runningTime)
      if [[ $2 && $2 == 'verbose' ]]; then
        verbose_msg=true
      else
        verbose_msg=false
      fi
      if [ -n  "$DEBUG" ]; then
        if $verbose_msg; then
          #only display verbose if DEBUG == verbose
          if [ ${DEBUG} == 'verbose' ]; then
            echo -e "\033[35mVERBOSE\033[0m: \033[32m[${_time}s] ${FUNCNAME[1]}()\033[0m: ${MSG}"
          fi
        else
          echo -e "\033[34mDEBUG\033[0m: \033[32m[${_time}s] ${FUNCNAME[1]}()\033[0m: ${MSG}"
        fi
      fi
    }
    echodo() {
      echo "${@}"
      (${@})
    }
    
    infodo() {
      info "${@}"
      (${@})
    }
    
    containsElement () {
      local e
      for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
      return 1
    }
    
    checkFile() {
      _file=$1
      if [[ -f ${_file} ]]; then
        debug "$_file exists"
      else
        error "Specified file ${_file} doesn't exist. Cannot continue"
        exit 1
      fi
    }
    
    makeDirIfNeedful() {
      _dir=$1
      if [[ -d ${_dir} ]]; then
        debug "$_dir exists"
      else
        warn "Specified dir ${_dir} doesn't exist. Doing the needful"
        mkdir -p ${_dir}
      fi
    }
    checkDir() {
      _dir=$1
      if [[ -d ${_dir} ]]; then
        debug "$_dir exists"
      else
        error "Specified dir ${_dir} doesn't exist. Cannot continue"
        exit 1
      fi
    }
    
    checkMountpoint() {
      _mountpoint=$1
        _matches=$(/usr/bin/awk " \$2 ~ \"${_mountpoint}\" {print \$2}" /proc/mounts|/usr/bin/wc -l)
        #if the second field of the output of /proc/mounts matches the mountpoint,  print the output to wc to return the number of matches.
        # this could be done differently.
        if [[ "${_matches}" == 0 ]]; then
          # we didn't get a match for some reason. Not a mountpoint we can trust. fail.
          error "Got ${_matches} matches while inspecting /proc/mounts for ${_mountpoint}. Cannot proceed."
          exit 1
        elif [[ "${_matches}" == 1 ]]; then
          debug "Success. matched ${_mountpoint} in /proc/mounts."
        else
          error "Got an unexpected count of ${_matches}  when looking at /proc/mounts. Perhaps you could explicitly match versus regex?"
          # Comment this out if you don't mind multiple matches for some reason.
          exit 1
          #
        fi
    }
    
    echodo() {
         echo "${@}"
         (${@})
         }
    
    yearmon() {
         date '+%Y%m%d'
         }
    
    fqdn() {
         (nslookup ${1} 2>&1 || echo Name ${1}) \
                 | tail -3 | grep Name| sed -e 's,.*e:[ \t]*,,'
         }
    
    
    spinner()
    {
      #http://stackoverflow.com/questions/12498304/using-bash-to-display-a-progress-working-indicator
      #http://stackoverflow.com/questions/1570262/shell-get-exit-code-of-background-process
      local pid=$!
      if [[ $1 ]]; then
        local cmd=$1
        debug "using ${cmd} as the command name for `ps -p ${pid} -o cmd h`"
      else
        local cmd=`ps -p ${pid} -o cmd h`
      fi
      local delay=0.5
      local spinstr='|/-\'
      echo -ne "\033[32m"
      while [ "$(ps a | awk '{print $1}' | grep $pid)" ]; do
        local temp=${spinstr#?}
        printf "%c" "$spinstr"
        local spinstr=$temp${spinstr%"$temp"}
        sleep $delay
        printf "\b\b\b"
      done
      wait $pid
      local pidstatus=$?
      debug "pid: ${pid} returned status: ${pidstatus}" verbose
      printf "\b\b"
      echo -ne "\033[0m"
      case ${pidstatus} in
        0 )
        debug "${cmd} exited successfully"
        ;;
        100 )
        #apt-get returns this when it's installed a package
        error "${cmd} could not install something? "
        exit 1
        ;;
        * )
        #assume something went wrong
        error "The execution of [ ${cmd} ] failed!"
        exit 1
        ;;
      esac
    }
    snifftest()
    {
      info "Checking that all necessary binaries exist"
      for F in ${_SSLBIN}; do
        checkFile ${F}
      done
    }
    inspectCert(){
      _CERT = $1
    }
    inspectCSR(){
      _CSR=$1
      checkFile ${_CSR}
      infodo "${_SSLBIN} req -text  -noout -in ${_CSR}"
    
    }
    inspectKey(){
      _KEY=$1
      _O='0'
      _E='0'
      checkFile ${_KEY}
      info "Keyfile ${_KEY} found. Starting inspection"
      if [[ `${_SSLBIN} rsa -in ${_KEY} -check -noout` ]]; then 
          info "Key ${_KEY} passes openssl inspection"
      else 
        error "Key ${_KEY} no bueno! Openssl errored."
        exit 1
      fi
    }
    validateParams(){
      for VAR in C ST L O OU HOST BITS DAYS CN CONTACT; do
        if [[ $(echo ${!VAR}|wc -w) -gt 0 ]]; then
          debug "Validating ${VAR}: ${!VAR}"
        else
          error "${VAR} seems wrong: ${!VAR}\n Please inspect yout params file: ${_PARAMSFILE}"
          exit 1
        fi  
      done
      info "Params Validated"  
      return 0
    }
    validateSANs(){
      _SANITER=0
      _SCOUNT=$1
      debug "Validating that we have ${_SCOUNT} SANs defined"
      while [[ ${_SANITER} -lt ${_SANCOUNT} ]]; do
        let _SANITER=(${_SANITER}+1)
        _SN=SAN${_SANITER}
        if [[ $(echo ${!_SN}|wc -w) -gt 0 ]]; then
          debug "${_SANITER}/${_SANCOUNT}: SAN${_SANITER}: ${!_SN}"
        else
          error "SAN ${_SANITER} of ${_SANCOUNT} seems problematic. Check your paramsfile."
          exit 1
        fi 
      done
      info "${_SANCOUNT} SAN vars present"
      return 0
    }
    
    validateIPs(){
      _IPITER=0
      _ICOUNT=$1
      debug "Validating that we have ${_ICOUNT} IPs defined"
      while [[ ${_IPITER} -lt ${_IPCOUNT} ]]; do
        let _IPITER=(${_IPITER}+1)
        _IPN=IP${_IPITER}
        if [[ $(echo ${!_IPN}|wc -w) -gt 0 ]]; then
          debug "${_IPITER}/${_IPCOUNT}: IP${_IPITER}: ${!_IPN}"
        else
          error "IP ${_IPITER} of ${_IPCOUNT} of seems problematic. Check your paramsfile."
          exit 1
        fi
      done
      info "${_IPCOUNT} IP vars present"
      return 0
    }
    fork() { (setsid "$@" &); }
    
    validateME() {
      debug "testing Params"
      validateParams; _VP=$?
      debug "testing SANs"
      validateSANs ${_SANCOUNT}; _VS=$?
      debug "testing IPs"
      validateIPs  ${_IPCOUNT};  _VI=$?
      if [[ ${_VP} == 0 && ${_VS} == 0 && ${_VI} == 0 ]]; then
        info "smells ok. Proceeding"
      else
        error "sorry. something went wrong. \n Fuck your day."
        exit 1
      fi;
    }
    genCSRDetails() {
      export CSR="${HOST}-${DATE}-csr.pem"
      export CSR_DETAILS="${HOST}-${DATE}-csr-details.txt"
      export mydir="${PWD}"
      export MYCSRPATH="${mydir}/${CSR}"
      export MYKEYPATH="${mydir}/${_KEYFILE}"
      export MYCSR_DETAILS="${mydir}/${CSR_DETAILS}"
      debug "\n      CSR: ${CSR}\n  Details: ${MYCSR_DETAILS}\n CSR Path: ${MYCSRPATH}\n  KeyPath: ${MYKEYPATH}"
      info "Creating ${MYCSR_DETAILS}"
      cat > ${MYCSR_DETAILS} <<-EOF
    [req]
    default_bits = ${BITS}
    prompt = no
    default_md = sha256
    req_extensions = req_ext
    x509_extensions = x509_ext
    distinguished_name = dn
    
    [ dn ]
    emailAddress=${CONTACT}
    C=${C}
    ST=${ST}
    L=${L}
    O=${O}
    OU=${OU}
    CN = ${CN}
    
    [ req_ext ]
    subjectKeyIdentifier = hash
    subjectAltName = @alt_names
    keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
    extendedKeyUsage = serverAuth, clientAuth,  codeSigning, 1.3.6.1.5.5.8.2.2
    
    [ x509_ext ]
    subjectKeyIdentifier = hash
    subjectAltName = @alt_names
    keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
    extendedKeyUsage = serverAuth, clientAuth,  codeSigning, 1.3.6.1.5.5.8.2.2
    
    [ alt_names ]
    email = ${CONTACT}
    EOF
      _SANITER=0
      _SCOUNT=${_SANCOUNT}
      _IPITER=0
      _ICOUNT=${_IPCOUNT}
      debug "adding SANs\
    $(while [[ ${_SANITER} -lt ${_SANCOUNT} ]]; do let _SANITER=(${_SANITER}+1) ; _SN=SAN${_SANITER}; echo "DNS.${_SANITER} = ${!_SN}">> ${MYCSR_DETAILS}; echo -n '.' ; done)"  
      debug "adding IPs\
    $(while [[ ${_IPITER} -lt ${_IPCOUNT} ]]; do let _IPITER=(${_IPITER}+1); _IPN=IP${_IPITER}; echo "IP.${_IPITER}  = ${!_IPN}" >> ${MYCSR_DETAILS}; echo -n '.'; done)"
      info "CSR Details populated."
    }
    
    genCSR(){
      if [[ -n ${_KEYFILE} ]]; then
        info "Generating CSR from existing Keyfile: ${MYKEYPATH}"
        infodo  "${_SSLBIN} req -new -sha256 -nodes -extensions req_ext -out ${MYCSRPATH} -key ${MYKEYPATH} -config  ${MYCSR_DETAILS} "
      else
        info "Generating CSR and key from details"
        infodo "${_SSLBIN} req -new -sha256 -nodes -extensions req_ext -out ${MYCSRPATH} -newkey rsa:${BITS} -keyout ${MYKEYPATH} -config  ${MYCSR_DETAILS} -days ${DAYS}"
        echodo "${_SSLBIN} req -text -days ${DAYS} -noout -in ${MYCSRPATH}"
      fi
    }
    if [ $# -lt ${MIN_OPTS} ]; then
      usage
    else
      export _SSLBIN=`which openssl`
      DATE=`yearmon`
      info 'ensuring expected binaries are in place'
      snifftest
      _PARAMSFILE=$1
      _SANCOUNT=$2||0
      _IPCOUNT=$3||0
      _KEYFILE=$4||false
      info 'unsetting paramsfile keys'
      for VAR in C ST L O OU HOST BITS DAYS CN CONTACT; do
        unset ${!VAR}
      done
      . ${_PARAMSFILE}
      validateME
      if [[ -n $_KEYFILE ]]; then
        info "Was Given KeyFile: ${_KEYFILE}"
        inspectKey ${_KEYFILE};_KC=$?
      else
         info "No Keyfile Given"
      fi
      genCSRDetails; _CSRDRC=$?
      if [[ ${_CSRDC} -eq 0 ]]; then
        genCSR
      else 
        warn "genCSRDetails return code was not 0: [ ${_CSRDC}]"
      fi
    fi
    params 246 B
    C="US"
    ST="Texas"
    L="Austin"
    O="Wolfspyre Labs"
    OU="Public"
    HOST='PRETTY_NAME'
    BITS=2048
    DAYS=1826
    CN='CERT-CN-HERE'
    CONTACT='domains@wolfspaw.com'
    SAN1='san-one'
    SAN2='SAN-two'
    SAN3='you-get-the-drill'
    IP1='1.2.3.4'
    IP2='5.6.7.8'
    IP3='127.0.0.1'
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Please register or to comment