#!/bin/bash
#------------------------------
# Script to manage vCenter SSL certificates.
#
# Author: Vincent Santa Maria [vsantamaria@vmware.com]
#------------------------------

#------------------------------
# for debugging purposes only, uncomment the following line:
# export PS4='+[${SECONDS}s][${BASH_SOURCE}:${LINENO}]: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'; set -x;
# to debug run: ./vCert 2>vCert-debug.txt
#------------------------------

VERSION="4.13.0"
VCERT_PID="$$"
trap "exit 1" 10

#------------------------------
# Prints help information
#------------------------------
function printHelp() {
   cat << EOF
vCert: vCenter Certificate Management Utility
Usage: $0 [options]
Options:
   -h | --help          Prints this help menu
   -d | --debug         Enables debug-level logging
   -j | --just-do-it    JUST DO IT! Script operations will continue if backup tasks fail
   -u | --user          Specify an SSO administrator account
   -v | --version       Prints script version
   -w | --password      Password for the specified SSO administrator account

EOF
}

#------------------------------
# Parses arguments passed to the script
#------------------------------
function parseArguments() {
   logInfo 'Entering the parseArguments function'
   logDetails "Arguments: $#"
   if [ "$#" -ge 1 ]; then
      logInfo 'There are arguments passed'
      while [ "$#" -ge 1 ]; do
	      logInfo "Parsing argument '$1'"
	      case "$1" in
		      -h|--help)
               logInfo 'Printing help menu'
			      stopLoading
			      printHelp
			      exit
			      ;;
            -d|--debug)
               logInfo 'Enabling debug-level logging'
               DEBUG=1
               shift 1
               ;;
            -j|--just-do-it)
               logInfo 'Disabling exit on backup task failure'
               EXIT_ON_BACKUP_FAILURE=0
               shift 1
               ;;
			   -u|--user)
               logInfo "Specified SSO Administrator account: $2"
			      VMDIR_USER_UPN="$2"
			      VMDIR_USER=$(echo $VMDIR_USER_UPN | awk -F'@' '{print $1}')
			      shift 2
			      ;;
			   -v|--version)
               logInfo 'Printing script version'
			      stopLoading			   
			      echo "vCert: version $VERSION"
			      exit
			      ;;
			   -w|--password)
               logInfo "Specified SSO Administrator password: $(echo $2 | tr '[:print:]' '*')"
			      echo -n "$2" > $STAGE_DIR/.vmdir-user-password
               chmod 640 $STAGE_DIR/.vmdir-user-password
			      VMDIR_USER_PASSWORD="$2"
			      shift 2
			      ;;
			   *)
               logInfo "Invalid argument $1"
			      echo $'\n'"${YELLOW}Invalid argument '$1'"
			      stopLoading
			      printHelp
			      exit
			      ;;
		   esac
	   done	  
   fi
   if [ -n "$VMDIR_USER_UPN" ] && [ -f $STAGE_DIR/.vmdir-user-password ]; then VERIFY_PASSED_CREDENTIALS=1; fi   
}

#------------------------------
# Print loading message
#------------------------------
function loading() {
   i=2
   e[0]='.'
   e[1]='..'
   e[2]='...'
   while [ $i -lt 3 ]; do
      echo -ne "\r\033[KLoading${e[$i]}"
      if [ $i -eq 2 ]; then
         i=0
      else
         ((++i))
      fi
      sleep 1
   done
}

loading &
LOADING_PID=$!

#------------------------------
# Stop loading message
#------------------------------
function stopLoading() {
   kill $LOADING_PID 
   wait $LOADING_PID > /dev/null 2>&1
   echo -ne "\r\033[K"
}

#------------------------------
# Print section header
#------------------------------
function header() {
   printf "\n${CYAN}$1\n"
   printf "%65s${NORMAL}\n" | tr " " "-"
   logInfo "Operation: $1"
}

#------------------------------
# Print task description
#------------------------------
function task() {
   printf "%-52s" "$1"
   logInfo "Task: $1"
}

#------------------------------
# Print formatted status message with colored text
#------------------------------
function statusMessage() {
   printf "%13s\n" "${1}" | sed "s/${1}/${!2}&${NORMAL}/"
   logInfo "Task Status: $1"
}

#------------------------------
# Print formatted 'errror' message
#------------------------------
function errorMessage() {
   if [ -z $3 ]; then printf "%13s\n\n" "FAILED" | sed "s/FAILED/${RED}&${NORMAL}/"; else printf "\n\n"; fi
   logError "Task Error: $1"
   
   if [ -z $2 ]; then
      printf "${YELLOW}${1}. Exiting...${NORMAL}\n\n"
      kill -10 $VCERT_PID
   else
      case $2 in
         'backup')
            if [ $EXIT_ON_BACKUP_FAILURE == 1 ]; then 
               printf "${YELLOW}${1}. Exiting...${NORMAL}\n\n"
               kill -10 $VCERT_PID
            fi
            ;;
         *)
            printf "${YELLOW}${1}. Exiting...${NORMAL}\n\n"
            kill -10 $VCERT_PID
            ;;
      esac
   fi
}

#------------------------------
# Main logging function
#------------------------------
function logEntry() {
   if [ -n "$1" ]; then
      IN="$1"
      LOG_LEVEL="$2"
   else
      read IN
      LOG_LEVEL='INFO'
   fi
   LOG_TIMESTAMP=$(date "+%Y-%m-%dT%H:%M:%S %Z %:z")
   echo "$LOG_TIMESTAMP $LOG_LEVEL $IN" >> $LOG 2>/dev/null
}

#------------------------------
# Logging function for log details
#------------------------------
function logDetails() {
   if [ -n "$1" ]; then
      IN="$1"
   else
      read IN
   fi
   echo "$IN" | sed -e 's/^[.]*/--> &/g' >> $LOG 2>/dev/null
}

#------------------------------
# Logging function for info-level logging
#------------------------------
function logInfo() {
   if [ -n "$1" ]; then
      IN="$1"
   else
      read IN
   fi
   logEntry "$IN" 'INFO'
}

#------------------------------
# Logging function for error-level logging
#------------------------------
function logError() {
   if [ -n "$1" ]; then
      IN="$1"
   else
      read IN
   fi
   logEntry "$IN" 'ERROR'
}

#------------------------------
# Logging function for debug-level logging
#------------------------------
function logDebug() {
   if [ "$DEBUG" -gt 0 ]; then
      if [ -n "$1" ]; then
         IN="$1"
      else
         read IN
      fi
      logEntry "$IN" 'DEBUG'
   fi
}

#------------------------------
# Logging function for debug-level logging
#------------------------------
function logDebugDetails() {
   if [ "$DEBUG" -gt 0 ]; then
      if [ -n "$1" ]; then
         IN="$1"
      else
         read IN
      fi
      echo "$IN" | sed -e 's/^[.]*/--> &/g' >> $LOG 2>/dev/null
   fi
}

#------------------------------
# Set color variables
#------------------------------
function enableColor() {
   RED=$(tput setaf 1)
   GREEN=$(tput setaf 2)
   YELLOW=$(tput setaf 3)
   CYAN=$(tput setaf 6)
   NORMAL=$(tput sgr0)
}

#------------------------------
# Clear color variables for reports
#------------------------------
function disableColor() {
   RED=''
   GREEN=''
   YELLOW=''
   CYAN=''
   NORMAL=''
}

#------------------------------
# Pre-start operations
#------------------------------
function preStartOperations() {
   if [ ! -d $LOG_DIR ]; then mkdir $LOG_DIR; fi
   if [ ! -d $STAGE_DIR ]; then mkdir -p $STAGE_DIR; fi
   if [ ! -d $REQUEST_DIR ]; then mkdir -p $REQUEST_DIR; fi
   if [ ! -d $BACKUP_DIR ]; then mkdir -p $BACKUP_DIR; fi
   if [ ! -f $LOG ]; then touch $LOG; fi
   logInfo "Starting vCert version $VERSION"
      
   echo -n "$VMDIR_MACHINE_PASSWORD" > $STAGE_DIR/.machine-account-password
   chmod 640 $STAGE_DIR/.machine-account-password     
   
   updateVcSupport
   
   setTimestamp
   
   parseArguments "$@"
   
   enableColor
   
   checkServices
   
   checkVmdirMachineCredentials
   
   checkCAPermissions   
   
   setSolutionUsers
   
   setVECSStores
   
   clearCSRInfo
   
   checkForVCF
   
   stopLoading
}

#------------------------------
# make sure vCert.log is included in a support bundle
#------------------------------
function updateVcSupport() {
   if [ ! -f /etc/vmware/vm-support/vcert.mfx ]; then
      logInfo 'Creating vc-support manifest for vCert'
      cat << EOF > /etc/vmware/vm-support/vcert.mfx
% Manifest name: vcert
% Manifest group: VirtualAppliance
% Manifest default: Enabled
# action Options file/command
copy IGNORE_MISSING $LOG_DIR/*	  
EOF
   fi
}

#------------------------------
# set the TIMESTAMP variable
#------------------------------
function setTimestamp() {
   TIMESTAMP=$(date +%Y%m%d%H%M%S)
}

#------------------------------
# Cleanup operations
#------------------------------
function cleanup() {
   if [ $CLEANUP -eq 1 ]; then
      rm -Rf $STAGE_DIR
   fi
}

#------------------------------
# Validate an IP address
#------------------------------
function validateIp() {
   logInfo 'Attempting to validate $1 as an IP address'
   RETURN=1
   if [[ $1 =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
      OIFS=$IFS
      IFS='.'
      ip=($1)
      IFS=$OIFS
      [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
      RETURN=$?
   fi
   if [ $RETURN ]; then
      logInfo '$1 is a valid IP address'
   else
      logInfo '$1 is not a valid IP address'
   fi
   return $RETURN
}

#------------------------------
# Authenticate if needed
#------------------------------
function authenticateIfNeeded() {
   if [ -z "$VMDIR_USER_UPN" ] || [ ! -f $STAGE_DIR/.vmdir-user-password ]; then
      getSSOCredentials
      verifySSOCredentials
   fi
   if [ $VERIFY_PASSED_CREDENTIALS == 1 ]; then verifySSOCredentials; fi
}

#------------------------------
# Get SSO administrator credentials
#------------------------------
function getSSOCredentials() {
   unset VMDIR_USER_UPN_INPUT
   read -p $'\n'"Please enter a Single Sign-On administrator account [${VMDIR_USER_UPN_DEFAULT}]: " VMDIR_USER_UPN_INPUT
   
   if [ -z "$VMDIR_USER_UPN_INPUT" ]; then 
      VMDIR_USER_UPN=$VMDIR_USER_UPN_DEFAULT
   else
      VMDIR_USER_UPN=$VMDIR_USER_UPN_INPUT
   fi

   logInfo "User has chosen the following Single Sign-On account: $VMDIR_USER_UPN"

   VMDIR_USER=$(echo $VMDIR_USER_UPN | awk -F'@' '{print $1}')
   USER_PROVIDED_SSO_DOMAIN=$(echo $VMDIR_USER_UPN | awk -F'@' '{print $2}')

   if [ "$USER_PROVIDED_SSO_DOMAIN" != "$SSO_DOMAIN" ]; then
      echo ''
      while [ "$USER_PROVIDED_SSO_DOMAIN" != "$SSO_DOMAIN" ]; do         
         read -p "${YELLOW}Invalid domain, please provide an account in the SSO domain ($SSO_DOMAIN):${NORMAL} " VMDIR_USER_UPN_INPUT
         VMDIR_USER_UPN=$VMDIR_USER_UPN_INPUT
         VMDIR_USER=$(echo $VMDIR_USER_UPN | awk -F'@' '{print $1}')
         USER_PROVIDED_SSO_DOMAIN=$(echo $VMDIR_USER_UPN | awk -F'@' '{print $2}')
      done
      echo ''
   fi

   read -r -s -p "Please provide the password for $VMDIR_USER_UPN: " VMDIR_USER_PASSWORD
   echo -n "$VMDIR_USER_PASSWORD" > $STAGE_DIR/.vmdir-user-password
   chmod 640 $STAGE_DIR/.vmdir-user-password
   echo ''
}

#------------------------------
# Verify SSO credentials
#------------------------------
function verifySSOCredentials() {
   VERIFIED=0
   ATTEMPT=1

   logInfo "Validating credentials for ${VMDIR_USER_UPN}"

   while [ $ATTEMPT -le 3 ]; do
      if ! $LDAP_SEARCH -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=Servers,cn=$SSO_SITE,cn=Sites,cn=Configuration,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password "(objectclass=vmwDirServer)" cn 2>/dev/null 1>/dev/null; then
         logInfo "Invalid credentials for $VMDIR_USER_UPN (attempt $ATTEMPT)"
		   if [ $VERIFY_PASSED_CREDENTIALS == 1 ] && [ $ATTEMPT == 1 ]; then
            echo "${YELLOW}Unable to validate the provided credentials for $VMDIR_USER_UPN${NORMAL}"
		      getSSOCredentials
		   else
            read -r -s -p $'\n'"${YELLOW}Invalid credentials, please enter the password for $VMDIR_USER_UPN:${NORMAL} " VMDIR_USER_PASSWORD
            echo -n "$VMDIR_USER_PASSWORD" > $STAGE_DIR/.vmdir-user-password
            chmod 640 $STAGE_DIR/.vmdir-user-password
		   fi
         ((++ATTEMPT))
      else
         VERIFIED=1
         logInfo "Credentials verified for $VMDIR_USER_UPN"
         if [ $ATTEMPT -gt 1 ]; then echo ''; fi
         break
      fi
   done

   if [ $VERIFIED == 0 ]; then
      errorMessage "Unable to verify credentials for $VMDIR_USER_UPN"
   fi
}

#------------------------------
# Check if vmafdd, vmdird, and reverse proxy are running
#------------------------------
function checkServices() {
   if [[ "$VC_VERSION" =~ ^[78] ]]; then
      logInfo 'Checking state of the vmware-envoy service'
      if ! checkService 'vmware-envoy'; then
         logError 'The Envoy service is not running'
         echo $'\n'"${YELLOW}The Envoy Service is not running!"
         echo "The script cannot continue. Exiting...${NORMAL}"
         stopLoading
         exit
      fi
      logInfo 'The vmware-envoy service is running'
   fi
   logInfo 'Checking state of the vmware-rhttpproxy service'
   if ! checkService 'vmware-rhttpproxy'; then
      logError 'The reverse proxy service is not running'
      echo $'\n'"${YELLOW}The Reverse Proxy Service is not running!}"
      echo "The script cannot continue. Exiting...${NORMAL}"
      stopLoading
      exit
   fi
   logInfo 'The vmware-rhttpproxy service is running'
   logInfo 'Checking state of the vmafdd service'
   if ! checkService 'vmafdd'; then
      logError 'The vmafdd service is not running'
      echo $'\n'"${YELLOW}The VMware Authentication Framework Service is not running!"
      echo "The script cannot continue. Exiting...${NORMAL}"
      stopLoading
      exit
   fi
   logInfo 'The vmafdd service is running'
   if [ $NODE_TYPE != 'management' ]; then
      logInfo 'Checking state of the vmdird service'
      if ! checkService 'vmdird'; then
         logError 'The vmdird service is not running'
         echo $'\n'"${YELLOW}The VMware Directory Service is not running!"
         echo "The script cannot continue. Exiting...${NORMAL}"
         stopLoading
         exit
      else
         VMDIR_STATE=$(echo 6 | /usr/lib/vmware-vmdir/bin/vdcadmintool 2>/dev/null | awk '{print $NF}' | tr -d '\n')
      fi
      logInfo "The vmdird service is running and in the following state: $VMDIR_STATE"
   fi
}

#------------------------------
# Check if PSC is configured to be behind a load balancer
#------------------------------
function checkPSCHA() {
   if [ $NODE_TYPE = 'infrastructure' ]; then
      PSC_LB=$(grep proxyName /usr/lib/vmware-sso/vmware-sts/conf/server.xml | sed 's/ /\n/g' | grep proxyName | awk -F'=' '{print $NF}' | tr -d '"')
   fi
}

#------------------------------
# Notice for additional steps with PSC HA
#------------------------------
function noticePSCHA() {
   if [ $NODE_TYPE = 'infrastructure' ] && [ -n "$PSC_LB" ]; then
      cat << EOF
${YELLOW}-------------------------!!! WARNING !!!-------------------------
This PSC has been detected to be in an HA configuration.
 - The new certificate and private key should be installed on all other
   PSCs configured behind the load balancer.
 - If the load balancer is configured for SSL termination, it will need
   the new Machine SSL certificate and private key.
 - If the load balancer is configured for SSL passthrough, no additional
   configuration should be necessary.${NORMAL}
EOF
   fi
}

#------------------------------
# Check if service is running
#------------------------------
function checkService() {
   if service-control --status $1 | grep -i stopped >/dev/null 2>&1; then
      return 1
   else
      return 0
   fi
}

#------------------------------
# Check if machine account can connect to VMware Directory
#------------------------------
function checkVmdirMachineCredentials() {
   logInfo "Checking credentials for machine account $VMDIR_MACHINE_ACCOUNT_DN"
   if ! $LDAP_SEARCH -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "ou=Domain Controllers,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password dn > /dev/null 2>&1; then
      LDAP_ERROR=$?
      echo $'\n'"${YELLOW}The machine account $VMDIR_MACHINE_ACCOUNT_DN"
      echo "was unable to authenticate to the VMware Directory instance at $PSC_LOCATION:$VMDIR_PORT"
      echo "LDAP error: $LDAP_ERROR"
      echo "The script cannot continue. Exiting...${NORMAL}"
      logError "Credentials for machine account $VMDIR_MACHINE_ACCOUNT_DN could not be verified (LDAP error: $LDAP_ERROR)"
      stopLoading
      exit
   fi
   logInfo "Credentials for machine account $VMDIR_MACHINE_ACCOUNT_DN verified"
}

#------------------------------
# Check if machine account has proper CA permissions
#------------------------------
function checkCAPermissions() {
   logInfo "Machine account DN: $VMDIR_MACHINE_ACCOUNT_DN"
   logInfo "PSC location: $PSC_LOCATION"
   if [ $NODE_TYPE != 'management' ]; then 
      DCADMINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=DCAdmins,cn=BuiltIn,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password member | sed -e 's/^ //g' | tr -d '\n' | sed -e 's/member:/\n&/g')
      logInfo 'Checking DCAdmins membership:'
      logDetails "$DCADMINS"
      if echo "$DCADMINS" | grep -i "$VMDIR_MACHINE_ACCOUNT_DN" 2>/dev/null > /dev/null; then
         CAADMINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=CAAdmins,cn=BuiltIn,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password member | sed -e 's/^ //g' | tr -d '\n' | sed -e 's/member:/\n&/g')
         if echo "$CAADMINS" | grep -i "cn=DCAdmins,cn=BuiltIn,$VMDIR_DOMAIN_DN" 2>/dev/null > /dev/null; then
            return 0
         else
            echo $'\n'"${YELLOW}The DCAdmins SSO group is not a member of the CAAdmins SSO group!"
            echo "The script cannot continue. Exiting...${NORMAL}"
            logError 'The DCAdmins SSO group is not a member of the CAAdmins SSO group'
            stopLoading
            exit
         fi
      else
         echo $'\n'"${YELLOW}The machine account is not a member of the DCAdmins SSO group!"
         echo "The script cannot continue. Exiting...${NORMAL}"
         logError 'The machine account is not a member of the DCAdmins SSO group'
         stopLoading
         exit
      fi
   else
      DCCLIENTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=DCClients,cn=BuiltIn,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password member | sed -e 's/^ //g' | tr -d '\n' | sed -e 's/member:/\n&/g')
      logInfo 'Checking DCClients membership:'
	   logDetails "$DCCLIENTS"
	   if echo "$DCCLIENTS" | grep -i "$VMDIR_MACHINE_ACCOUNT_DN" 2>/dev/null > /dev/null; then
         CAADMINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=CAAdmins,cn=BuiltIn,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password member | sed -e 's/^ //g' | tr -d '\n' | sed -e 's/member:/\n&/g')
         if echo "$CAADMINS" | grep -i "cn=DCClients,cn=BuiltIn,$VMDIR_DOMAIN_DN" 2>/dev/null > /dev/null; then
            return 0
         else
            echo $'\n'"${YELLOW}The DCAdmins SSO group is not a member of the CAAdmins SSO group!"
            echo "The script cannot continue. Exiting...${NORMAL}"
            logError 'The DCAdmins SSO group is not a member of the CAAdmins SSO group'
            stopLoading
            exit
         fi
      else
         echo $'\n'"${YELLOW}The machine account is not a member of the DCClients SSO group!"
         echo "The script cannot continue. Exiting...${NORMAL}"
         logError 'The machine account is not a member of the DCAdmins SSO group'
         stopLoading
         exit
      fi
   fi
}

#------------------------------
# Set the Solution Users for this node
#------------------------------
function setSolutionUsers() {
   SOLUTION_USERS=('machine' 'vsphere-webclient')
   if [ $NODE_TYPE != 'infrastructure' ]; then
      SOLUTION_USERS+=('vpxd' 'vpxd-extension')
      if [[ "$VC_VERSION" =~ ^[78] ]]; then
         SOLUTION_USERS+=('hvc' 'wcp')
      fi
   fi
   logInfo "Setting Solution Users: ${SOLUTION_USERS[*]}"
}

#------------------------------
# Set the VECS Stores and default permissions for this node
#------------------------------
function setVECSStores() {
   VECS_STORES='MACHINE_SSL_CERT TRUSTED_ROOTS TRUSTED_ROOT_CRLS machine vsphere-webclient'
   declare -gA VECS_STORE_READ_PERMISSIONS=()
   declare -gA VECS_STORE_WRITE_PERMISSIONS=()
   if [ $NODE_TYPE == 'infrastructure' ]; then
      VECS_STORE_READ_PERMISSIONS[MACHINE_SSL_CERT]=''
	   VECS_STORE_READ_PERMISSIONS[TRUSTED_ROOTS]='EVERYONE'
	   VECS_STORE_READ_PERMISSIONS[TRUSTED_ROOT_CRLS]='EVERYONE'
	   VECS_STORE_READ_PERMISSIONS[machine]='cm'
      VECS_STORE_READ_PERMISSIONS[vsphere-webclient]='vapiEndpoint'
   else
      VECS_STORES+=' vpxd vpxd-extension SMS'
      VECS_STORE_READ_PERMISSIONS[MACHINE_SSL_CERT]='updatemgr vsphere-ui vpxd vpostgres vsphere-client vsm'
      VECS_STORE_READ_PERMISSIONS[TRUSTED_ROOTS]='EVERYONE vpxd'
      VECS_STORE_READ_PERMISSIONS[TRUSTED_ROOT_CRLS]='EVERYONE vpxd'
      VECS_STORE_READ_PERMISSIONS[machine]='vpxd cm'
      VECS_STORE_READ_PERMISSIONS[vsphere-webclient]='vsphere-ui vpxd perfcharts vapiEndpoint'
      VECS_STORE_READ_PERMISSIONS[vpxd]='vpxd'
      VECS_STORE_READ_PERMISSIONS[vpxd-extension]='deploy updatemgr vsphere-ui vpxd vsm imagebuilder content-library eam mbcs'
      VECS_STORE_READ_PERMISSIONS[SMS]='deploy vpxd'
   fi
   
   case $VC_VERSION in
      '6.5')
	      if [ $NODE_TYPE != 'infrastructure' ]; then
	         VECS_STORE_READ_PERMISSIONS[SMS]='vpxd'
			   VECS_STORE_READ_PERMISSIONS[vpxd-extension]+=' vsphere-client'
		   fi
	      ;;
      '6.7')
	      if [ $NODE_TYPE == 'infrastructure' ]; then
		      VECS_STORES+=' APPLMGMT_PASSWORD'
		      VECS_STORE_READ_PERMISSIONS[APPLMGMT_PASSWORD]=''
	      else 
		     VECS_STORES+=' APPLMGMT_PASSWORD'
		     VECS_STORE_READ_PERMISSIONS[APPLMGMT_PASSWORD]='vpxd'
		     VECS_STORE_READ_PERMISSIONS[data-encipherment]='vpxd'
			  VECS_STORE_READ_PERMISSIONS[vpxd-extension]+=' vsphere-client'
		   fi
	      ;;
	  '7.0')
	      VECS_STORES+=' APPLMGMT_PASSWORD data-encipherment hvc wcp'
		   VECS_STORE_READ_PERMISSIONS[MACHINE_SSL_CERT]='updatemgr vsphere-ui vpxd vpostgres vsm'
		   VECS_STORE_READ_PERMISSIONS[machine]='vpxd vsan-health'
		   VECS_STORE_READ_PERMISSIONS[hvc]='vpxd'
		   if [ $VC_BUILD -ge 19480866 ]; then
		      VECS_STORE_READ_PERMISSIONS[vpxd-extension]='vlcm wcp deploy updatemgr vsphere-ui vpxd vsm vsan-health imagebuilder content-library eam vstatsuser'
		      VECS_STORE_READ_PERMISSIONS[wcp]='wcp vpxd content-library'
		   else
		      VECS_STORE_READ_PERMISSIONS[vpxd-extension]='vlcm deploy updatemgr vsphere-ui vpxd vsm imagebuilder content-library eam vstatsuser'
		      VECS_STORE_READ_PERMISSIONS[wcp]='vpxd content-library'
		   fi
		   if [ $VC_BUILD -ge 20051473 ]; then
		      VECS_STORE_WRITE_PERMISSIONS[TRUSTED_ROOTS]='sps'
			   VECS_STORE_READ_PERMISSIONS[machine]+=' observability'
			   VECS_STORE_WRITE_PERMISSIONS[machine]='infraprofile certauth certmgr'
			   VECS_STORE_READ_PERMISSIONS[vsphere-webclient]+=' analytics'
			   VECS_STORE_WRITE_PERMISSIONS[vsphere-webclient]='infraprofile'
			   VECS_STORE_WRITE_PERMISSIONS[vpxd-extension]='infraprofile sps'
			   VECS_STORE_READ_PERMISSIONS[vpxd-extension]+=' analytics'
			   VECS_STORE_WRITE_PERMISSIONS[SMS]='sps'
		   fi
		   VECS_STORE_READ_PERMISSIONS[APPLMGMT_PASSWORD]='vpxd'
		   VECS_STORE_READ_PERMISSIONS[data-encipherment]='vpxd'
	      ;;
      '8.0')
         VECS_STORE_READ_PERMISSIONS[MACHINE_SSL_CERT]='vlcm updatemgr vsphere-ui vpxd vpostgres vsm vsan-health lighttpd'
         VECS_STORE_WRITE_PERMISSIONS[MACHINE_SSL_CERT]='rhttpproxy'
         VECS_STORE_WRITE_PERMISSIONS[TRUSTED_ROOTS]='rhttpproxy sps'
         if [ $VC_BUILD -lt 22385739 ]; then
            VECS_STORE_READ_PERMISSIONS[machine]='statsmon vsan-health sca vpxd observability'
         else
            VECS_STORE_READ_PERMISSIONS[machine]='vsan-health sca vpxd observability'
         fi
         VECS_STORE_WRITE_PERMISSIONS[machine]='certauth certmgr infraprofile sts'
         VECS_STORE_READ_PERMISSIONS[vsphere-webclient]+=' analytics'
         VECS_STORE_WRITE_PERMISSIONS[vsphere-webclient]='infraprofile'
         VECS_STORE_READ_PERMISSIONS[vpxd]+=' vsan-health'
         if [ $VC_BUILD -lt 22385739 ]; then
            VECS_STORE_READ_PERMISSIONS[vpxd-extension]='analytics content-library deploy eam imagebuilder updatemgr vlcm vpxd vsan-health vsm vsphere-ui vstatsuser wcp'
         else
            VECS_STORE_READ_PERMISSIONS[vpxd-extension]='analytics content-library eam imagebuilder updatemgr vlcm vpxd vsan-health vsm vsphere-ui vstatsuser wcp'
         fi
         VECS_STORE_WRITE_PERMISSIONS[vpxd-extension]='infraprofile sps'
         VECS_STORE_READ_PERMISSIONS[hvc]='vpxd'
         VECS_STORE_READ_PERMISSIONS[wcp]='wcp content-library'
         VECS_STORE_READ_PERMISSIONS[SMS]='deploy'
         VECS_STORE_WRITE_PERMISSIONS[SMS]='vpxd'
         if [ $VC_BUILD -ge 20920323 ]; then
            VECS_STORE_WRITE_PERMISSIONS[SMS]=''
            if [ $VC_BUILD -lt 21560480 ]; then
               VECS_STORE_WRITE_PERMISSIONS[TRUSTED_ROOTS]+=' hvc infraprofile'
               VECS_STORE_WRITE_PERMISSIONS[machine]+=' sts hvc'
               VECS_STORE_WRITE_PERMISSIONS[vsphere-webclient]+=' hvc'
               VECS_STORE_WRITE_PERMISSIONS[vpxd]='hvc'
               VECS_STORE_WRITE_PERMISSIONS[vpxd-extension]+=' hvc'
               VECS_STORE_WRITE_PERMISSIONS[hvc]='hvc'
               VECS_STORE_READ_PERMISSIONS[wcp]+=' vpxd'
               VECS_STORE_READ_PERMISSIONS[SMS]+=' vpxd'
            else
               VECS_STORE_WRITE_PERMISSIONS[TRUSTED_ROOTS]=''
               VECS_STORE_WRITE_PERMISSIONS[machine]='certauth certmgr infraprofile'
            fi
            
         fi
         
         ;;
   esac
}

#------------------------------
# Check if vCenter is managed by SDDC Manager
#------------------------------
function checkForVCF() {
   SDDC_MANAGER=$($LDAP_SEARCH -LLL -h $PSC_LOCATION -b "cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password '(objectclass=vmwSTSTenant)' vmwSTSLogonBanner | tr -d '\n' | awk -F'::' '{print $NF}' | tr -d ' ' | base64 -d 2>/dev/null | grep 'SDDC Manager' | awk -F '[()]' '{print $2}' | grep -v '^$')
}

#------------------------------
# Print warning about VCHA on the main operation menu
#------------------------------
function operationMenuSDDCWarning() {
   cat << EOF
${YELLOW}-------------------------!!! WARNING !!!-------------------------
This vCenter is managed by the following SDDC Manager:
   $SDDC_MANAGER

Updating certificates may require adding new
CA certificates to the SDDC Manager keystore.

See https://kb.vmware.com/s/article/78607 for details.$NORMAL

EOF
}

#------------------------------
# Get access token for running API calls to SDDC Manager
#------------------------------
function getSDDCAccessToken() {
   authenticateIfNeeded
   
   task 'Get API access token'
   SDDC_API_ACCESS_TOKEN_RESPONSE=$(curl -i -k -X POST https://$SDDC_MANAGER/v1/tokens -H 'Content-Type: application/json' -H 'Accept: application/json' -d "{'username' : '$VMDIR_USER_UPN', 'password' : '$(cat $STAGE_DIR/.vmdir-user-password)'}" 2>&1 | logDebug)
   if echo "$SDDC_API_ACCESS_TOKEN_RESPONSE" | grep '^HTTP' | grep '200' > /dev/null; then
      SDDC_API_ACCESS_TOKEN=$(echo "$SDDC_API_ACCESS_TOKEN_RESPONSE" | grep '^{' | jq . | grep accessToken | awk '{print $NF}' | tr -d '",')
	   statusMessage 'OK' 'GREEN'
   else
      errorMessage 'Unable to get access token from the SDDC Manager'
   fi   
}

#------------------------------
# Add new CA certificates to SDDC Manager via API
#------------------------------
function publishCACertsSDDCManager() {
   CA_CERT_STRING=$(cat $1 | awk '{printf "%s\\n", $0}' | sed -e 's/[\\n]*$//g')
   
   task 'Publish CA cert for outbound connections'
   logDebug "CA JSON string: $CA_CERT_STRING"
   if [ -n "$SDDC_API_ACCESS_TOKEN" ]; then
      SDDC_API_PUBLISH_CA_OUTBOUND_RESPONSE=$(curl -i -k -X POST https://$SDDC_MANAGER/v1/sddc-manager/trusted-certificates -H "Authorization: Bearer $SDDC_API_ACCESS_TOKEN" -H 'Content-Type: application/json' -H 'Accept: application/json' -d "{'certificate':'$CA_CERT_STRING','certificateUsageType':'TRUSTED_FOR_OUTBOUND'}" 2>&1 | logDebug)
      if echo "$SDDC_API_PUBLISH_CA_OUTBOUND_RESPONSE" | grep '^HTTP' | grep '200' > /dev/null; then
         statusMessage 'OK' 'GREEN'
      else
         logInfo "Publish CA to SDDC Manager response code (outbound trust): $SDDC_API_PUBLISH_CA_INBOUND_RESPONSE"
         errorMessage 'Unable to publish CA certificate to SDDC Manager'		 
      fi
      if [[ "$VC_VERSION" =~ ^[78] ]] && [ $VC_BUILD -ge 17327517 ]; then
         task 'Publish CA cert for inbound connections'
         SDDC_API_PUBLISH_CA_INBOUND_RESPONSE=$(curl -i -k -X POST https://$SDDC_MANAGER/v1/sddc-manager/trusted-certificates -H "Authorization: Bearer $SDDC_API_ACCESS_TOKEN" -H 'Content-Type: application/json' -H 'Accept: application/json' -d "{'certificate':'$CA_CERT_STRING','certificateUsageType':'TRUSTED_FOR_INBOUND'}" 2>&1 | logDebug)
         if echo "$SDDC_API_PUBLISH_CA_INBOUND_RESPONSE" | grep '^HTTP' | grep '200' > /dev/null; then
            statusMessage 'OK' 'GREEN'
         else
            logInfo "Publish CA to SDDC Manager response code (inbound trust): $SDDC_API_PUBLISH_CA_INBOUND_RESPONSE"
            errorMessage 'Unable to publish CA certificate to SDDC Manager'		
         fi
      fi
   else
      errorMessage 'No API access token found'
   fi
}

#------------------------------
# Print the operation menu
#------------------------------
function operationMenu() {
   UPDATED_MACHINE_SSL=0
   UPDATED_TRUST_ANCHORS=0

   header "vCenter $VC_VERSION Certificate Management Utility ($VERSION)"
   logInfo 'Printing Main Menu'
   cat << EOF
 1. Check current certificates status
 2. View certificate info
 3. Manage certificates
 4. Manage SSL trust anchors
 5. Check configurations
 6. Reset all certificates with VMCA-signed certificates
 7. ESXi certificate operations
 8. Restart services
 9. Generate certificate report
EOF

   if isVCHAConfigured; then echo ' I. vCenter High Availability information'; fi
   
   echo ' E. Exit'
   echo ''
   
   if isVCHAConfigured; then
      operationMenuVCHAWarning
   fi
   
   if [ -n "$SDDC_MANAGER" ]; then
      operationMenuSDDCWarning
   fi

   if [ $NODE_TYPE != 'management' ] && [ "$VMDIR_STATE" != 'Normal' ] && [ "$VMDIR_STATE" != 'Standalone' ]; then
      echo "${YELLOW}The VMware Directory service is not in NORMAL mode ($VMDIR_STATE)!"
	   echo 'Certificate operations should not be actioned until this service'
	   echo "is running correctly in a NORMAL state.${NORMAL}"
	   exit      
   else
      if [ "$NODE_TYPE" != 'infrastructure' ] && ! checkService 'vmware-vpostgres'; then
         echo "${YELLOW}The vPostgres service is stopped!"
         echo "Many certificate operations require changes to the vCenter database."
         echo "Please ensure this service is running before replacing any certificates."
         echo "Hint: Check the number of CRL entries in VECS${NORMAL}"
         echo ''
      fi
      read -p 'Select an option [1]: ' OPERATION

      if [ -z $OPERATION ]; then OPERATION=1; fi

      logInfo "User selected option '$OPERATION'"
   fi
}

#------------------------------
# Check if VCHA is configured
#------------------------------
function isVCHAConfigured() {
   if cat /storage/vmware-vmon/defaultStartProfile | grep 'HACore' > /dev/null; then
      VMON_SERVICE_PROFILE='--vmon-profile HAActive'
      return 0
   else
      VMON_SERVICE_PROFILE='--all'
      return 1
   fi
}

#------------------------------
# Get current VCHA mode
#------------------------------
function getVCHAMode() {
   authenticateIfNeeded
   VCHA_MODE='UNKNOWN'
   SESSION_HEADER=$(curl -k -i -u "$VMDIR_USER_UPN:$(cat $STAGE_DIR/.vmdir-user-password)" -X POST -c $STAGE_DIR/session-info.txt https://localhost/rest/com/vmware/cis/session 2>/dev/null)
   if echo "$SESSION_HEADER" | grep '^HTTP' | grep '200' > /dev/null; then
      if [[ "$VC_VERSION" =~ ^6 ]]; then
	     VCHA_MODE_INFO=$(curl -k -b $STAGE_DIR/session-info.txt https://localhost/rest/vcenter/vcha/cluster/mode 2>/dev/null | python -m json.tool --sort-keys)		 
	  else
	     VCHA_MODE_INFO=$(curl -k -b $STAGE_DIR/session-info.txt https://localhost/rest/vcenter/vcha/cluster/mode 2>/dev/null | jq .)
	  fi
	  logDebug "VCHA Mode API call: $VCHA_MODE_INFO"
      VCHA_MODE=$(echo "$VCHA_MODE_INFO" | grep 'mode' | awk '{print $NF}' | tr -d '"')     
   fi
   logInfo "VCHA Mode: $VCHA_MODE"
}

#------------------------------
# Print warning about VCHA on the main operation menu
#------------------------------
function operationMenuVCHAWarning() {
   echo "${YELLOW}-------------------------!!! WARNING !!!-------------------------"
   printf 'vCenter High Availability has been configured,'
   if service-control --status vmware-vcha | grep -i stopped; then
      printf " but the\nservice is currently stopped. "
   else
      printf " and the\nservice is currently running. "
   fi
   printf "\n\nRestarting services may trigger a failover.\nFor more information, select option 'I' from the menu.\n\n${NORMAL}"
}

#------------------------------
# Print VCHA information
#------------------------------
function VCHAInfo() {
   if [ $NODE_TYPE != 'infrastructure' ]; then
   getVCHAMode
   cat << EOF

${YELLOW}The supported methods of replacing SSL certificates with 
vCenter High Availability configured are:

   1. Place VCHA into Maintenance Mode so restarting 
      services does not trigger an automatic failover, or
	  
   2. Destroy the VCHA cluster, replace the SSL certificate(s), 
      and re-create the VCHA cluster   
EOF
      case $VCHA_MODE in
	      'MAINTENANCE')
		      cat << EOF

The VCHA cluster is in Maintenance Mode, so you should be able 
to proceed with the certificate replacement.${NORMAL}
			
EOF
            if checkService 'vmware-vpxd'; then
               read -p 'Place VCHA cluster into Enabled Mode? [n]: ' VCHA_SET_MM_INPUT

               if [[ "$VCHA_SET_MM_INPUT" =~ ^[Yy] ]]; then
			         header 'vCenter High Availability Mode'
			         task 'Put VCHA cluster into Enabled Mode'
			         VCHA_MM_API=$(curl -k -i -b $STAGE_DIR/session-info.txt -H 'Content-Type: application/json' -X PUT https://localhost/rest/vcenter/vcha/cluster/mode?vmw-task=true -d '{"mode":"ENABLED"}' 2>/dev/null)
			         logDebug "$VCHA_MM_API"
			         if echo "$VCHA_MM_API" | grep '^HTTP' | grep '200' > /dev/null; then
			            statusMessage 'OK' 'GREEN'
			         else
			            errorMessage 'Unable to place VCHA cluster into Enabled Mode'
			         fi
			      fi
			   fi
		      ;;
		 
		   'ENABLED')
		      cat << EOF

The VCHA cluster is in Enabled Mode, so restarting 
services will trigger a failover to the Passive Node. 
It is recommended to place the VCHA cluster into 
Maintenance Mode before performing operations on any 
SSL certificates.${NORMAL}
			
EOF
            if checkService 'vmware-vpxd'; then
               read -p 'Place VCHA cluster into Maintenance Mode? [n]: ' VCHA_SET_MM_INPUT

               if [[ "$VCHA_SET_MM_INPUT" =~ ^[Yy] ]]; then
			         header 'vCenter High Availability Mode'
			         task 'Put VCHA cluster into Maintenance Mode'
			         VCHA_MM_API=$(curl -k -i -b $STAGE_DIR/session-info.txt -H 'Content-Type: application/json' -X PUT https://localhost/rest/vcenter/vcha/cluster/mode?vmw-task=true -d '{"mode":"MAINTENANCE"}' 2>/dev/null)
			         logDebug "$VCHA_MM_API"
			         if echo "$VCHA_MM_API" | grep '^HTTP' | grep '200' > /dev/null; then
			            statusMessage 'OK' 'GREEN'
			         else
			            errorMessage 'Unable to place VCHA cluster into Maintenance Mode'
			         fi
			      fi
			   fi
		      ;;
		 
		   'UNKNOWN')
		      cat << EOF
			
The state of the VCHA cluster cannot be determined 
via the REST API. It is recommended to destroy the 
VCHA cluster before performing operations on any 
SSL certificates.${NORMAL}
EOF
			
		      ;;
	   esac
   else
      printf "\n${YELLOW}Invalid operation${NORMAL}\n\n"
   fi
}

#------------------------------
# Process the operation selected by user
#------------------------------
function processOperationMenu() {
   setTimestamp
   
   if [[ $OPERATION =~ ^[Ee] ]]; then 
      cleanup
      exit
   fi
   
   if [[ $OPERATION =~ ^[Ii] ]]; then
      VCHAInfo
   elif [[ "$OPERATION" =~ ^[0-9]+$ ]]; then
      echo ''

      case $OPERATION in
         1)
            checkCerts
            ;;
         2)
            viewCertificateMenu
            ;;      
         3)
            manageCertificateMenu
            ;;         
         4)
            manageSSLTrustAnchors
            ;;         
         5)
            checkConfigurationMenu
            ;;         
         6)
            resetAllCertificates            
            ;;         
		   7)
		      manageESXiCertificates
		      ;;		 
         8)
            restartServicesMenu
            ;;		 
		   9)
		      #viewCertificateReportMenu
			   generatevCenterCertificateReport
		      ;;         
         *)
            echo $'\n'"${YELLOW}Invalid operation${NORMAL}"
            ;;
      esac      
   else
      echo $'\n'"${YELLOW}Invalid operation${NORMAL}"
   fi
   operationMenu
   processOperationMenu
}

#------------------------------
# Perform quick check of certificates
#------------------------------
function checkCerts() {
   authenticateIfNeeded
      
   resetCertStatusChecks
   
   header 'Checking Certifcate Status'
   
   task 'Checking Machine SSL certificate'
   checkVECSCert 'MACHINE_SSL_CERT' '__MACHINE_CERT'
   
   if checkMachineSSLCSR; then
      task 'Checking Machine SSL CSR'
	  checkVECSCert 'MACHINE_SSL_CERT' '__MACHINE_CSR'
   fi

   echo 'Checking Solution User certificates:'
   for soluser in "${SOLUTION_USERS[@]}"; do
      task "   $soluser"
      checkVECSCert "$soluser" "$soluser"
   done   
  
   if [ $NODE_TYPE != 'infrastructure' ]; then   
      task 'Checking SMS self-signed certificate'
      checkVECSCert 'SMS' 'sms_self_signed'

      if [[ "$VC_VERSION" =~ ^8 ]]; then
         task 'Checking SMS VMCA-signed certificate'
         checkVECSCert 'SMS' 'sps-extension'
      fi
	  
      if [ "$VC_VERSION" != '6.5' ]; then
         task 'Checking data-encipherment certificate'
         checkVECSCert 'data-encipherment' 'data-encipherment'
      fi
      
      task 'Checking Authentication Proxy certificate'
      checkFilesystemCert '/var/lib/vmware/vmcam/ssl/vmcamcert.pem'
      
      task 'Checking Auto Deploy CA certificate'
      checkFilesystemCert '/etc/vmware-rbd/ssl/rbd-ca.crt'
   fi
   
   if checkVECSStore 'BACKUP_STORE'; then
      echo 'Checking BACKUP_STORE entries:'
      for alias in $($VECS_CLI entry list --store BACKUP_STORE | grep Alias | awk '{print $NF}'); do
         task "   $alias"
         checkVECSCert 'BACKUP_STORE' $alias
      done
   fi
   if checkVECSStore 'BACKUP_STORE_H5C'; then
      echo 'Checking BACKUP_STORE_H5C entries:'
	  for alias in $($VECS_CLI entry list --store BACKUP_STORE_H5C | grep Alias | awk '{print $NF}'); do
         task "   $alias"
         checkVECSCert 'BACKUP_STORE_H5C' $alias
      done
	  
   fi
   if checkVECSStore 'STS_INTERNAL_SSL_CERT'; then
      task 'Checking legacy Lookup Service certificate'
      checkVECSCert 'STS_INTERNAL_SSL_CERT' '__MACHINE_CERT'
   fi
   
   if [ $NODE_TYPE != 'management' ]; then
      if [ -f '/usr/lib/vmware-vmdir/share/config/vmdircert.pem' ]; then
         task 'Checking VMDir certificate'
         checkFilesystemCert '/usr/lib/vmware-vmdir/share/config/vmdircert.pem'
	  fi
      
      task 'Checking VMCA certificate'
      checkFilesystemCert '/var/lib/vmware/vmca/root.cer'
      header 'Checking STS Signing Certs & Signing Chains'
      manageSTSTenantCerts 'Check'
   fi
   
   checkCACertificates
   
   checkSMSVASACerts
   
   quickCheckVECSStores
   
   quickCheckServicePrincipals
   
   checkCRLs
   
   manageCACCerts 'Check'
   
   manageLDAPSCerts 'Check'
   
   manageTanzuSupervisorClusterCerts 'Check'
   
   quickCheckSSLTrustAnchors
   
   if [ $NODE_TYPE != 'infrastructure' ]; then 
      manageVCExtensionThumbprints 'Checking'
	   checkAutoDeployDB
	   checkVMCADatabaseConfig
   fi

   if [ $NODE_TYPE != 'management' ]; then
      quickCheckSTSConfig
   fi
   
   buildCertificateStatusMessage
   
   if [ -n "$CERT_STATUS_MESSAGE" ]; then
      echo $'\n'"${YELLOW}------------------------!!! Attention !!!------------------------ "
      echo "$CERT_STATUS_MESSAGE${NORMAL}"    
   fi
}

#------------------------------
# Resets the certificate status flags
#------------------------------
function resetCertStatusChecks() {
   CERT_STATUS_MESSAGE=''
   CERT_STATUS_EXPIRES_SOON=0
   CERT_STATUS_MISSING_PNID=0
   CERT_STATUS_MISSING_SAN=0
   CERT_STATUS_KEY_USAGE=0
   CERT_STATUS_EXPIRED=0
   CERT_STATUS_NON_CA=0
   CERT_STATUS_BAD_ALIAS=0
   CERT_STATUS_SHA1_SIGNING=0
   CERT_STATUS_MISSING=0
   CERT_STATUS_MISSING_VMDIR=0
   CERT_STATUS_MISMATCH_SERVICE_PRINCIPAL=0
   CERT_STATUS_TOO_MANY_CRLS=0
   CERT_STATUS_MISSING_CA=0
   CERT_STATUS_EXPIRED_EMBEDDED_CA=0
   CERT_STATUS_STORE_MISSING=0
   CERT_STATUS_STORE_PERMISSIONS=0
   CERT_STATUS_SERVICE_PRINCIPAL_MISSING=0
   CERT_STATUS_VMCA_EMPTY_CONFIG=0
   CERT_STATUS_VMCA_MODE=0 
   CERT_STATUS_CLIENT_CA_LIST_FILE_LOCATION=0
   CERT_STATUS_CLIENT_CA_LIST_FILE_MISSING=0
   CERT_STATUS_STS_VECS_CONFIG=0
   CERT_STATUS_STS_CONNECTION_STRINGS=0
   CERT_STATUS_UNSUPPORTED_SIGNATURE_ALGORITHM=0
   CERT_STATUS_CA_MISSING_SKID=0
}

#------------------------------
# Checks on certificates in VECS
#------------------------------
function checkVECSCert() {
   KU_LIST='Digital Signature Key Encipherment Key Agreement Data Encipherment Non Repudiation'
   case $1 in
      'MACHINE_SSL_CERT')
         CHECK_PNID=1
         CHECK_KU=1
		   CHECK_SAN=1
         CHECK_SHA1=1
         CHECK_SERVICE_PRINCIPAL=0
		   CHECK_CA_CHAIN=1
		   CHECK_EMBEDDED_CHAIN=1
         ;;     

      SMS)
         CHECK_PNID=0
         CHECK_KU=0
         CHECK_SAN=0
         CHECK_SHA1=0
         CHECK_SERVICE_PRINCIPAL=0
		   CHECK_CA_CHAIN=0
		   CHECK_EMBEDDED_CHAIN=0
         ;;
      
      *)
         CHECK_PNID=0
         CHECK_KU=1
		   CHECK_SAN=1
         CHECK_SHA1=1
		   CHECK_CA_CHAIN=1
		   CHECK_EMBEDDED_CHAIN=1
         CHECK_SERVICE_PRINCIPAL=0
         if [[ " ${SOLUTION_USERS[*]} " =~ " $1 " ]]; then CHECK_SERVICE_PRINCIPAL=1; fi
		   if [ "$1" == 'wcp' ]; then CHECK_SAN=0; fi
         ;;
   esac
   logInfo "Checking VECS for store '$2'"
   if ! $VECS_CLI entry list --store $1 | grep Alias | grep $2 > /dev/null 2>&1; then
      CERT_STATUS_MISSING=1
	   statusMessage 'NOT FOUND' 'RED'
      logError "Store '$2' not found"
	   return 1
   else 
      logInfo "Found Store '$2'"
   fi
   
   TEMP_CERT=$($VECS_CLI entry getcert --store $1 --alias $2 2>/dev/null)
   
   if [ -z "$TEMP_CERT" ]; then 
      statusMessage 'PROBLEM' 'RED'
      logError "No certificate found for alias '$2' in store '$1'"
      return 1
   fi

   if ! isExpired "$TEMP_CERT" 'hash'; then
      DAYS_LEFT=$(checkCertExpireSoon "$TEMP_CERT")
      if [[ $DAYS_LEFT -ge 0 ]]; then
         CERT_STATUS_EXPIRES_SOON=1   
         statusMessage "$DAYS_LEFT DAYS" 'YELLOW'
         logInfo "Certificate for alias '$2' in store '$1' expires in $DAYS_LEFT days"
         return 0
      else
         if [ $CHECK_PNID = 1 ]; then
            if ! echo "$TEMP_CERT" | openssl x509 -noout -text 2>&1 | grep -A1 'Subject Alternative Name' | grep -i "$PNID" > /dev/null; then
               CERT_STATUS_MISSING_PNID=1
               statusMessage 'NO PNID' 'YELLOW' 
               logInfo "Certificate for alias '$2' in store '$1' does not have the PNID ($PNID) in the Subject Alternative Name field"
               return 0
            fi
         fi
         if [ $CHECK_KU = 1 ]; then
            if ! checkCertKeyUsage "$TEMP_CERT" "$1:$2" "$KU_LIST"; then
               CERT_STATUS_KEY_USAGE=1
               statusMessage 'KEY USAGE' 'YELLOW'
               logInfo "Certificate for alias '$2' in store '$1' does not have the expected Key Usage Values"           
               return 0
            fi             
         fi
         if [ $CHECK_SAN = 1 ]; then
            if ! echo "$TEMP_CERT" | openssl x509 -noout -text 2>&1 | grep 'Subject Alternative Name' > /dev/null; then
               CERT_STATUS_MISSING_SAN=1
               statusMessage 'NO SAN' 'YELLOW'
               logInfo "Certificate for alias '$2' in store '$1' has no values in the Subject Alternative Name field"
               return 0
            fi
         fi
         if [ $CHECK_SERVICE_PRINCIPAL = 1 ]; then
            if ! checkServicePrincipalCert "$1"; then                          
               CERT_STATUS_MISMATCH_SERVICE_PRINCIPAL=1
               statusMessage 'MISMATCH' 'YELLOW'
               logError "The certificate in the VECS store '$1' does not match the certificate for the corresponding Service Principal in VMware Directory"
               return 0
            fi
         fi
		   if [ $CHECK_CA_CHAIN = 1 ]; then
		      if ! checkCACertsPresent "$TEMP_CERT"; then
			      CERT_STATUS_MISSING_CA=1
			      statusMessage 'MISSING CA' 'YELLOW'
               logError "One of the CA certificates in the signing chain for the certificate for alias '$2' in store '$1' is missing"
			      return 0
			   fi
		   fi
		   if [ $CHECK_EMBEDDED_CHAIN = 1 ]; then
		      if ! checkEmbeddedChain "$TEMP_CERT"; then
			      CERT_STATUS_EXPIRED_EMBEDDED_CA=1
			      statusMessage 'EMBEDDED CA' 'YELLOW'
			      return 0
			   fi
		   fi
         if ! checkCertSignatureAlgorithm "$TEMP_CERT"; then
            CERT_STATUS_UNSUPPORTED_SIGNATURE_ALGORITHM=1
            statusMessage 'ALGORITHM' 'YELLOW'
            logInfo "Certificate for alias '$2' in store '$1' is signed with an unsupported Signature Algorithm" 
            return 0
         fi
         statusMessage 'VALID' 'GREEN'      
         return 0
      fi
   else
      CERT_STATUS_EXPIRED=1
      statusMessage 'EXPIRED' 'YELLOW'
      logError "Certificate for alias '$2' in store '$1' is expired"     
      return 1
   fi   
}

#------------------------------
# Check for the existence of the __MACHINE_CSR alias in VECS
#------------------------------
function checkMachineSSLCSR() {
   if $VECS_CLI entry list --store 'MACHINE_SSL_CERT' | grep Alias | grep '__MACHINE_CSR' > /dev/null 2>&1; then
      return 0
   else
      return 1
   fi
}

#------------------------------
# Check Solution User cert in VECS matches Service Principal
#------------------------------
function checkServicePrincipalCert() {
   VECS_THUMBPRINT=$($VECS_CLI entry getcert --store $1 --alias $1 2>&1 | openssl x509 -noout -fingerprint -sha1 2>&1)
   SERVICE_PRINCIPAL_HASH=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$1-$MACHINE_ID,cn=ServicePrincipals,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password userCertificate 2>&1 | grep '^userCertificate' | awk '{print $NF}')
   SERVICE_PRINCIPAL_CERT=$(buildCertFromHash "$SERVICE_PRINCIPAL_HASH")
   SERVICE_PRINCIPAL_THUMBPRINT=$(echo "$SERVICE_PRINCIPAL_CERT" | openssl x509 -noout -fingerprint -sha1 2>&1)
   logInfo "Checking Service Principal: VECS Thumbprint: $VECS_THUMBPRINT"
   logInfo "Checking Service Principal: Service Principal Thumbprint: $SERVICE_PRINCIPAL_THUMBPRINT"
   
   if [ "$VECS_THUMBPRINT" = "$SERVICE_PRINCIPAL_THUMBPRINT" ]; then
      return 0
   else
      return 1
   fi  
}

#------------------------------
# Check if certificate on the file system has expired
#------------------------------
function checkFilesystemCert() {
   if [ ! -f $1 ]; then
      CERT_STATUS_MISSING=1
      statusMessage 'NOT FOUND' 'RED'
      logError "Certificate at $1 could not be found"
      return 1
   fi
   logInfo "Checking certificate at $1"
   FS_CERT=$(cat $1)
   checkCert "$FS_CERT"
}

#------------------------------
# Check if certificate has expired
#------------------------------
function checkCert() {
   logInfo 'Checking the following certificate'
   logDetails "$1"
   logDebugDetails "'$(echo $1 | openssl x509 -noout -text 2>/dev/null)'"
   if ! isExpired "$1" 'hash'; then
      DAYS_LEFT=$(checkCertExpireSoon "$1")
      if [[ $DAYS_LEFT -gt 0 ]]; then
         CERT_STATUS_EXPIRES_SOON=1   
         statusMessage "$DAYS_LEFT DAYS" 'YELLOW'
         logInfo "Certificate expires in $DAYS_LEFT days"
         return 0
      else
         if ! checkCertSignatureAlgorithm "$1"; then
            CERT_STATUS_UNSUPPORTED_SIGNATURE_ALGORITHM=1
            statusMessage 'ALGORITHM' 'YELLOW'
            return 1
         fi
         statusMessage 'VALID' 'GREEN'
         logInfo 'Certificate is valid'
         return 0
      fi
   else
      CERT_STATUS_EXPIRED=1
      statusMessage 'EXPIRED' 'YELLOW'
      logError 'Certificate is expired'   
      return 1
   fi
}

#------------------------------
# Check if a certificate is expired
#------------------------------
function isExpired() {
   if [ "$2" == 'file' ]; then
      HASH=$(cat "$1")
   else
      HASH="$1"
   fi
   logInfo 'Checking expiration of the following certificate'
   logDetails "$HASH"
   if echo "$HASH" | openssl x509 -noout -checkend 0 > /dev/null 2>&1; then
      return 1
   else
      return 0
   fi
}

#------------------------------
# Backup certificate and key from filesystem
#------------------------------
function backupFilesystemCertKey() {
   task 'Backing up certificate and private key'
   
   if [ -f $1 ]; then
     cp $1 $BACKUP_DIR/$3-$TIMESTAMP.crt 2>&1 | logDebug || errorMessage "Unable to backup $3 certificate"
   else
      statusMessage 'NOT FOUND' 'YELLOW'
      logError "Certificate not found at $1"
   fi
   
   if [ -f $2 ]; then
      cp $2 $BACKUP_DIR/$3-$TIMESTAMP.key 2>&1 | logDebug || errorMessage "Unable to backup $3 key"
   else
      statusMessage 'NOT FOUND' 'YELLOW'
      logError "Private key not found at $2"
   fi
   statusMessage 'OK' 'GREEN'
   logInfo "Certificate and key backed up to $BACKUP_DIR/$3-$TIMESTAMP.crt and $BACKUP_DIR/$3-$TIMESTAMP.key"
}

#------------------------------
# Check if cert has unsupported signature algorithms
#------------------------------
function checkCertSignatureAlgorithm() {
   CERT_HASH=$1
   CERT_SIGNATURE_ALGORITHM=$(echo "$CERT_HASH" | openssl x509 -noout -text | grep 'Signature Algorithm' | head -n1 | awk '{print $NF}')
   logInfo "Checking certificate signature algorithm '$CERT_SIGNATURE_ALGORITHM' against unsupported signature algorithms ($UNSUPPORTED_SIGNATURE_ALGORITHMS)"
   if echo "$CERT_SIGNATURE_ALGORITHM" | grep -iE "$UNSUPPORTED_SIGNATURE_ALGORITHMS" > /dev/null; then
      return 1
   else
      return 0
   fi
}

#------------------------------
# Check if cert has recommended Key Usage
#------------------------------
function checkCertKeyUsage() {
   CERT_HASH=$1
   CERT_DESCRIPTION=$2
   KU_LIST=$3
   UNSUPPORTED_KEY_USAGE=0
   
   if ! echo "$CERT_HASH" | openssl x509 -text -noout 2>/dev/null | grep 'X509v3 Key Usage' > /dev/null; then
      return 0
   fi

   logInfo "Checking Key Usage for cert $CERT_DESCRIPTION among supported values of: $KU_LIST"
   
   KEY_USAGE_SEARCH=$(echo "$CERT_HASH" | openssl x509 -text -noout 2>/dev/null | grep -A1 'X509v3 Key Usage' | tail -n1 | sed -e 's/^[[:space:]]*//' -e 's/, /\n/g')
   
   IFS=$'\n'
   for key_usage in $KEY_USAGE_SEARCH; do
      KEY_USAGE_SEARCH_RESULT=$(echo "$KU_LIST" | grep "$key_usage")
      if [ -z "$KEY_USAGE_SEARCH_RESULT" ]; then
	     logInfo "Found unsupported Key Usage value: $key_usage"
	     UNSUPPORTED_KEY_USAGE=1
	  else
	     logInfo "Found supported Key Usage value: $key_usage"
	  fi
   done
   IFS=$' \t\n'
   if [ "$UNSUPPORTED_KEY_USAGE" == 1 ]; then
      return 1
   else
      return 0
   fi
}

#------------------------------
# Check if cert is expiring within 30 days
#------------------------------
function checkCertExpireSoon() {
   if ! echo "$1" | openssl x509 -noout -checkend 2592000 > /dev/null 2>&1; then
      CERT_END_DATE=$(echo "$1" | openssl x509 -noout -enddate 2>/dev/null | sed "s/.*=\(.*\)/\1/")
      CERT_END_EPOCH=$(date -d "$CERT_END_DATE" +%s)
      NOW_EPOCH=$(date -d now +%s)
      DAYS_LEFT=$(( (CERT_END_EPOCH - NOW_EPOCH) / 86400))
      
      echo "$DAYS_LEFT"
   else
      echo '-1'
   fi
}

#------------------------------
# Check VASA Provider certs in SMS store
#------------------------------
function checkSMSVASACerts() {
   if [ $NODE_TYPE != 'infrastructure' ]; then
      SMS_VASA_ENTRIES=$($VECS_CLI entry list --store SMS | grep Alias | sed -e 's/Alias ://g' -e 's/^[[:space:]]*//g' | grep -vE '^sms_self_signed$|^sps-extension$')
      if [ -n "$SMS_VASA_ENTRIES" ]; then
         header 'Checking Additional Entries in SMS Store'
	  
         for alias in $SMS_VASA_ENTRIES; do
            task "$alias"
            checkVECSCert 'SMS' "$alias"
         done
      fi
   fi
}

#------------------------------
# Quick check of VECS store status and permissions
#------------------------------
function quickCheckVECSStores() {
   header 'Checking VECS Stores'
   echo 'Checking status and permissions for VECS stores:'
   logInfo 'Checking status and permissions for VECS stores'
   MISSING_STORES=''
   declare -gA MISSING_STORE_READ_PERMISSIONS=()
   declare -gA MISSING_STORE_WRITE_PERMISSIONS=()
   
   for store in $VECS_STORES; do
      task "   $store"
	   if ! checkVECSStore $store; then
	      CERT_STATUS_STORE_MISSING=1
		   if [ -z "$MISSING_STORES" ]; then MISSING_STORES+="$store"; else MISSING_STORES+=" $store"; fi
		   statusMessage 'MISSING' 'YELLOW'
         logError "Could not find store '$store' in VECS"
	   else
	      PERMISSIONS_OK=1
	      STORE_PERMISSIONS=$($VECS_CLI store get-permissions --name $store)
		   STORE_PERMISSIONS_FORMATTED=$'\n'$(echo "$STORE_PERMISSIONS" | head -n2)
		   STORE_PERMISSIONS_FORMATTED+=$'\n'$(echo "$STORE_PERMISSIONS" | tail -n+3 | column -t)
         logInfo "Permissions for VECS store $store:"
		   logDetails "$STORE_PERMISSIONS_FORMATTED"
		   logInfo "Users with expected read permissions: ${VECS_STORE_READ_PERMISSIONS[$store]}"
		   logInfo "Users with expected write permissions: ${VECS_STORE_WRITE_PERMISSIONS[$store]}"
		   for user in ${VECS_STORE_READ_PERMISSIONS[$store]}; do
		      if ! echo "$STORE_PERMISSIONS" | grep $user | grep 'read' > /dev/null; then
			      logInfo "Could not find read permission for user $user in VECS store $store"
			      if [ -z ${MISSING_STORE_READ_PERMISSIONS[$store]} ]; then MISSING_STORE_READ_PERMISSIONS[$store]="$user"; else MISSING_STORE_READ_PERMISSIONS[$store]=" $user"; fi
			      PERMISSIONS_OK=0
			   else
			      logInfo "Found read permission for user $user in VECS store $store"
			   fi			
		   done
		   if [[ "$VC_VERSION" =~ ^[78] ]] && [ $VC_BUILD -ge 20051473 ]; then
		      for user in ${VECS_STORE_WRITE_PERMISSIONS[$store]}; do
			      if ! echo "$STORE_PERMISSIONS" | grep $user | grep -E 'OWNER|write' > /dev/null; then                  
			         logInfo "Could not find write permission for user $user in VECS store $store"
				      if [ -z ${MISSING_STORE_WRITE_PERMISSIONS[$store]} ]; then MISSING_STORE_WRITE_PERMISSIONS[$store]="$user"; else MISSING_STORE_WRITE_PERMISSIONS[$store]=" $user"; fi
				      PERMISSIONS_OK=0
			      else
			         logInfo "Found write permission for user $user in VECS store $store"
			      fi
			   done
		   fi
		   if [ $PERMISSIONS_OK == 1 ]; then
		      statusMessage 'OK' 'GREEN'
		   else
		      CERT_STATUS_STORE_PERMISSIONS=1
			   statusMessage 'PERMISSIONS' 'YELLOW'
		   fi
	   fi
   done
}

#------------------------------
# Check and remediation of VECS store status and permissions
#------------------------------
function checkVECSStores() {
   quickCheckVECSStores
   unset RECREATE_VECS_STORES_INPUT
   unset FIX_VECS_STORES_PERMISSIONS
   
   if [ -n "$MISSING_STORES" ]; then
      read -p $'\n'"Some VECS stores are missing, recreate them? [n]: " RECREATE_VECS_STORES_INPUT
	  if [[ $RECREATE_VECS_STORES_INPUT =~ ^[Yy] ]]; then recreateMissingVECSStores; fi
   fi
   
   if [[ ${MISSING_STORE_READ_PERMISSIONS[*]} ]] || [[ ${MISSING_STORE_WRITE_PERMISSIONS[*]} ]]; then
      read -p $'\n'"Some VECS stores are missing expected permissions, reassign them? [n]: " FIX_VECS_STORES_PERMISSIONS
      if [[ $FIX_VECS_STORES_PERMISSIONS =~ ^[Yy] ]]; then  fixVECSStorePermissions; fi
   fi   
}

#------------------------------
# Recreate missing VECS store
#------------------------------
function recreateMissingVECSStores() {
   header 'Recreate missing VECS stores'
   for store in $MISSING_STORES; do
      task "Recreate store $store"
	   if [ "$store" == "SMS" ]; then
	      vmon-cli -r sps > /dev/null 2>&1 || errorMessage "Unable to create the VECS store SMS by restarting the sps service"
	   else
	      $VECS_CLI store create --name $store > /dev/null 2>&1 || errorMessage "Unable to create VECS store $store"
	   fi
	   statusMessage 'OK' 'GREEN'
	   echo 'Assigning permissions:'
	   for user in ${VECS_STORE_READ_PERMISSIONS[$store]}; do
	      task "   Read permmisson for user $user"
		   $VECS_CLI store permission --name $store --user $user --grant read > /dev/null 2>&1 || errorMessage "Unable to assign read permission to user $user on store $store"
		   statusMessage 'OK' 'GREEN'
	   done
	   if [[ "$VC_VERSION" =~ ^[78] ]] && [ $VC_BUILD -ge 20051473 ]; then
	      for user in ${VECS_STORE_WRITE_PERMISSIONS[$store]}; do
		      task "   Write permmisson for user $user"
		      $VECS_CLI store permission --name $store --user $user --grant write > /dev/null 2>&1 || errorMessage "Unable to assign write permission to user $user on store $store"
		      statusMessage 'OK' 'GREEN'
		   done
	   fi
   done
}

#------------------------------
# Recreate missing VECS store permissions
#------------------------------
function fixVECSStorePermissions() {
   header 'Fix VECS store permissions'
   logInfo "Stores missing read permissions: ${!MISSING_STORE_READ_PERMISSIONS[*]}"
   for store in "${!MISSING_STORE_READ_PERMISSIONS[@]}"; do
      echo "Assign read permissions on store $store:"
	  for user in ${MISSING_STORE_READ_PERMISSIONS[$store]}; do
	     task "   Read permission for user $user"
		 $VECS_CLI store permission --name $store --user $user --grant read > /dev/null 2>&1 || errorMessage "Unable to assign read permission to user $user on store $store"
		 statusMessage 'OK' 'GREEN'
	  done
   done
   if [[ "$VC_VERSION" =~ ^[78] ]] && [ $VC_BUILD -ge 20051473 ]; then
      logInfo "Stores missing write permissions: ${!MISSING_STORE_WRITE_PERMISSIONS[*]}"
      for store in "${!MISSING_STORE_WRITE_PERMISSIONS[@]}"; do
	      echo "Assign write permissions on store $store:"
	      for user in ${MISSING_STORE_WRITE_PERMISSIONS[$store]}; do
		      task "   Write permission for user $user"
			   $VECS_CLI store permission --name $store --user $user --grant write > /dev/null 2>&1 || errorMessage "Unable to assign write permission to user $user on store $store"
		      statusMessage 'OK' 'GREEN'
		   done
	   done
   fi
}

#------------------------------
# Check if a particular VECS store is present
#------------------------------
function checkVECSStore() {
   if $VECS_CLI store list | grep "^$1\$" > /dev/null; then
      return 0
   else
      return 1
   fi
}

#------------------------------
# Check if a particular entry exists in a VECS store
#------------------------------
function checkVECSEntry() {
   if $VECS_CLI entry list --store "$1" | grep "$2\$" > /dev/null; then
      return 0
   else
      return 1
   fi
}

#------------------------------
# Manage STS Signing certificates
#------------------------------
function manageSTSTenantCerts() {
   case $1 in
      'Check')      
         checkSTSTenantCerts
         checkSTSTrustedCertChains
      ;;
      
      'View')
         viewSTSTenantCerts
      ;;
      
      'Replace')
         authenticateIfNeeded
         viewSTSTenantCerts
         if promptReplaceSTS; then
            if replaceSSOSTSCert; then promptRestartVMwareServices; fi
         fi
      ;;
   esac
}

#------------------------------
# Manage Smart Card (CAC) certificates
#------------------------------
function manageCACCerts() {
   case $1 in
      'Check')
         if configuredForCAC; then
            checkRhttpproxyCACCerts
            checkVMDirCACCerts
         fi
         ;;   
      'View')
         if configuredForCAC; then
            viewRhttpproxyCACCerts
            viewVMDirCACCerts
         else
            echo $'\n'"${YELLOW}This vCenter Server is not configured for Smart Card authentication${NORMAL}"
         fi
         ;;      
      'Manage')
         if configuredForCAC; then
            viewRhttpproxyCACCerts
            viewVMDirCACCerts
            header 'Manage Smart Card Issuing CA Certificates'
            cat << EOF
1. Add Smart Card issuing CA certificate(s)
   to Reverse Proxy filter file
2. Remove Smart Card issuing CA certificate(s)
   from Reverse Proxy filter file
3. Add Smart Card issuing CA certificate(s)
   to VMware Directory
4. Remove Smart Card issuing CA certificate(s)
   from VMware Directory   
EOF
            read -p $'\n'"Enter selection [Return to Main Menu]: " MANAGE_CAC_INPUT
            
            case $MANAGE_CAC_INPUT in
               1)
                  addCACCertsFilterFile
                  ;;               
               2)
                  removeCACCertsFilterFile
                  ;;			   
               3)
                  updateSSOCACConfig 'add'
                  ;;               
               4)
                  updateSSOCACConfig 'remove'
                  ;;
            esac
         else
            echo $'\n'"This vCenter Server is not configured for Smart Card authentication"
            read -t $READ_TIMEOUTS -p $'\nConfigure vCenter Server for Smart Card authentication? [n]: ' CONFIGURE_CAC_INPUT
            
            if [ $? -lt 128 ]; then
               if [[ "$CONFIGURE_CAC_INPUT" =~ ^[Yy] ]]; then configureCACAuthentication; fi
            else
               echo ''
            fi
         fi
         ;;
   esac
}

#------------------------------
# Check Reverse Proxy Smart Card signing CA certificates
#------------------------------
function checkRhttpproxyCACCerts() {
   CAC_FILTER_FILE=$(grep clientCAListFile /etc/vmware-rhttpproxy/config.xml | sed -e 's|<clientCAListFile>||g' -e 's|</clientCAListFile>||g' | tr -d ' ' | grep -v '^<!--')
   if [ -n "$CAC_FILTER_FILE" ]; then
      header 'Check Reverse Proxy Smart Card signing CA certificates'
      task 'Check CA Filter File'
      if [[ "$VC_VERSION" =~ ^7 ]] && [[ $VC_BUILD -ge 20845200 ]]; then
         if [ "$CAC_FILTER_FILE" == '/usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem' ]; then
            if [ -s $CAC_FILTER_FILE ]; then
               statusMessage 'OK' 'GREEN'
            else
               statusMessage 'MISSING' 'YELLOW'
               CERT_STATUS_CLIENT_CA_LIST_FILE_MISSING=1
            fi
         else
            statusMessage 'PROBLEM' 'YELLOW'
            CERT_STATUS_CLIENT_CA_LIST_FILE_LOCATION=1
         fi
      else
         if [ -s $CAC_FILTER_FILE ]; then
            statusMessage 'OK' 'GREEN'
         else
            statusMessage 'MISSING' 'YELLOW'
            CERT_STATUS_CLIENT_CA_LIST_FILE_MISSING=1
         fi
      fi
      if [ -s $CAC_FILTER_FILE ]; then
         rm $STAGE_DIR/rhttpproxy-ca-* 2>&1 | logDebug
         csplit -s -z -f $STAGE_DIR/rhttpproxy-ca- -b %02d.crt $CAC_FILTER_FILE '/-----BEGIN CERTIFICATE-----/' '{*}'
	      i=1
         #for cert in $(ls $STAGE_DIR/rhttpproxy-ca-*); do
	      #   task "Certificate $i"
		   #   checkFilesystemCert "$cert"
		   #   ((++i))
	      #done
         for cert in $STAGE_DIR/rhttpproxy-ca-*; do
            [[ -e "$cert" ]] || break
            task "Certificate $i"
            checkFilesystemCert "$cert"
            ((++i))
         done
	      rm $STAGE_DIR/rhttpproxy-ca-* 2>&1 | logDebug
      fi
   fi
}

#------------------------------
# View Reverse Proxy Smart Card signing CA certificates
#------------------------------
function viewRhttpproxyCACCerts() {
   REVERSE_PROXY_CAC_CERT_THUMBPRINTS=()
   CAC_FILTER_FILE=$(grep clientCAListFile /etc/vmware-rhttpproxy/config.xml | sed -e 's|<clientCAListFile>||g' -e 's|</clientCAListFile>||g' | tr -d ' ' | grep -v '^<!--')
   if [ -z $CAC_FILTER_FILE ] && [ -f /usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem ] && [ ! -z "$(cat /usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem)" ]; then
      CAC_FILTER_FILE='/usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem'
   fi
   header 'Reverse Proxy CA Certificate Filter File'
   logInfo "Smart Card filter file: $CAC_FILTER_FILE"
   if [ -n "$CAC_FILTER_FILE" ] && [ -s "$CAC_FILTER_FILE" ]; then
      rm $STAGE_DIR/rhttpproxy-ca-* 2>&1 | logDebug
      csplit -s -z -f $STAGE_DIR/rhttpproxy-ca- -b %02d.crt $CAC_FILTER_FILE '/-----BEGIN CERTIFICATE-----/' '{*}'
      i=1
      for cert in $STAGE_DIR/rhttpproxy-ca-*; do
         [[ -e "$cert" ]] || break
         TEMP_CERT=$(cat "$cert")         
         CERT_OUTPUT=$(viewBriefCertificateInfo "$TEMP_CERT")
         REVERSE_PROXY_CAC_CERT_THUMBPRINTS+=($(openssl x509 -noout -fingerprint -sha1 -in $cert 2>>/dev/null | awk -F'=' '{print $NF}'))
         printf "%2s. %s\n\n" $i "$CERT_OUTPUT"
         ((++i))
      done
      rm $STAGE_DIR/rhttpproxy-ca-* 2>&1 | logDebug
   else
      echo "${YELLOW}No Smart Card CA filter file found or it is empty.$NORMAL"
   fi
}

#------------------------------
# Check VMware Directory Smart Card signing CA certificates
#------------------------------
function checkVMDirCACCerts() {
   CAC_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=ClientCertAuthnTrustedCAs,cn=Default,cn=ClientCertificatePolicies,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,${VMDIR_DOMAIN_DN}" -D "cn=${VMDIR_USER},cn=users,${VMDIR_DOMAIN_DN}" -y $STAGE_DIR/.vmdir-user-password '(objectclass=*)' userCertificate 2>/dev/null | grep -v '^dn:' | sed -e 's/userCertificate:: //g')
   if [ -n "$CAC_CERTS" ]; then
      header 'Check VMDir Smart Card signing CA certificates'
      i=1
	  for hash in $CAC_CERTS; do
	     TEMP_CERT=$(buildCertFromHash "$hash")
		 task "Certificate $i"
		 checkCert "$TEMP_CERT"
		 ((++i))
	  done
   fi
}

#------------------------------
# View VMware Directory Smart Card signing CA certificates
#------------------------------
function viewVMDirCACCerts() {
   CAC_CERT_LIST=()
   header 'Smart Card Issuing CA Certificates'
   CAC_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=ClientCertAuthnTrustedCAs,cn=Default,cn=ClientCertificatePolicies,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,${VMDIR_DOMAIN_DN}" -D "cn=${VMDIR_USER},cn=users,${VMDIR_DOMAIN_DN}" -y $STAGE_DIR/.vmdir-user-password '(objectclass=*)' userCertificate 2>/dev/null | grep -v '^dn:' | sed -e 's/userCertificate:: //g')
   i=1
   if [ -n "$CAC_CERTS" ]; then
      for hash in $CAC_CERTS; do
         TEMP_CERT=$(buildCertFromHash "$hash")
         CAC_CERT_LIST+=("$TEMP_CERT")
         CERT_OUTPUT=$(viewBriefCertificateInfo "$TEMP_CERT")
      
         printf "%2s. %s\n\n" $i "$CERT_OUTPUT"
         ((++i))
      done
   else
      echo "${YELLOW}No Smart Card issuing CA certificates found in VMware Directory.$NORMAL"
   fi
}

#------------------------------
# Add Smart Card (CAC) issuing certificates to reverse proxy filter file
#------------------------------
function addCACCertsFilterFile() {
   read -e -p $'\nEnter path to new Smart Card issuing certifcate(s): ' NEW_CAC_CERTS_INPUT
   while [ ! -f $NEW_CAC_CERTS_INPUT ]; do read -s -p $'\n'"${YELLOW}File not found, enter path to new Smart Card issuing certifcate(s):${NORMAL} " NEW_CAC_CERTS_INPUT; done
   
   rm $STAGE_DIR/new-cac-cert-* 2>&1 | logDebug
   csplit -s -z -f $STAGE_DIR/new-cac-cert- -b %02d.crt $NEW_CAC_CERTS_INPUT '/-----BEGIN CERTIFICATE-----/' '{*}'
   
   header 'Adding New Smart Card Issuing Certificates'
   CAC_FILTER_FILE=$(grep '<clientCAListFile>' /etc/vmware-rhttpproxy/config.xml | awk -F'>' '{print $2}' | awk -F'<' '{print $1}')
   if ! echo $CAC_FILTER_FILE | grep '^/' > /dev/null 2>&1; then CAC_FILTER_FILE="/etc/vmware/rhttpproxy/$CAC_FILTER_FILE"; fi
   
   for cert in $(ls $STAGE_DIR/new-cac-cert-*); do
      if ! isExpired "$cert" 'file'; then
         task "Adding cert $(openssl x509 -noout -hash -in $cert 2>&1 | logDebug) to reverse proxy file"
         cat $cert >> $CAC_FILTER_FILE
         statusMessage 'OK' 'GREEN'                  
      fi
   done
   sed -i '/^$/d' $CAC_FILTER_FILE
}

#------------------------------
# Remove Smart Card (CAC) issuing certificates from reverse proxy filter file
#------------------------------
function removeCACCertsFilterFile() {
   read -p $'\nEnter number of Smart Card issuing certificate(s) to remove (comma-separated list): ' CAC_CERT_REMOVE_INPUT
   
   if [ -n "$CAC_CERT_REMOVE_INPUT" ]; then
      header 'Removing Smart Card Issuing Certificates'
      HASHES_TO_REMOVE=()
      for index in $(echo $CAC_CERT_REMOVE_INPUT | tr -d ' ' | sed 's/,/ /g'); do
         HASHES_TO_REMOVE+=(" ${REVERSE_PROXY_CAC_CERT_THUMBPRINTS[$((index - 1))]}")
      done
	  
	  CAC_FILTER_FILE=$(grep clientCAListFile /etc/vmware-rhttpproxy/config.xml | sed -e 's|<clientCAListFile>||g' -e 's|</clientCAListFile>||g' | tr -d ' ')
      if ! echo "$CAC_FILTER_FILE" | grep '^/' > /dev/null 2>&1; then CAC_FILTER_FILE="/etc/vmware/rhttpproxy/$CAC_FILTER_FILE"; fi
      
	  logInfo "Hashes to remove from $CAC_FILTER_FILE: ${HASHES_TO_REMOVE[@]}"
            
	  if [ -f $CAC_FILTER_FILE ]; then
         if [ -f $STAGE_DIR/new-cac-certs.pem ]; then rm $STAGE_DIR/new-cac-certs.pem; fi
         csplit -s -z -f $STAGE_DIR/rhttpproxy-ca- -b %02d.crt $CAC_FILTER_FILE '/-----BEGIN CERTIFICATE-----/' '{*}'
         for cert in $(ls $STAGE_DIR/rhttpproxy-ca-*); do
            CERT_THUMBPRINT=$(openssl x509 -noout -fingerprint -sha1 -in $cert 2>/dev/null | awk -F'=' '{print $NF}')
            if [[ ! " ${HASHES_TO_REMOVE[@]} " =~ " $CERT_THUMBPRINT" ]]; then
               cat $cert >> $STAGE_DIR/new-cac-certs.pem
            fi
         done
         task 'Updating reverse proxy filter file'
         cp $STAGE_DIR/new-cac-certs.pem $CAC_FILTER_FILE > /dev/null 2>&1 || errorMessage 'Unable to update reverse proxy filter file'
         statusMessage 'OK' 'GREEN'
      else
         errorMessage 'Unable to determine reverse proxy filter file'
      fi
   fi
   rm $STAGE_DIR/rhttpproxy-ca-* 2>&1 | logDebug        
}

#------------------------------
# Configure Smart Card (CAC) authentication
#------------------------------
function configureCACAuthentication() {
   read -e -p $'\nEnter path to Smart Card issuing CA certificate(s): ' CAC_CA_FILE_INPUT
   while [ ! -f "$CAC_CA_FILE_INPUT" ]; do read -e -p 'File not found, please provide path to the Smart Card issuing CA certificate(s) certificate: ' CAC_CA_FILE_INPUT; done
   
   header 'Configure Smart Card authentication'
   task 'Verify CA certificates'
   csplit -s -z -f $STAGE_DIR/cac-ca- -b %02d.crt "$CAC_CA_FILE_INPUT" '/-----BEGIN CERTIFICATE-----/' '{*}'
   
   for cert in $(ls $STAGE_DIR/cac-ca-*); do
      if isCertCA "$(cat $cert)"; then
         if ! isExpired "$cert" 'file'; then
            cat "$cert" >> $STAGE_DIR/cac-certs.pem
         fi
      fi
   done
   statusMessage 'OK' 'GREEN'
   
   if [ ! -s $STAGE_DIR/cac-certs.pem ]; then
      errorMessage "No valid CA certificates found in $CAC_CA_FILE_INPUT"
   else
      task 'Backup reverse proxy config'
      cp /etc/vmware-rhttpproxy/config.xml /etc/vmware-rhttpproxy/config.xml.backup 2>/dev/null || errorMessage 'Unable to backup /etc/vmware-rhttpproxy/config.xml'
      statusMessage 'OK' 'GREEN'
      
      cp $STAGE_DIR/cac-certs.pem /usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem
      
      task 'Configure reverse proxy'
      CAC_FILTER_FILE=$(grep '<clientCAListFile>' /etc/vmware-rhttpproxy/config.xml | awk -F'>' '{print $2}' | awk -F'<' '{print $1}')
      sed -i -e "s|$CAC_FILTER_FILE|/usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem|" -e 's|<!-- <clientCAListFile>|<clientCAListFile>|' -e 's|</clientCAListFile> -->|</clientCAListFile>|' -e 's|<!-- <clientCertificateMaxSize>|<clientCertificateMaxSize>|' -e 's|</clientCertificateMaxSize> -->|</clientCertificateMaxSize>|' -e '/<clientCAListFile>/i <requestClientCertificate>true</requestClientCertificate>' /etc/vmware-rhttpproxy/config.xml > /dev/null 2>&1 || errorMessage 'Unable to update reverse proxy configuration'
      statusMessage 'OK' 'GREEN'
      
      updateSSOCACConfig 'add' '/usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem'
   fi  
}

#------------------------------
# Export SSO Smart Card CA certificates
#------------------------------
function exportSSOCACCerts() {
   SSO_CAC_CA_CERTS=$(ldapsearch -LLL -h localhost -b "cn=DefaultClientCertCAStore,cn=ClientCertAuthnTrustedCAs,cn=Default,cn=ClientCertificatePolicies,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password userCertificate 2>/dev/null | sed -e 's/^ //g' | tr -d '\n' | sed -e 's/dn:/\n&/g' -e 's/userCertificate:/\n&/g' | grep '^userCertificate' | awk '{print $NF}')
   logInfo 'Exporting Smart Card CA certificates from VMware Directory'
   SSO_CAC_CA_CERT_FILES=()
   if [ -n "$SSO_CAC_CA_CERTS" ]; then
      i=$1
	   for hash in $SSO_CAC_CA_CERTS; do
	      CERT_PRESENT=0
	      TEMP_CERT=$(buildCertFromHash "$hash")
		   TEMP_CERT_THUMBPRINT=$(echo "$TEMP_CERT" | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}')
		   logInfo "Checking SSO Smart Card CA certificate with thumbprint $TEMP_CERT_THUMBPRINT"
         for cert in $(ls $STAGE_DIR/sso-cac-ca-cert-*.crt); do
		      CURRENT_CERT_THUMBPRINT=$(openssl x509 -noout -fingerprint -sha1 -in $cert 2>/dev/null | awk -F'=' '{print $NF}')
            logDebug "Checking SSO Smart Card CA certificate thumbprint ($TEMP_CERT_THUMBPRINT) against new certificate thumbprint ($CURRENT_CERT_THUMBPRINT)"
			   if [ "$CURRENT_CERT_THUMBPRINT" = "$TEMP_CERT_THUMBPRINT" ]; then CERT_PRESENT=1; fi
		   done
		 
		   if [ $CERT_PRESENT -eq 0 ]; then
		      echo "$TEMP_CERT" > $STAGE_DIR/sso-cac-ca-cert-$i.crt
		      SSO_CAC_CA_CERT_FILES+=("$STAGE_DIR/sso-cac-ca-cert-$i.crt")
            logInfo "Saving SSO Smart Card CA certificate to $STAGE_DIR/sso-cac-ca-cert-$i.crt"
		   fi
		   ((++i))
	  done
   fi
}

#------------------------------
# Export SSO LDAPS CA certificates
#------------------------------
function exportSSOLDAPSCerts() {
   logInfo "Exporting LDAPS certificates with Identity Source type: $1"
   case $1 in
      'Microsoft ADFS')
	      LDAPS_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' userCertificate 2>/dev/null | sed -e 's/^ //g' | grep '^userCertificate:' | awk '{print $NF}')         
	      ;;
      *)
	      LDAPS_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$2,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(|(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP))' userCertificate 2>/dev/null | sed -e 's/^ //g' | grep '^userCertificate:' | awk '{print $NF}')		 
	      ;;
   esac   
   
   for hash in $LDAPS_CERTS; do
      TEMP_CERT=$(buildCertFromHash "$hash")
      TEMP_CERT_SUBJECT_HASH=$(echo "$TEMP_CERT" | openssl x509 -noout -hash)
      echo "$TEMP_CERT" > $STAGE_DIR/${3}$TEMP_CERT_SUBJECT_HASH.crt
   done
} 

#------------------------------
# Update SSO with Smart Card configuration
#------------------------------
function updateSSOCACConfig() {
   rm $STAGE_DIR/sso-cac-ca-cert-* > /dev/null 2>&1
   CAC_CA_CERTS=()     
   case $1 in 
      'add')
         if [ -n "$2" ]; then
            CA_FILE=$2
         else
            read -e -p $'\nEnter path to smart card CA certificate file: ' CA_FILE
            while [ ! -f $CA_FILE ]; do read -e -p "${YELLOW}File not found, enter path to smart card CA certificate file:${NORMAL} " CA_FILE; done
         fi
         header 'Adding Smart Card CA certificates to VMware Directory'
         csplit -z -s -f $STAGE_DIR/sso-cac-ca-cert- -b %01d.crt $CA_FILE '/-----BEGIN CERTIFICATE-----/' '{*}'
		 
         CAC_CA_CERT_COUNT=$(ls $STAGE_DIR/sso-cac-ca-cert-* | wc -l)
		 
         exportSSOCACCerts "$CAC_CA_CERT_COUNT"
		 
         for cert in $(ls $STAGE_DIR/sso-cac-ca-cert-*); do
            CERT_HASH=$(openssl x509 -noout -hash -in $cert 2>&1 | logDebug)
            task "Adding certificate $CERT_HASH"
            CAC_CA_CERTS+=("$cert")
            statusMessage 'OK' 'GREEN'
         done
         ;;
	  
      'remove')
         exportSSOCACCerts '0'
		 
         read -p $'\nEnter the number(s) of the certificate(s) to delete (multiple entries separated by a comma): ' CAC_CAS_TO_REMOVE
		 
         if [ -n "$CAC_CAS_TO_REMOVE" ]; then
            header 'Removing Smart Card CA certificates from VMware Directory'
            for index in $(echo $CAC_CAS_TO_REMOVE | tr -d ' ' | sed 's/,/ /g'); do
               CERT_TO_REMOVE=${CAC_CERT_LIST[$((index - 1))]}
               THUMBPRINT_TO_REMOVE=$(echo "$CERT_TO_REMOVE" | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}')
               SUBJECT_HASH_TO_REMOVE=$(echo "$CERT_TO_REMOVE" | openssl x509 -noout -hash)
               for cert in $(ls $STAGE_DIR/sso-cac-ca-cert-*); do
                  CURRENT_CERT_THUMBPRINT=$(openssl x509 -noout -fingerprint -sha1 -in $cert 2>/dev/null | awk -F'=' '{print $NF}')
				  
                  if [ "$THUMBPRINT_TO_REMOVE" = "$CURRENT_CERT_THUMBPRINT" ]; then
                     task "Removing certificate $SUBJECT_HASH_TO_REMOVE"
                     rm $cert > /dev/null 2>&1 || errorMessage "Unable to remove certificate $SUBJECT_HASH_TO_REMOVE"
                     statusMessage 'OK' 'GREEN'
                  fi
               done
            done
         fi
		 
         for cert in $(ls $STAGE_DIR/sso-cac-ca-cert-*); do
            CAC_CA_CERTS+=("$cert")
         done
         ;;
   esac
   
   SSO_CAC_CERTS=$(printf -v joined '%s,' "${CAC_CA_CERTS[@]}"; echo "${joined%,}")
   logInfo "Updating SSO configuration with '$SSO_CAC_CERTS'"
   task 'Update SSO configuration'
   sso-config.sh -set_authn_policy -certAuthn true -cacerts "$SSO_CAC_CERTS" -t "$SSO_DOMAIN" > /dev/null 2>&1 || errorMessage "Unable to configure SSO for Smart Card authentication"
   
   statusMessage 'OK' 'GREEN'   
}

#------------------------------
# Manage AD over LDAP certificates
#------------------------------
function manageLDAPSCerts() {
   if configuredForADoverLDAPS; then
      case $1 in
         'Check')
            checkLDAPSCerts			
            ;;		 
         'View')
            viewLDAPSCerts
            read -p $'\nSelect certificate [Return to Main Menu]: ' VIEW_LDAPS_CERT_INPUT
            if [ -n "$VIEW_LDAPS_CERT_INPUT" ] && [[ $VIEW_LDAPS_CERT_INPUT -le $LDAPS_CERT_COUNTER ]]; then
               LDAP_CERT_HASH=${LDAPS_CERT_HASHES[$((VIEW_LDAPS_CERT_INPUT - 1))]}
               TEMP_CERT=$(buildCertFromHash "$LDAP_CERT_HASH")
               viewCertificateInfo "$TEMP_CERT" 'view-path'
            fi
            ;;      
         'Manage')
            selectLDAPSDomain
            viewIdentitySourceLDAPSCerts                   
            ;;
      esac
   fi
}

#------------------------------
# List LDAPS domains
#------------------------------
function selectLDAPSDomain() {
   LDAPS_DOMAINS=()
   SELECTED_LDAPS_DOMAIN=''
   AD_OVER_LDAPS_DOMAINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' -s one cn 2>/dev/null | sed -e 's/^ //g' | grep '^cn:' | awk '{print $NF}')
   OPENLDAP_DOMAINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP)' -s one cn 2>/dev/null | sed -e 's/^ //g' | grep '^cn:' | awk '{print $NF}')
   ADFS_ISSUER=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSExternalIdp)' -s one vmwSTSIssuerName 2>/dev/null | grep '^vmwSTSIssuerName:' | awk '{print $NF}')
   
   for domain in $OPENLDAP_DOMAINS; do
      LDAPS_DOMAINS+=("$domain|OpenLDAP")
   done

   for domain in $AD_OVER_LDAPS_DOMAINS; do
      LDAPS_DOMAINS+=("$domain|AD over LDAPS")
   done

   if [[ "$VC_VERSION" =~ ^[78] ]]; then
      if [ ! -z $ADFS_ISSUER ]; then
         LDAPS_DOMAINS+=("$ADFS_ISSUER|Microsoft ADFS")
      fi
   fi

   if [ ${#LDAPS_DOMAINS[@]} -eq 0 ]; then 
      echo "${YELLOW}There are no Identity Sources configured that can utilize an LDAPS connection.${NORMAL}"
   else
      header 'Select Domain to Manage LDAPS Certificates'
	   i=1
	   for entry in "${LDAPS_DOMAINS[@]}"; do
         domain=$(echo "$entry" | awk -F '|' '{print $1}')
         source_type=$(echo "$entry" | awk -F '|' '{print $2}')
	      printf "%2s. %s (%s)\n" $i $domain "$source_type"  
		   ((++i))
	   done
	  
	   read -p $'\nSelect domain [1]: ' SELECTED_LDAPS_DOMAIN_INPUT
	  
	   if [ -z $SELECTED_LDAPS_DOMAIN_INPUT ]; then SELECTED_LDAPS_DOMAIN_INPUT=1; fi

	   SELECTED_LDAPS_DOMAIN=${LDAPS_DOMAINS[(($SELECTED_LDAPS_DOMAIN_INPUT - 1))]}
   fi
}

#------------------------------
# Check LDAPS certificates
#------------------------------
function checkLDAPSCerts() {
   AD_OVER_LDAPS_DOMAINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' -s one cn 2>/dev/null | sed -e 's/^ //g' | grep '^cn:' | awk '{print $NF}')   
   OPENLDAP_DOMAINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP)' -s one cn 2>/dev/null | sed -e 's/^ //g' | grep '^cn:' | awk '{print $NF}')
   
   if [ -n "$OPENLDAP_DOMAINS" ]; then
      header 'Check OpenLDAP LDAPS certificates'
      for domain in $OPENLDAP_DOMAINS; do
         echo "Domain: $domain"
         LDAP_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$domain,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP)' userCertificate 2>/dev/null | sed -e 's/^ //g' | grep '^userCertificate:' | awk '{print $NF}')
         i=1
		   for hash in $LDAP_CERTS; do
		      TEMP_CERT=$(buildCertFromHash "$hash")
			   task "   Certificate $i"
			   checkCert "$TEMP_CERT"
			   ((++i))
		   done	
      done
   fi
   if [ -n "$AD_OVER_LDAPS_DOMAINS" ]; then
      header 'Check AD over LDAPS certificates'
	   for domain in $AD_OVER_LDAPS_DOMAINS; do
	      echo "Domain: $domain"
		   LDAP_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$domain,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' userCertificate 2>/dev/null | sed -e 's/^ //g' | grep '^userCertificate:' | awk '{print $NF}')
		   i=1
		   for hash in $LDAP_CERTS; do
		      TEMP_CERT=$(buildCertFromHash "$hash")
			   task "   Certificate $i"
			   checkCert "$TEMP_CERT"
			   ((++i))
		   done		 
	   done
   fi
   if [[ "$VC_VERSION" =~ ^[78] ]]; then
      ADFS_LDAPS_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' userCertificate 2>/dev/null | grep '^userCertificate' | awk '{print $NF}')
      if [ -n "$ADFS_LDAPS_CERTS" ]; then
	      header 'Check ADFS LDAPS certificates'
		   i=1
		   for hash in $ADFS_LDAPS_CERTS; do
		      TEMP_CERT=$(buildCertFromHash "$hash")
			   task "Certificate $i"
			   checkCert "$TEMP_CERT"
			   ((++i))
		   done
      fi
   fi
}

#------------------------------
# View Identity Source LDAPS certificates
#------------------------------
function viewIdentitySourceLDAPSCerts() {
   if [ ! -z "$SELECTED_LDAPS_DOMAIN" ]; then
      LDAPS_CERT_THUMBPRINT_LIST=()
      LDAPS_CERT_HASHES=()
      LDAPS_CERT_COUNTER=1
      
      domain=$(echo "$SELECTED_LDAPS_DOMAIN" | awk -F '|' '{print $1}')
      source_type=$(echo "$SELECTED_LDAPS_DOMAIN" | awk -F '|' '{print $2}')

      case $source_type in
         'Microsoft ADFS')
            LDAP_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' userCertificate 2>/dev/null | grep '^userCertificate' | awk -F':' '{print $NF}')
            header='Manage Certificates for External Provider: Microsoft ADFS'
            ;;
         *)
            LDAP_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$domain,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' userCertificate 2>/dev/null | sed -e 's/^ //g' | grep '^userCertificate:' | awk '{print $NF}')
            header="Manage Certificates for Identity Provider: Domain $domain"
		      ;;
      esac
      header 'Currently Configured Certificatse for LDAPS Connection'
      viewLDAPSCertInfo "$LDAP_CERTS" "$domain" "$source_type"

      header "$header"
      cat << EOF
 1. Add LDAP server certificate(s)
 2. Remove LDAP server certificate(s)
EOF
      read -p $'\nEnter selection [Return to Main Menu]: ' MANAGE_LDAPS_INPUT

      case $MANAGE_LDAPS_INPUT in
         1)
            addLDAPSCerts
            ;;               
         2)
            removeLDAPSCerts
            ;;
      esac
   fi
}

#------------------------------
# View LDAPS certificates
#------------------------------
function viewLDAPSCerts() {
   LDAPS_CERT_THUMBPRINT_LIST=()
   LDAPS_DOMAINS=()
   LDAPS_CERT_HASHES=()
   LDAPS_CERT_COUNTER=1
   LDAPS_CERT_DNS=()
   AD_OVER_LDAPS_DOMAINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' -s one cn 2>/dev/null | sed -e 's/^ //g' | grep '^cn:' | awk '{print $NF}')
   OPENLDAP_DOMAINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP)' -s one cn 2>/dev/null | sed -e 's/^ //g' | grep '^cn:' | awk '{print $NF}')
   ADFS_ISSUER=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSExternalIdp)' -s one vmwSTSIssuerName 2>/dev/null | grep '^vmwSTSIssuerName:' | awk '{print $NF}')
   header 'LDAPS Certificates'
   if [ -n "$OPENLDAP_DOMAINS" ]; then
      for domain in $OPENLDAP_DOMAINS; do
         LDAPS_DOMAINS+=("$domain|OpenLDAP")
         LDAP_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$domain,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP)' userCertificate 2>/dev/null | sed -e 's/^ //g' | grep '^userCertificate:' | awk '{print $NF}')
		   viewLDAPSCertInfo "$LDAP_CERTS" "$domain" 'OpenLDAP'
      done
   fi
   if [ -n "$AD_OVER_LDAPS_DOMAINS" ]; then
      for domain in $AD_OVER_LDAPS_DOMAINS; do
         LDAPS_DOMAINS+=("$domain|AD over LDAPS")
         LDAP_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$domain,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' userCertificate 2>/dev/null | sed -e 's/^ //g' | grep '^userCertificate:' | awk '{print $NF}')
		   viewLDAPSCertInfo "$LDAP_CERTS" "$domain" 'AD over LDAPS'
	  done
   fi
   if [[ "$VC_VERSION" =~ ^[78] ]]; then
      ADFS_LDAPS_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' userCertificate 2>/dev/null | grep '^userCertificate' | awk -F':' '{print $NF}')
      if [ -n "$ADFS_LDAPS_CERTS" ]; then
	      LDAP_CERTS=$ADFS_LDAPS_CERTS
         LDAPS_DOMAINS+=("$ADFS_ISSUER|Microsoft ADFS")
		   viewLDAPSCertInfo "$LDAP_CERTS" "$ADFS_ISSUER" 'Microsoft ADFS'
      fi
   fi
}

#------------------------------
# View AD over LDAPS certificate info
#------------------------------
function viewLDAPSCertInfo() {
   for hash in $1; do
      TEMP_CERT=$(buildCertFromHash "$hash")
      LDAPS_CERT_THUMBPRINT_LIST+=($(echo "$TEMP_CERT" | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}'))
	   LDAPS_CERT_HASHES+=($hash)
      CERT_OUTPUT=$(viewBriefCertificateInfo "$TEMP_CERT")
      if [[ "$2" =~ ^http ]]; then
	      CERT_OUTPUT+=$'\n'"    External Issuer: $2"
      else 
         CERT_OUTPUT+=$'\n'"    Domain: $2"
      fi
      CERT_OUTPUT+=$'\n'"    Identity Source Type: $3"
      
      printf "%2s. %s\n\n" $LDAPS_CERT_COUNTER "$CERT_OUTPUT"
      case $3 in
         'AD over LDAPS')
            LDAPS_CERT_DNS+=("cn=$2,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN")
            ;;
         *)
            ADFS_LDAP_PROVIDER_DN=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSIdentityStore)' dn 2>>$LOG | awk '{print $NF}')
            LDAPS_CERT_DNS+=("$ADFS_LDAP_PROVIDER_DN")
            ;;
      esac
      ((++LDAPS_CERT_COUNTER))
   done
}

#------------------------------
# Add LDAPS certificates
#------------------------------
function addLDAPSCerts() {  
   domain=$(echo "$SELECTED_LDAPS_DOMAIN" | awk -F '|' '{print $1}')
   source_type=$(echo "$SELECTED_LDAPS_DOMAIN" | awk -F '|' '{print $2}')
   
   case $source_type in
      'Microsoft ADFS')
         file_prefix='ldaps-adfs-cert-'
         ;;
      *)
         file_prefix="ldaps-$domain-cer-"
         ;;
   esac

   read -e -p $'\nEnter path to new LDAP server certificate(s): ' NEW_LDAPS_CERTS_INPUT

   while [ ! -f "$NEW_LDAPS_CERTS_INPUT" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to new LDAP server certificate(s):${NORMAL} " NEW_LDAPS_CERTS_INPUT; done
   if [ -f "$NEW_LDAPS_CERTS_INPUT" ]; then
      csplit -s -z -f $STAGE_DIR/$file_prefix -b %02d.crt "$NEW_LDAPS_CERTS_INPUT" '/-----BEGIN CERTIFICATE-----/' '{*}'
   
      exportSSOLDAPSCerts "$source_type" "$domain" "$file_prefix"
   
      header 'Publish new LDAP server ceritifcates'   
      updateLDAPSCerts "$file_prefix"
   fi
}

#------------------------------
# Remove LDAPS certificates
#------------------------------
function removeLDAPSCerts() {
   domain=$(echo "$SELECTED_LDAPS_DOMAIN" | awk -F '|' '{print $1}')
   source_type=$(echo "$SELECTED_LDAPS_DOMAIN" | awk -F '|' '{print $2}')
   
   case $source_type in
      'Microsoft ADFS')
         file_prefix='ldaps-adfs-cert-'
         ;;
      *)
         file_prefix="ldaps-$domain-cer-"
         ;;
   esac
   
   read -p $'\nEnter the number(s) of the LDAP server certificate(s) to remove (comma-separated list): ' REMOVE_LDAP_CERTS_INPUT
   
   exportSSOLDAPSCerts "$source_type" "$domain" "$file_prefix"
   
   for index in $(echo "$REMOVE_LDAP_CERTS_INPUT" | tr -d ' ' | sed 's/,/ /g'); do
      to_delete_thumbprint=${LDAPS_CERT_THUMBPRINT_LIST[$((index - 1))]}
      logInfo "Removing LDAPS certificate with thumbprint $to_delete_thumbprint"
	   for cert in $(ls $STAGE_DIR/$file_prefix*); do
         current_thumbprint=$(openssl x509 -noout -fingerprint -sha1 -in $cert 2>/dev/null | awk -F'=' '{print $NF}')
	      if [ "$current_thumbprint" = "$to_delete_thumbprint" ]; then
		      logInfo "Removing $cert"
			   rm $cert 2>&1 | logDebug
		   fi		 
	   done
   done
   
   header 'Publish new LDAP server ceritifcates'
   updateLDAPSCerts "$file_prefix"
}

#------------------------------
# Remove LDAP certificates
#------------------------------
function updateLDAPSCerts() {
   domain=$(echo "$SELECTED_LDAPS_DOMAIN" | awk -F '|' '{print $1}')
   source_type=$(echo "$SELECTED_LDAPS_DOMAIN" | awk -F '|' '{print $2}')

   case $source_type in
      'Microsoft ADFS')
         LDAPS_UPDATE_DN=$($LDAP_SEARCH -LLL -o ldif-wrap=no -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSIdentityStore)' dn | awk '{print $NF}')
         ;;
      *)
         LDAPS_UPDATE_DN="cn=$domain,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN"
         ;;
   esac

   echo "dn: $LDAPS_UPDATE_DN" > $STAGE_DIR/$1.ldif
   echo 'changetype: modify' >> $STAGE_DIR/$1.ldif
   echo 'replace: userCertificate' >> $STAGE_DIR/$1.ldif
   
   for cert in $(ls $STAGE_DIR/$1*.crt); do
      task "Staging certificate $(openssl x509 -noout -hash -in $cert 2>&1 | logDebug)"
	   CERT_BINARY_FILE=$(echo "$cert" | sed -e 's/.crt/.der/')
      if openssl x509 -inform pem -outform der -in $cert -out $CERT_BINARY_FILE > /dev/null 2>&1; then
	      echo "userCertificate:< file://$CERT_BINARY_FILE" >> $STAGE_DIR/$1.ldif
		   statusMessage 'OK' 'GREEN'
      else
	      statusMessage 'ERROR' 'YELLOW'
	  fi
   done
   
   task 'Update LDAPS certificates'
   $LDAP_MODIFY -x -h $PSC_LOCATION -p $VMDIR_PORT -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password -f $STAGE_DIR/$1.ldif > /dev/null 2>&1 || errorMessage 'Unable to update LDAPS server certificates'
   statusMessage 'OK' 'GREEN'
   rm $STAGE_DIR/ldaps-$1-cert* 2>&1 | logDebug
}

#------------------------------
# Manage Tanzu Supervisor Cluster certificates
#------------------------------
function manageTanzuSupervisorClusterCerts() {
   if tanzuSupervisorClustersPresent; then
      case $1 in 
	     'Check')
		    checkTanzuSupervisorCluseterCerts
		    ;;
		 
		 'View')
		    viewTanzuSupervisorCluseterCerts
		    ;;
			
		 'Manage')
		    ;;
	  esac
   fi   
}

#------------------------------
# Check certificates in the Tanzu Supervisor Clusters
#------------------------------
function checkTanzuSupervisorCluseterCerts() {
   header 'Checking Tanzu Supervisor Cluster Certificates'
   
   IFS=$'\n'
   for line in $(/usr/lib/vmware-wcp/decryptK8Pwd.py | grep -E '^Cluster: |^IP: |^PWD: '); do
      if [[ "$line" =~ ^Cluster ]]; then
         TANZU_CLUSTER_ID=$(echo "$line" | awk '{print $NF}' | awk -F':' '{print $1}' | sed -e 's/domain-c//')
	      TANZU_CLUSTER=$(/opt/vmware/vpostgres/current/bin/psql -d VCDB -U postgres -c "SELECT e.name FROM vpx_entity AS e LEFT JOIN vpx_object_type AS ot ON e.type_id = ot.id WHERE ot.name='CLUSTER_COMPUTE_RESOURCE' AND e.id=$TANZU_CLUSTER_ID" -t | sed -e 's/^[[:space:]]*//g' | grep -v '^$')   
      fi
      if [[ "$line" =~ ^IP ]]; then
         TANZU_CLUSTER_IP=$(echo "$line" | awk '{print $NF}')
      fi
      if [[ "$line" =~ ^PWD ]]; then
         TANZU_CLUSTER_PASSWD=$(echo "$line" | awk '{print $NF}')
         echo "Cluster: $TANZU_CLUSTER"
         logInfo "Checking cluster: $TANZU_CLUSTER"
         ssh-keygen -R $TANZU_CLUSTER_IP 2>&1 | logDebug
         sshpass -p "$TANZU_CLUSTER_PASSWD" ssh -q -o StrictHostKeyChecking=no -t -t root@$TANZU_CLUSTER_IP 'for cert in $(find / -type f \( -name "*.cert" -o -name "*.crt" \)  -print 2>/dev/null | egrep -v "ca.crt$|ca-bundle.crt$|kubelet\/pods|var\/lib\/containerd|run\/containerd|bootstrapper"); do printf "%-52s" "   $cert"; if openssl x509 -noout -in $cert -checkend 0 > /dev/null 2>&1; then printf "%13s\n" "VALID"; else printf "%13s\n" "EXPIRED"; fi; done'	| sed -e "s/VALID/${GREEN}&${NORMAL}/g" -e "s/EXPIRED/${YELLOW}&${NORMAL}/g"
      fi
   done
   IFS=$' \t\n'
}

#------------------------------
# View info on Tanzu Supervisor Cluster certificates
#------------------------------
function viewTanzuSupervisorCluseterCerts() {
   header 'View Tanzu Supervisor Cluster Certificates'
   TANZU_CLUSTERS=()
   TANZU_CLUSTER_IDS=()
   i=1
   echo ''
   for tanzu_cluster_id in $(/usr/lib/vmware-wcp/decryptK8Pwd.py | grep '^Cluster: ' | awk '{print $NF}' | awk -F':' '{print $1}' | sed -e 's/domain-c//'); do
      TANZU_CLUSTER=$(/opt/vmware/vpostgres/current/bin/psql -d VCDB -U postgres -c "SELECT e.name FROM vpx_entity AS e LEFT JOIN vpx_object_type AS ot ON e.type_id = ot.id WHERE ot.name='CLUSTER_COMPUTE_RESOURCE' AND e.id=$tanzu_cluster_id" -t | sed -e 's/^[[:space:]]*//g' | grep -v '^$')
      if [ -n "$TANZU_CLUSTER" ]; then
         TANZU_CLUSTERS+=("$TANZU_CLUSTER")
         TANZU_CLUSTER_IDS+=("$tanzu_cluster_id")
         printf "%2s. %s\n" $i "$TANZU_CLUSTER"
      fi
   done
   
   if [ -n "$TANZU_CLUSTERS" ]; then
      read -p $'\nSelect Supervisor Cluster [Return to Main Menu]: ' TANZU_CLUSTER_INPUT
	  
      if [ -n "$TANZU_CLUSTER_INPUT" ]; then
         TANZU_CLUSTER_NAME=${TANZU_CLUSTERS[$((TANZU_CLUSTER_INPUT - 1))]}
         TANZU_CLUSTER_ID=${TANZU_CLUSTER_IDS[$((TANZU_CLUSTER_INPUT - 1))]}
         TANZU_CLUSTER_INFO=$(/usr/lib/vmware-wcp/decryptK8Pwd.py | grep -A2 "domain-c$TANZU_CLUSTER_ID")
         TANZU_CLUSTER_IP=$(echo "$TANZU_CLUSTER_INFO" | awk '/^IP: /{print $NF}')
         TANZU_CLUSTER_PASSWD=$(echo "$TANZU_CLUSTER_INFO" | awk '/^PWD: /{print $NF}')
		 
         header "'$TANZU_CLUSTER_NAME' Certificates"
         ssh-keygen -R $TANZU_CLUSTER_IP 2>&1 | logDebug
         sshpass -p "$TANZU_CLUSTER_PASSWD" ssh -q -o StrictHostKeyChecking=no -t -t root@$TANZU_CLUSTER_IP 'for cert in $(find / -type f \( -name "*.cert" -o -name "*.crt" \)  -print 2>/dev/null | egrep -v "ca.crt$|ca-bundle.crt$|kubelet\/pods|var\/lib\/containerd|run\/containerd|bootstrapper"); do echo "Cert: $cert"; openssl x509 -noout -in $cert -text; echo ''; done'
	  fi
   fi
}

#------------------------------
# Check if STS Tenant Credential certificates have expired
#------------------------------
function checkSTSTenantCerts() {
   CA_SKIDS=$($DIR_CLI trustedcert list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | awk '{print $NF}')
   TENANT_CREDENTIAL_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password '(objectclass=vmwSTSTenantCredential)' userCertificate)
   
   IFS=$'\n'
   for line in $TENANT_CREDENTIAL_CERTS; do
	  if [[ "$line" =~ ^dn: ]]; then
	     TENANT_CN=$(echo "$line" | awk '{print $NF}' | awk -F',' '{print $1}' | awk -F'=' '{print $NF}')
	     echo "Checking $TENANT_CN:"
	  else
	     hash=$(echo "$line" | awk '{print $NF}')
		 TEMP_CERT=$(buildCertFromHash "$hash")
		 if echo "$TEMP_CERT" | openssl x509 -text -noout 2>/dev/null | grep 'CA:TRUE' > /dev/null 2>&1; then
            checkSTSTenantCert "$TEMP_CERT" $TENANT_CN 'CA' "$CA_SKIDS"
         else
            checkSTSTenantCert "$TEMP_CERT" $TENANT_CN 'signing'
         fi
	  fi
   done
   IFS=$' \t\n'
}

#------------------------------
# Check if STS Tenant Credential certificates have expired
#------------------------------
function checkSTSTrustedCertChains() {
   CA_SKIDS=$($DIR_CLI trustedcert list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | awk '{print $NF}')
   TENANT_TRUSTED_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password '(&(objectclass=vmwSTSTenantTrustedCertificateChain)(cn=TrustedCertChain*))' userCertificate)
   
   IFS=$'\n'
   for line in $TENANT_TRUSTED_CERTS; do
      if [[ "$line" =~ ^dn: ]]; then
         CHAIN_CN=$(echo "$line" | awk '{print $NF}' | awk -F',' '{print $1}' | awk -F'=' '{print $NF}')
         echo "Checking $CHAIN_CN:"
      else
         hash=$(echo "$line" | awk '{print $NF}')
         TEMP_CERT=$(buildCertFromHash "$hash")
         if echo "$TEMP_CERT" | openssl x509 -text -noout 2>/dev/null | grep 'CA:TRUE' > /dev/null 2>&1; then
            checkSTSTenantCert "$TEMP_CERT" $CHAIN_CN 'CA' "$CA_SKIDS"
         else
            checkSTSTenantCert "$TEMP_CERT" $CHAIN_CN 'signing'
         fi
      fi
   done
   IFS=$' \t\n'
}

#------------------------------
# Check if individual STS Signing certificate has expired
#------------------------------
function checkSTSTenantCert() {
   task "   $2 $3 certificate"

   if ! isExpired "$1" 'hash'; then
      CERT_SKID=$(echo "$1" | openssl x509 -noout -text | grep -A1 'Subject Key Id' | tail -n1 | tr -d ': ')
      if ! echo "$4" | grep "$CERT_SKID" > /dev/null && [ "$3" == 'CA' ]; then
	     CERT_STATUS_MISSING_VMDIR=1
		 statusMessage 'MISSING' 'YELLOW'
		 return 0
	  else	  
	     DAYS_LEFT=$(checkCertExpireSoon "$1")
         if [[ $DAYS_LEFT -gt 0 ]]; then
            CERT_STATUS_EXPIRES_SOON=1
            statusMessage "$DAYS_LEFT DAYS" 'YELLOW'
            return 0
         else
            HAS_KEY_USAGE=$(checkCertKeyUsage "$1" "STS Tenant $2 $3")
            if [[ $3 == 'signing' && $HAS_KEY_USAGE -gt 0 ]]; then
               CERT_STATUS_KEY_USAGE=1      
               statusMessage 'KEY USAGE' 'YELLOW'          
               return 0
            fi
            statusMessage 'VALID' 'GREEN'      
            return 0
         fi
	  fi
   else
      CERT_STATUS_EXPIRED=1
      statusMessage 'EXPIRED' 'YELLOW'      
      return 1
   fi 
}

#------------------------------
# View STS Signing certificates
#------------------------------
function viewSTSTenantCerts() {
   header 'View STS signing certificates'   
   LDAP_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password '(objectclass=vmwSTSTenantCredential)' userCertificate | grep '^userCertificate' | awk '{print $NF}')
   TENANT_COUNT=1

   for hash in $LDAP_CERTS; do
      TEMP_CERT=$(buildCertFromHash "$hash")
      CERT_INFO=$(viewBriefCertificateInfo "$TEMP_CERT")
      
      if echo "$TEMP_CERT" | openssl x509 -noout -text 2>/dev/null | grep 'CA:TRUE' > /dev/null; then
         CERT_OUTPUT="   Certificate Type: CA Certificate"$'\n    '
      else
         CERT_OUTPUT="   Certificate Type: Signing Certificate"$'\n    '
      fi
      
      CERT_OUTPUT+=$CERT_INFO

      if echo "$TEMP_CERT" | openssl x509 -text -noout 2>/dev/null | grep 'CA:TRUE' > /dev/null 2>&1; then
         echo $'\n'"$CERT_OUTPUT"$'\n'
         ((++TENANT_COUNT))         
      else
         echo "Tenant Credential $TENANT_COUNT"
         echo "$CERT_OUTPUT"
      fi
   done
}

#------------------------------
# Check CA certificates in VMDir and VECS
#------------------------------
function checkCACertificates() {
   VMDIR_CERTS=()
   VMDIR_CERT_SKIDS=()
   VECS_CERTS=()
   VECS_CERT_ALIASES=()
   header 'Checking CA certificates in VMDir [by CN(id)]'
   for skid in $($DIR_CLI trustedcert list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | awk '{print $NF}'); do
      logInfo "Retrieving certificate with Subject Key ID $skid from VMDir"
      $DIR_CLI trustedcert get --id $skid --outcert $STAGE_DIR/$skid.crt --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>&1 | logInfo
        
      task "${skid}"
      CA_CERT=$(cat $STAGE_DIR/$skid.crt)
      if isExpired "$STAGE_DIR/$skid.crt" 'file'; then
         CERT_STATUS_EXPIRED=1
         statusMessage 'EXPIRED' 'YELLOW'
      elif ! isCertCA "$(cat $STAGE_DIR/$skid.crt)"; then
         CERT_STATUS_NON_CA=1
         statusMessage 'NON-CA' 'YELLOW'
      else         
         DAYS_LEFT=$(checkCertExpireSoon "$CA_CERT")
         if ! openssl x509 -noout -text -in $STAGE_DIR/$skid.crt 2>/dev/null | grep 'Subject Key Identifier:' > /dev/null; then
            CERT_STATUS_CA_MISSING_SKID=1
            statusMessage 'NO SKID' 'YELLOW'
         elif [[ $DAYS_LEFT -gt 0 ]]; then
            CERT_STATUS_EXPIRES_SOON=1       
            statusMessage "$DAYS_LEFT DAYS" 'YELLOW'
         else     
            statusMessage 'VALID' 'GREEN'
         fi
      fi
   done
   
   header 'Checking CA certificates in VECS [by Alias]'
   
   IFS=$'\n'
   for alias in $($VECS_CLI entry list --store TRUSTED_ROOTS 2>>$LOG | grep '^Alias' | awk '{print $NF}'); do
      logInfo "Checking certificate with alias '$alias'"
      TEMP_VECS_CERT=$($VECS_CLI entry getcert --store TRUSTED_ROOTS --alias "$alias" 2>>$LOG)
      
      task $alias
      
      if isExpired "$TEMP_VECS_CERT" 'hash'; then
         CERT_STATUS_EXPIRED=1
         statusMessage 'EXPIRED' 'YELLOW'
      elif ! echo "$TEMP_VECS_CERT" | openssl x509 -text -noout 2>/dev/null | grep 'CA:TRUE' > /dev/null; then
         CERT_STATUS_NON_CA=1
         statusMessage 'NON-CA' 'YELLOW'
      elif [ $(echo "$TEMP_VECS_CERT" | openssl x509 -fingerprint -sha1 -noout 2>/dev/null | cut -d '=' -f 2 | tr -d ':' | awk '{print tolower($0)}') != "$alias" ]; then
         CERT_STATUS_BAD_ALIAS=1
         statusMessage 'BAD ALIAS' 'YELLOW'
      else
         DAYS_LEFT=$(checkCertExpireSoon "$TEMP_VECS_CERT")
         if [[ $DAYS_LEFT -gt 0 ]]; then
            CERT_STATUS_EXPIRES_SOON=1       
            statusMessage "$DAYS_LEFT DAYS" 'YELLOW'
         else
            statusMessage 'VALID' 'GREEN'
         fi
      fi
   done
   IFS=$' \t\n'
}

#------------------------------
# Publish new signing chain to VMDir
#------------------------------
function publishCASigningCertificates() {
   csplit -s -z -f $STAGE_DIR/signing-ca-new- -b %02d.crt $1 '/-----BEGIN CERTIFICATE-----/' '{*}'
   
   VMDIR_CA_SKIDS=$($DIR_CLI trustedcert list --login $VMDIR_USER --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | tr -d '\t' | awk -F':' '{print $2}')
   
   for cert in $(ls $STAGE_DIR/signing-ca-new-*.crt); do
      CURRENT_SKID=$(openssl x509 -noout -text -in $cert 2>/dev/null | grep -A1 'Subject Key Id' | tail -n1 | tr -d ' ' | sed 's/keyid://' | tr -d ':')
      if echo "$VMDIR_CA_SKIDS" | grep "$CURRENT_SKID" > /dev/null; then
         $DIR_CLI trustedcert get --id $CURRENT_SKID --login $VMDIR_USER --password "$(cat $STAGE_DIR/.vmdir-user-password)" --outcert $STAGE_DIR/signing-ca-old-$CURRENT_SKID.crt 2>&1 | logInfo 
         $DIR_CLI trustedcert unpublish --login $VMDIR_USER --password "$(cat $STAGE_DIR/.vmdir-user-password)" --cert $STAGE_DIR/signing-ca-old-$CURRENT_SKID.crt 2>&1 | logInfo
      fi
   done
   
   $DIR_CLI trustedcert publish --chain --cert $TRUSTED_ROOT_CHAIN --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>&1 | logInfo || errorMessage 'Unable to publish trusted root chain to VMDir'
   statusMessage 'OK' 'GREEN'
   
   rm $STAGE_DIR/signing-ca-new-*.crt $STAGE_DIR/signing-ca-old-*.crt 2>&1 | logDebug
}

#------------------------------
# Check if certificate is a CA cert
#------------------------------
function isCertCA() {
   if echo "$1" | openssl x509 -noout -text 2>/dev/null | grep 'CA:TRUE' > /dev/null; then
      return 0
   else
      return 1
   fi
}

#------------------------------
# Quick check if Service Principal entries exist in VMware Directory
#------------------------------
function quickCheckServicePrincipals() {
   header 'Checking Service Principals'
   EXISTING_SERVICE_PRINCIPALS=$($DIR_CLI service list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>/dev/null)
   if [ -n "$EXISTING_SERVICE_PRINCIPALS" ]; then
      echo "Node $MACHINE_ID:"
      for soluser in "${SOLUTION_USERS[@]}"; do
         task "   $soluser"
		 if echo "$EXISTING_SERVICE_PRINCIPALS" | grep "$soluser-$MACHINE_ID" > /dev/null 2>&1; then
		    statusMessage 'PRESENT' 'GREEN'
		 else
		    CERT_STATUS_SERVICE_PRINCIPAL_MISSING=1
		    statusMessage 'MISSING' 'YELLOW'
		 fi
      done
   else
      task 'Listing SSO Service Principals'
      errorMessage 'Could not get list of Service Principal entries from VMware Directory'
   fi
}

#------------------------------
# Check if Service Principal entries exist in VMware Directory
#------------------------------
function checkServicePrincipals() {
   task 'Verifying Service Principal entries exist'
   MISSING_SERVICE_PRINCIPALS=''
   EXISTING_SERVICE_PRINCIPALS=$($DIR_CLI service list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>&1)
   if [ -n "$EXISTING_SERVICE_PRINCIPALS" ]; then
      for soluser in "${SOLUTION_USERS[@]}"; do
         if ! echo "$EXISTING_SERVICE_PRINCIPALS" | grep "$soluser-$MACHINE_ID" > /dev/null 2>&1; then
	         if [ -z "$MISSING_SERVICE_PRINCIPALS" ]; then MISSING_SERVICE_PRINCIPALS+="$soluser-$MACHINE_ID"; else MISSING_SERVICE_PRINCIPALS+=" $soluser-$MACHINE_ID"; fi
	      fi
      done
   
      if [ -n "$MISSING_SERVICE_PRINCIPALS" ]; then
         statusMessage 'ERROR' 'RED'
         echo $'\n'"${YELLOW}------------------------!!! Attention !!!------------------------ "
         echo 'The following Service Principal entries are missing:'
         for sp in $MISSING_SERVICE_PRINCIPALS; do
            echo " - $sp"
         done
	  
         echo $'\nPlease refer to KB https://kb.vmware.com/s/article/80469'
         echo 'on using the lsdoctor utility to recreate the missing'
         echo "Solution User/Service Principal entries.${NORMAL}"
	  
         if [[ "$VC_VERSION" =~ ^[78] ]] && (echo "$MISSING_SERVICE_PRINCIPALS" | grep "wcp-$MACHINE_ID" > /dev/null || echo "$MISSING_SERVICE_PRINCIPALS" | grep "hvc-$MACHINE_ID" > /dev/null); then
            echo $'\n'"${YELLOW}Note: The hvc and/or wcp Service Principal entries are"
            echo 'missing, and currently lsdoctor will NOT recreate these'
            echo 'Service Principal entries. These will need to be created'
            echo "and assigned to the proper groups manually.${NORMAL}"		
         fi
         exit
      else
         statusMessage 'OK' 'GREEN'
      fi
   else
      errorMessage 'Could not get list of Service Principal entries from VMware Directory'
   fi
}

#------------------------------
# Builds the expanded message detailng issues with certificates
#------------------------------
function buildCertificateStatusMessage() {
   if [ $CERT_STATUS_EXPIRES_SOON == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates are expiring within 30 days\n'; fi
   
   if [ $CERT_STATUS_MISSING_PNID == 1 ]; then CERT_STATUS_MESSAGE+=" - One or more certificates are missing the PNID ($PNID) from the SAN entry"$'\n'; fi
   
   if [ $CERT_STATUS_KEY_USAGE == 1 ]; then 
      CERT_STATUS_MESSAGE+=$' - One or more certificates do not have the recommended\n'
      CERT_STATUS_MESSAGE+=$'   Key Usage values\n'; fi
   
   if [ $CERT_STATUS_EXPIRED == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates are expired\n'; fi
   
   if [ $CERT_STATUS_NON_CA == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates are not CA certificates\n'; fi
   
   if [ $CERT_STATUS_BAD_ALIAS == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more entries in the TRUSTED_ROOTS store have an alias that is not the SHA1 thumbprint\n'; fi
   
   if [ $CERT_STATUS_MISSING_SAN == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates do not have any Subject Alternative Name values\n'; fi
   
   if [ $CERT_STATUS_SHA1_SIGNING == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates are signed using the SHA-1 algorithm\n'; fi
   
   if [ $CERT_STATUS_MISSING == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates are missing\n'; fi
   
   if [ $CERT_STATUS_MISSING_VMDIR == 1 ]; then 
      CERT_STATUS_MESSAGE+=$' - One or more CA certificates are missing from\n'
      CERT_STATUS_MESSAGE+=$'   VMware Directory\n'
   fi
   
   if [ $CERT_STATUS_MISMATCH_SERVICE_PRINCIPAL == 1 ]; then 
      CERT_STATUS_MESSAGE+=$' - One or more Solution User certificates does not match\n'
      CERT_STATUS_MESSAGE+=$'   the Service Principal certificate in VMware Directory\n'
   fi
   
   if [ $CERT_STATUS_MISSING_CA == 1 ]; then 
      CERT_STATUS_MESSAGE+=$' - One or more certificates do not have all of the CA\n'
      CERT_STATUS_MESSAGE+=$'   certificates in its signing chain in VMware Directory\n'
   fi
   
   if [ $CERT_STATUS_EXPIRED_EMBEDDED_CA == 1 ]; then
      CERT_STATUS_MESSAGE+=$' - One or more certificates has a CA certificate embedded\n'
      CERT_STATUS_MESSAGE+=$'   in its chain that is expired\n'
   fi

   if [ $CERT_STATUS_UNSUPPORTED_SIGNATURE_ALGORITHM == 1 ]; then
      CERT_STATUS_MESSAGE+=$' - One or more certificates is using an unsupported\n'
      CERT_STATUS_MESSAGE+=$'   signature algorithm'
   fi
   
   if [ $CERT_STATUS_STORE_MISSING == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more VECS stores are missing\n'; fi
   
   if [ $CERT_STATUS_STORE_PERMISSIONS == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more VECS stores are missing permissions\n'; fi
   
   if [ $CERT_STATUS_SERVICE_PRINCIPAL_MISSING == 1 ]; then 
      CERT_STATUS_MESSAGE+=$' - One or more Service Principal entries are missing\n'
      CERT_STATUS_MESSAGE+=$'   from VMware Directory\n'; fi
   
   if [ $TRUST_ANCHORS_MISMATCH == 1 ]; then
      CERT_STATUS_MESSAGE+=$' - One or more vCenter/PSC nodes have mismatched SSL trust anchors\n'
   fi

   if [ $TRUST_ANCHORS_UNKNOWN == 1 ]; then 
      CERT_STATUS_MESSAGE+=$' - The Machine SSL certificate could not be obtained from\n'
      CERT_STATUS_MESSAGE+=$'   the following nodes to check SSL trust anchors:\n'
      for unknown_node in "${TRUST_ANCHORS_UNKNOWN_NODES[@]}"; do
         CERT_STATUS_MESSAGE+="     $unknown_node"$'\n'
      done
   fi  

   if [ $TRUST_ANCHORS_CHECK_URI == 1 ]; then
      if [ $TRUST_ANCHORS_CHECK_URI_MISMATCH == 1 ]; then
         CERT_STATUS_MESSAGE+=$' - One or more vCenter/PSC nodes have mismatched SSL trust anchors and\n'
         CERT_STATUS_MESSAGE+=$'   have Lookup Service registrations using the IP address instead\n'
         CERT_STATUS_MESSAGE+=$'   of the PNID in the endpoint URIs. These can be fixed with the\n'
         CERT_STATUS_MESSAGE+=$'   lsdoctor utility: https://kb.vmware.com/s/article/80469\n'
      else
         CERT_STATUS_MESSAGE+=$' - One or more vCenter/PSC nodes have Lookup Service registrations\n'
         CERT_STATUS_MESSAGE+=$'   using the IP address instead of the PNID in the endpoint URIs.\n'
         CERT_STATUS_MESSAGE+=$'   These can be fixed with the lsdoctor utility:\n'
         CERT_STATUS_MESSAGE+=$'   https://kb.vmware.com/s/article/80469\n'
      fi
   fi 
   
   if [ $CERT_STATUS_TOO_MANY_CRLS == 1 ]; then CERT_STATUS_MESSAGE+=' - The number of CRLs in VECS may be preventing some services from starting'; fi
   
   if [ $CERT_STATUS_VMCA_EMPTY_CONFIG == 1 ]; then
      CERT_STATUS_MESSAGE+=$' - Some are one or more vpxd.certmgmt.certs.cn.* settings with empty values\n'
      CERT_STATUS_MESSAGE+=$'   This can cause issues pushing VMCA-signed certificates to ESXi hosts\n'
   fi
   
   if [ $CERT_STATUS_VMCA_MODE == 1 ]; then
      CERT_STATUS_MESSAGE+=" - The certificate management mode is set to 'thumbprint'"$'\n'
      CERT_STATUS_MESSAGE+="   This is not recommended, and should be set to 'vmca' or 'custom'"$'\n'
   fi

   if [ $CERT_STATUS_CLIENT_CA_LIST_FILE_LOCATION == 1 ]; then
      CERT_STATUS_MESSAGE+=" - The <clientCAListFile> value must reference the following file:"$'\n'
      CERT_STATUS_MESSAGE+="   /usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem"
   fi

   if [ $CERT_STATUS_CLIENT_CA_LIST_FILE_MISSING == 1 ]; then
      CERT_STATUS_MESSAGE+=" - The <clientCAListFile> file at /usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem does not exist"$'\n'
   fi

   if [ $CERT_STATUS_STS_VECS_CONFIG == 1 ]; then
      CERT_STATUS_MESSAGE+=$' - The STS server is configured to use a VECS store other than\n'
      CERT_STATUS_MESSAGE+=$'   the MACHINE_SSL_CERT store\n'
   fi

   if [ $CERT_STATUS_STS_CONNECTION_STRINGS == 1 ]; then
      CERT_STATUS_MESSAGE+=$' - The STS ConnectionStrings value is not set properly for an SSO\n'
      CERT_STATUS_MESSAGE+=$'   domain with multiple Domain Controllers\n'
   fi

   if [ $CERT_STATUS_CA_MISSING_SKID == 1 ]; then
      CERT_STATUS_MESSAGE+=$' - One or more CA certificates is missing the Subject Key ID extension'
   fi 
}

#------------------------------
# Check the number of CRLs in VECS
#------------------------------
function checkCRLs() {
   CERT_STATUS_TOO_MANY_CRLS=0
   NUM_CRLS=$($VECS_CLI entry list --store TRUSTED_ROOT_CRLS | head -n1 | awk '{print $NF}')
   header 'Checking Certificate Revocation Lists'
   task 'Number of CRLs in VECS'
   
   if [ $NUM_CRLS -le 30 ]; then
      statusMessage "$NUM_CRLS" 'GREEN'
   elif [ $NUM_CRLS -le 100 ]; then
      statusMessage "$NUM_CRLS" 'YELLOW'
   else
      statusMessage "$NUM_CRLS" 'RED'
      CERT_STATUS_TOO_MANY_CRLS=1
   fi
}

#------------------------------
# Clear CRLs in VECS
#------------------------------
function clearCRLs() {
   header 'Clear Certificate Revocation Lists in VECS'
   task 'Backup CRLs'
   
   if [ ! -d $BACKUP_DIR/old-CRLs ]; then mkdir $BACKUP_DIR/old-CRLs 2>/dev/null || errorMessage 'Unable to create backup CRL directory'; fi
   
   find /etc/ssl/certs -type f -iname '*.r[0-9]' -exec mv {} $BACKUP_DIR/old-CRLs \; || errorMessage "Unable to move CRL files to $BACKUP_DIR/old-CRLs"
   
   statusMessage 'OK' 'GREEN'
   
   task 'Delete CRLs from VECS (this may take some time)'
   for alias in $($VECS_CLI entry list --store TRUSTED_ROOT_CRLS | grep Alias | awk '{print $NF}'); do
      logInfo "Removing CRL $alias from VECS"
      $VECS_CLI entry delete --store TRUSTED_ROOT_CRLS --alias $alias -y > /dev/null 2>&1
   done
   
   statusMessage 'OK' 'GREEN'
   
   if [ $NODE_TYPE != 'management' ]; then
      restartVMwareServices 'vmafdd' 'vmdird' 'vmcad'
   else
      restartVMwareServices 'vmafdd'
   fi
}

#------------------------------
# Clear BACKUP_STORE in VECS
#------------------------------
function clearBackupStore() {
   header 'Clear BACKUP_STORE'
   
   for alias in $($VECS_CLI entry list --store BACKUP_STORE | grep Alias | awk '{print $NF}'); do
      task "Removing $alias"
      $VECS_CLI entry delete --store BACKUP_STORE --alias $alias -y > /dev/null 2>&1 || errorMessage "Unable to remove $alias entry from BACKUP_STORE"
      statusMessage 'OK' 'GREEN'
   done
   
   if checkVECSStore 'BACKUP_STORE_H5C'; then
      header 'Clear BACKUP_STORE_H5C'
      for alias in $($VECS_CLI entry list --store BACKUP_STORE_H5C | grep Alias | awk '{print $NF}'); do
         task "Removing $alias"
         $VECS_CLI entry delete --store BACKUP_STORE_H5C --alias $alias -y > /dev/null 2>&1 || errorMessage "Unable to remove $alias entry from BACKUP_STORE_H5C"
	     statusMessage 'OK' 'GREEN'
      done
   fi
}

#------------------------------
# Clear BACKUP_STORE in VECS
#------------------------------
function clearMachineSSLCSR() {
   unset DELETE_MACHINE_CSR_INPUT
   echo $'\n'"${YELLOW}-------------------------!!! WARNING !!!-------------------------"
   echo "This entry was created using the 'Generate Certificate"
   echo "Signing Request (CSR)' option from the vSphere Client."
   echo 'It contains the corresponding private key associated'
   echo 'with this CSR. DO NOT DELETE if you are still waiting'
   echo "for this request to be signed by your Certificate Authority!${NORMAL}"
   
   read -p $'\nDelete the __MACHINE_CSR entry from VECS? [n]: ' DELETE_MACHINE_CSR_INPUT
   
   if [ -z $DELETE_MACHINE_CSR_INPUT ]; then DELETE_MACHINE_CSR_INPUT='n'; fi
   
   if [[ $DELETE_MACHINE_CSR_INPUT =~ ^[Yy] ]]; then
      header 'Delete Machine SSL CSR entry in VECS'
      task 'Backup private key'
      $VECS_CLI entry getkey --store MACHINE_SSL_CERT --alias __MACHINE_CSR > $BACKUP_DIR/__MACHINE_CSR.key 2>&1 || errorMessage 'Unable to backup private key in __MACHINE_CSR entry from VECS'
      statusMessage 'OK' 'GREEN'
      
      task 'Delete entry in MACHINE_SSL_CERT store'
      $VECS_CLI entry delete --store MACHINE_SSL_CERT --alias __MACHINE_CSR -y > /dev/null 2>&1 || errorMessage "Unable to delete entry '__MACHINE_CSR' from VECS"
      statusMessage 'OK' 'GREEN'
   fi   
}

#------------------------------
# Perform quick check of SSL trust anchors
#------------------------------
function quickCheckSSLTrustAnchors() {
   header 'Checking SSL Trust Anchors'
   getSSODomainNodes
   TRUST_ANCHORS_UNKNOWN=0
   TRUST_ANCHORS_MISMATCH=0
   TRUST_ANCHORS_CHECK_URI=0
   TRUST_ANCHORS_CHECK_URI_MISMATCH=0
   TRUST_ANCHORS_UNKNOWN_NODES=()
   
   for node in "${SSO_NODES[@]}"; do
      MISMATCH=0
      task "$node"
      NODE_MACHINE_SSL_THUMBPRINT=$(echo | openssl s_client -connect $node:443 2>/dev/null | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}')
      if [ -n "$NODE_MACHINE_SSL_THUMBPRINT" ]; then
         TRUST_ANCHOR_SEARCH_FILTER="(&(|(vmwLKUPURI=https://$node:*)(vmwLKUPURI=https://$node/*))(|(objectclass=vmwLKUPServiceEndpoint)(objectclass=vmwLKUPEndpointRegistration)))"
         NODE_TRUST_ANCHORS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=Sites,cn=Configuration,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password "$TRUST_ANCHOR_SEARCH_FILTER" vmwLKUPEndpointSslTrust vmwLKUPSslTrustAnchor 2>>$LOG | grep -v '^dn:' | awk '{print $NF}' | sort | uniq)
         
         if [ -z "$NODE_TRUST_ANCHORS" ]; then
            TRUST_ANCHOR_SEARCH_FILTER="(&(|(vmwLKUPURI=https://$IP:*)(vmwLKUPURI=https://$IP/*))(|(objectclass=vmwLKUPServiceEndpoint)(objectclass=vmwLKUPEndpointRegistration)))"
            NODE_TRUST_ANCHORS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=Sites,cn=Configuration,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password "$TRUST_ANCHOR_SEARCH_FILTER" vmwLKUPEndpointSslTrust vmwLKUPSslTrustAnchor 2>>$LOG | grep -v '^dn:' | awk '{print $NF}' | sort | uniq)
            if [ -z "$NODE_TRUST_ANCHORS" ]; then
               statusMessage 'MISSING' 'YELLOW'
            else
               TRUST_ANCHORS_CHECK_URI=1
               for hash_raw in $NODE_TRUST_ANCHORS; do
                  logInfo "Checking following raw certificate hash"
                  logDetails "$hash_raw"
                  if [[ "$hash_raw" =~ ^TUl ]]; then
                     hash=$(echo $hash_raw | base64 --decode | tr -d '\r\n')
                  else
                     hash=($hash_raw)
                  fi
                  TEMP_CERT=$(buildCertFromHash "$hash")
                  ANCHOR_THUMBPRINT=$(echo "$TEMP_CERT" | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}')
                  logInfo "Checking node thumbprint $NODE_MACHINE_SSL_THUMBPRINT against unique trust anchor thumbprint $ANCHOR_THUMBPRINT"
                  if [ "$NODE_MACHINE_SSL_THUMBPRINT" != "$ANCHOR_THUMBPRINT" ]; then
                     MISMATCH=1
                  fi
               done
               if [ $MISMATCH -eq 0 ]; then
                  statusMessage 'CHECK URI' 'YELLOW'
               else
                  TRUST_ANCHORS_CHECK_URI_MISMATCH=1
                  statusMessage 'MISMATCH*' 'YELLOW'
               fi
            fi
         else
            for hash_raw in $NODE_TRUST_ANCHORS; do
               logInfo "Checking following raw certificate hash"
               logDetails "$hash_raw"
               if [[ "$hash_raw" =~ ^TUl ]]; then
                  hash=$(echo $hash_raw | base64 --decode | tr -d '\r\n')
               else
                  hash=($hash_raw)
               fi
               TEMP_CERT=$(buildCertFromHash "$hash")
               ANCHOR_THUMBPRINT=$(echo "$TEMP_CERT" | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}')
               logInfo "Checking node thumbprint $NODE_MACHINE_SSL_THUMBPRINT against unique trust anchor thumbprint $ANCHOR_THUMBPRINT"
               if [ "$NODE_MACHINE_SSL_THUMBPRINT" != "$ANCHOR_THUMBPRINT" ]; then
                  MISMATCH=1
                  TRUST_ANCHORS_MISMATCH=1
               fi
            done
            if [ $MISMATCH -eq 0 ]; then
               statusMessage 'VALID' 'GREEN'
            else
               statusMessage 'MISMATCH' 'YELLOW'
            fi
         fi
      else
         TRUST_ANCHORS_UNKNOWN=1
         TRUST_ANCHORS_UNKNOWN_NODES+=($node)
         statusMessage 'UNKNOWN' 'YELLOW'
      fi
   done
}

#------------------------------
# Get the PSC and vCenter nodes in an SSO Domain
#------------------------------
function getSSODomainNodes() {
   SSO_NODES=()
   PSC_NODES=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "ou=Domain Controllers,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=computer)' cn | grep '^cn:' | awk '{print $NF}')
   PSC_COUNT=$(echo "$PSC_NODES" | wc -l)
   VCENTER_NODES=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "ou=Computers,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=computer)' cn | grep '^cn:' | awk '{print $NF}')
   VCENTER_COUNT=$(echo "$VCENTER_NODES" | wc -l)
   
   for psc_node in "$PSC_NODES"; do
      if [[ ! "${SSO_NODES[@]}" =~ "$psc_node" ]]; then SSO_NODES+=($psc_node); fi
   done

   for vc_node in "$VCENTER_NODES"; do
      if [[ ! "${SSO_NODES[@]}" =~ "$vc_node" ]]; then SSO_NODES+=($vc_node); fi
   done
}

#------------------------------
# Print menu to view or manage certificates
#------------------------------
function printCertificateMenu() {
   authenticateIfNeeded
   header "$1 vCenter Certificates"
   logInfo "Printing the $1 vCenter Certificates Menu"
   echo ' 1. Machine SSL certificate'
   echo ' 2. Solution User certificates'
   echo ' 3. CA certificates in VMware Directory'
   echo ' 4. CA certificates in VECS'
   
   if [ $NODE_TYPE = 'infrastructure' ]; then printf "$YELLOW"; fi
   
   echo ' 5. Authentication Proxy certifcate'
   echo ' 6. Auto Deploy CA certificate'
   echo ' 7. SMS certificates'
   if [ "$VC_VERSION" == '6.5' ]; then printf "$YELLOW"; fi
   echo ' 8. Data Encipherment certificate'
   printf "$NORMAL"
   if [ $NODE_TYPE = 'infrastructure' ]; then printf "$YELLOW"; fi
   echo ' 9. vCenter Extension thumbprints'
   printf "$NORMAL"
   if [ $NODE_TYPE = 'management' ]; then
      printf "$YELLOW"
   else
      printf "$NORMAL"
   fi
   
   echo '10. VMware Directory certificate'
   echo '11. STS signing certificates'
   echo '12. VMCA certificate'
   if ! configuredForCAC; then printf "$YELLOW"; fi   
   echo '13. Smart Card CA certificates'
   printf "$NORMAL"
   if ! configuredForADoverLDAPS; then printf "$YELLOW"; fi
   echo '14. LDAPS Identity Source certificates'
   printf "$NORMAL"
   if ! tanzuSupervisorClustersPresent || [ "$1" == 'Manage' ]; then printf "$YELLOW"; fi
   echo '15. Tanzu Supervisor Cluster certificates'
   printf "$NORMAL"
   if [ "$1" == 'Manage' ]; then
      if ! checkVECSStore 'BACKUP_STORE'; then printf "$YELLOW"; fi
      echo '16. Clear BACKUP_STORE in VECS'
	  printf "$NORMAL"
      echo '17. Clear TRUSTED_ROOT_CRLS store in VECS'
	  if ! checkMachineSSLCSR; then printf "$YELLOW"; fi
	  echo '18. Clear Machine SSL CSR in VECS'
	  printf "$NORMAL"
   fi
   echo ''
}

#------------------------------
# Check if Smart Card authentication is configured
#------------------------------
function configuredForCAC() {
   if $LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password  '(objectclass=vmwSTSTenant)' vmwSTSAuthnTypes | grep 'vmwSTSAuthnTypes: 4' > /dev/null; then
      return 0
   else
      return 1
   fi
}

#------------------------------
# Check the an AD over LDAPS Identity Source is configured
#------------------------------
function configuredForADoverLDAPS() {
   if $LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' cn 2>/dev/null | grep cn > /dev/null 2>&1; then
      logInfo 'vCenter is using AD over LDAPS as an Identity Source'
      return 0
   elif $LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' cn 2>/dev/null | grep cn > /dev/null 2>&1; then
      logInfo 'vCenter is using ADFS as an Identity Source'
      return 0
   elif $LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP)' cn 2>/dev/null | grep cn > /dev/null 2>&1; then
      logInfo 'vCenter is using OpenLDAP as an Identity Source'
      return 0
   else
      logInfo 'vCenter is NOT using AD over LDAPS as an Identity Source'
      return 1
   fi
}

#------------------------------
# Check if there are any Tanzu Supervisor clusters deployed
#------------------------------
function tanzuSupervisorClustersPresent() {
   if [ -f /usr/lib/vmware-wcp/decryptK8Pwd.py ]; then
      if /usr/lib/vmware-wcp/decryptK8Pwd.py | grep -E '^Cluster: |^IP: |^PWD: ' > /dev/null 2>&1; then 
         logInfo 'Tanzu Supervisor Clusters detected'
         return 0
      else 
         logInfo 'No Tanzu Supervisor Clusters detected'
         return 1
      fi
   else
      return 1
   fi
}

#------------------------------
# Display options to view certificate info
#------------------------------
function viewCertificateMenu() {
   printCertificateMenu 'View'
   
   read -p 'Select an option [Return to Main Menu]: ' VIEW_CERT_OPERATION
   
   logInfo "User selected option $VIEW_CERT_OPERATION"
   
   if [[ "$VIEW_CERT_OPERATION" -ge 0 &&  "$VIEW_CERT_OPERATION" -le 15 ]]; then processViewCertificate; fi
}

#------------------------------
# Display options to manage certificates 
#------------------------------
function manageCertificateMenu() {
   printCertificateMenu 'Manage'
   
   read -t $READ_TIMEOUTS -p 'Select an option [Return to Main Menu]: ' MANAGE_CERT_OPERATION
   
   if [ $? -le 128 ]; then   
      logInfo "User selected option '$MANAGE_CERT_OPERATION'"
   
      if [[ "$MANAGE_CERT_OPERATION" -ge 1 &&  "$MANAGE_CERT_OPERATION" -le 18 ]]; then processManageCertificate; fi   
   else
      echo ''
   fi
}

#------------------------------
# Process view certificate selection
#------------------------------
function processViewCertificate() {
   case $VIEW_CERT_OPERATION in
      1)
         viewVECSCertificateInfo 'MACHINE_SSL_CERT' '__MACHINE_CERT'
         ;;
      
      2)
         for soluser in "${SOLUTION_USERS[@]}"; do
            echo $'\n'"Solution User: $soluser"
            viewVECSCertificateInfo "$soluser" "$soluser"
         done
         ;;
      
      3)
         manageVMDirCACertificates 'View'
         ;;
      
      4)
         manageVECSCACertificates 'View'
         ;;
      
      5)
         viewFilesystemCertificateInfo '/var/lib/vmware/vmcam/ssl/vmcamcert.pem'
         ;;
      
      6)
         viewFilesystemCertificateInfo '/etc/vmware-rbd/ssl/rbd-ca.crt'
         ;;
      
      7)
         manageSMSCertificates 'View'
         ;;
      
	  8) 
	     viewVECSCertificateInfo 'data-encipherment' 'data-encipherment'
	     ;;
	  
      9)
         manageVCExtensionThumbprints 'View'
         ;;
      
      10)
         if [[ "$VC_VERSION" =~ ^[78] ]]; then
            viewRemoteCertificateInfo 'localhost' '636'
         else
            viewFilesystemCertificateInfo '/usr/lib/vmware-vmdir/share/config/vmdircert.pem'
         fi
         ;;
         
      11)
         manageSTSTenantCerts 'View'
         ;;
      
      12)
         viewFilesystemCertificateInfo '/var/lib/vmware/vmca/root.cer'
         ;;
         
      13)
         manageCACCerts 'View'
         ;;
      
      14)
         manageLDAPSCerts 'View'
         ;;
	  
	  15)
	     manageTanzuSupervisorClusterCerts 'View'
		 ;;
   esac
}

#------------------------------
# Process manage certificate selection
#------------------------------
function processManageCertificate() {
   setTimestamp
   
   case $MANAGE_CERT_OPERATION in
      1)
         if promptReplaceMachineSSL; then
            if replaceMachineSSLCert && [[ $UPDATED_MACHINE_SSL -eq 1 ]]; then
               if [ $NODE_TYPE != 'infrastructure' ]; then           
                  SSLTrustAnchorSelf
                  updateSSLTrustAnchors
                  manageVCExtensionThumbprints 'Update'
                  updateAutoDeployDB
               fi
               if [ $NODE_TYPE = 'infrastructure' ]; then
                  if [ -n "$PSC_LB" ]; then
			            NODE_FQDN="$PSC_LB"
                  else
                     SSLTrustAnchorSelf
                  fi
			         updateSSLTrustAnchors
               fi
			      noticePSCHA
               promptRestartVMwareServices
            fi
            clearCSRInfo
         fi
         ;;
      
      2)
         if promptReplaceSolutionUsers; then       
            if replaceSolutionUserCerts && [ $NODE_TYPE != 'infrastructure' ]; then 
               manageVCExtensionThumbprints 'Update'
               promptRestartVMwareServices
            fi
            clearCSRInfo
         fi
         ;;
      
      3)
	      manageVMDirCACertificates 'Manage'
         ;;
      
      4)
         manageVECSCACertificates 'Manage'
         ;;
      
      5)
         if [ $NODE_TYPE != 'infrastructure' ]; then
            if promptReplaceAuthProxy; then
               if replaceAuthProxyCert; then
                  manageVCExtensionThumbprints 'Update' 
                  promptRestartVMwareServices 'vmcam'               
               fi
               clearCSRInfo
            fi
         else
            printf "\n${YELLOW}This operation must be done on the vCenter Server.${NORMAL}\n\n"
         fi
         ;;
      
      6)
         if [ $NODE_TYPE != 'infrastructure' ]; then
            if promptReplaceAutoDeployCA; then
               if replaceAutoDeployCACert; then promptRestartVMwareServices 'vmware-rbd-watchdog'; fi
               clearCSRInfo
            fi
         else
            printf "\n${YELLOW}This operation must be done on the vCenter Server.${NORMAL}\n\n"
         fi
         ;;
      
      7)
         manageSMSCertificates 'Manage'
         ;;
      
      8) 
	     replaceDataEnciphermentCertificate
	     ;;
	    
      9)
         if [ $NODE_TYPE != 'infrastructure' ]; then
            manageVCExtensionThumbprints 'Check'
         else
            printf "\n${YELLOW}This operation must be done on the vCenter Server.${NORMAL}\n\n"
         fi
         ;;
      
      10)
         if [[ "$VC_VERSION" =~ ^[78] ]]; then
            if [ -f /usr/lib/vmware-vmdir/share/config/vmdircert.pem ]; then
               removeVMDirCert
            else
               printf "\n${YELLOW}This operation is not available for vCenter 7.x or later${NORMAL}\n\n"
            fi
         elif [ $NODE_TYPE != 'management' ]; then
            if promptReplaceVMDir; then replaceVMDirCert; fi
         else
            printf "\n${YELLOW}This operation must be done on the Platform Services Controller${NORMAL}\n\n"
         fi
         ;;
      
      11)
         if [ $NODE_TYPE != 'management' ]; then
            manageSTSTenantCerts 'Replace'
         else
            printf "\n${YELLOW}This operation must be done on the Platform Services Controller${NORMAL}\n\n"
         fi
         ;;
      
      12)
         if [ $NODE_TYPE != 'management' ]; then
            if promptReplaceVMCA; then
               if replaceVMCACert && [ "$VMCA_REGENERATE_CERTIFICATES" == '1' ]; then resetAllCertificates; fi
            fi
         else
            printf "\n${YELLOW}This operation must be done on the Platform Services Controller${NORMAL}\n\n"
         fi
         ;;
      
      13)
         manageCACCerts 'Manage'
         ;;
      
      14)
         manageLDAPSCerts 'Manage'
         ;;
	  
	  15) 
	     #manageTanzuSupervisorClusterCerts 'Manage'
		 ;;
		 
	  16)
	     if checkVECSStore 'BACKUP_STORE'; then
	        clearBackupStore
		 else
		    echo $'\n'"${YELLOW}The BACKUP_STORE does not exist in VECS, nothing to do.$NORMAL"
		 fi
	     ;;
	  
	  17)
	     clearCRLs
	     ;;
	  
	  18)
	     clearMachineSSLCSR
	     ;;
   esac
}

#------------------------------
# Menu for options generating the certificate report
#------------------------------
function viewCertificateReportMenu() {
   unset CERTIFICATE_REPORT_INPUT
   header 'Certificate Report Options'
   cat << EOF
 1. Generate vCenter certificate report"
 2. Generate ESXi certificate report"
 3. Generate vCenter and ESXi certifiate report   
EOF
   
   read -p $'\nEnter report selection [Return to Main Menu]: ' CERTIFICATE_REPORT_INPUT
   
   if [ -z $CERTIFICATE_REPORT_INPUT ]; then return 1; fi
   
   if [ -f $VC_REPORT ]; then echo '' > $VC_REPORT; fi
   printf "\n"
   case $CERTIFICATE_REPORT_INPUT in
      1)
         generatevCenterCertificateReport
         ;;
      
      2)
         generateESXiCertificateReport
         ;;
      
      3)
         generatevCenterCertificateReport
         generateESXiCertificateReport
         ;;
   esac
}

#------------------------------
# Generate vCenter certificate report
#------------------------------
function generatevCenterCertificateReport() {
   if [ "$NODE_TYPE" != 'infrastructure' ] && ! checkService 'vmware-vpostgres'; then
      echo "${YELLOW}The vPostgres service is stopped!"
      echo "Please ensure this service is running before generating a certificate report."
      echo "Hint: Check the number of CRL entries in VECS${NORMAL}"
      return 1 
   fi

   authenticateIfNeeded
   disableColor

   printf '%0.1s' "="{1..130} | tee $VC_REPORT
   printf '\n' | tee -a $VC_REPORT
   echo 'SSL Certificate Report' | tee -a $VC_REPORT
   echo "vCert $VERSION" | tee -a $VC_REPORT
   echo "Host: $HOSTNAME" | tee -a $VC_REPORT
   echo "Date: $(date -u)" | tee -a $VC_REPORT
   echo "Node Type: $NODE_TYPE" | tee -a $VC_REPORT
   echo "Build: $VC_BUILD" | tee -a $VC_REPORT
   echo "Machine ID: $MACHINE_ID" | tee -a $VC_REPORT
   echo "PNID: $PNID" | tee -a $VC_REPORT
   if [ $NODE_TYPE != 'infrastructure' ]; then
      CERT_MGMT_MODE=$($PSQL -d VCDB -U postgres -c "SELECT value FROM vpx_parameter WHERE name='vpxd.certmgmt.mode'" -t | grep -v '^$')   
      echo "Certificate Management Mode: $CERT_MGMT_MODE" | tee -a $VC_REPORT
   fi
   printf '%0.1s' "="{1..130} | tee -a $VC_REPORT
   printf '\n' | tee -a $VC_REPORT
   
   VMDIR_CA_SUBJECT_IDS=''
   VECS_CA_SUBJECT_IDS=''
   for CNID in $($DIR_CLI trustedcert list --login "$VMDIR_USER_UPN" --password "$VMDIR_USER_PASSWORD" | grep 'CN(id)' | awk '{print $NF}'); do
      CERT=$($DIR_CLI trustedcert get --id $CNID --login "$VMDIR_USER_UPN" --password "$VMDIR_USER_PASSWORD" --outcert /dev/stdout | grep -v 'Certificate retrieved successfully')
      VMDIR_CERT_INFO=$(viewCertificateInfo "$CERT")
      
      VMDIR_CERT_SERIAL=$(echo "$VMDIR_CERT_INFO" | grep -A1 'Serial Number' | tail -n1 | tr -d ' ' | awk '{print toupper($0)}')
      VMDIR_CERT_SUBJECT=$(echo "$VMDIR_CERT_INFO" | grep 'Subject: ' | sed 's/Subject: //')
      VMDIR_CERT_SUBJECT_KEY=$(echo "$VMDIR_CERT_INFO" | grep -A1 'Subject Key Identifier' | tail -n1 | tr -d ' ')
      VMDIR_CA_SUBJECT_IDS+="serial:$VMDIR_CERT_SERIAL|DirName:$VMDIR_CERT_SUBJECT|keyid:$VMDIR_CERT_SUBJECT_KEY"$'\n'
   done
   
   IFS=$'\n'
   for alias in $($VECS_CLI entry list --store TRUSTED_ROOTS --text | grep 'Alias' | awk -F"[[:space:]]:[[:space:]]" '{print $NF}'); do
      CERT=$($VECS_CLI entry getcert --store TRUSTED_ROOTS --alias "$alias")
      VECS_CERT_INFO=$(viewCertificateInfo "$CERT")
      
      VECS_CERT_SERIAL=$(echo "$VECS_CERT_INFO" | grep -A1 'Serial Number' | tail -n1 | tr -d ' ' | awk '{print toupper($0)}')
      VECS_CERT_SUBJECT=$(echo "$VECS_CERT_INFO" | grep 'Subject: ' | sed 's/Subject: //')
      VECS_CERT_SUBJECT_KEY=$(echo "$VECS_CERT_INFO" | grep -A1 'Subject Key Identifier' | tail -n1 | tr -d ' ')
      VECS_CA_SUBJECT_IDS+="serial:$VECS_CERT_SERIAL|DirName:$VECS_CERT_SUBJECT|keyid:$VECS_CERT_SUBJECT_KEY"$'\n'
   done
   IFS=$' \t\n'
   
   echo 'VECS Certificates' | tee -a $VC_REPORT
   for store in $($VECS_CLI store list | grep -v 'APPLMGMT_PASSWORD'); do
      echo "   Store: $store" | tee -a $VC_REPORT
      
      IFS=$'\n'
      for alias in $($VECS_CLI entry list --store $store --text | grep 'Alias' | tr -d '\t' | awk -F':' '{print $NF}'); do
         echo "      Alias: $alias" | tee -a $VC_REPORT
         VECS_HASH=$($VECS_CLI entry getcert --store $store --alias "$alias" 2>/dev/null)
         if [[ $? -eq 0 ]]; then
            if ! echo "$VECS_HASH" | head -n1 | grep 'BEGIN CERTIFICATE' > /dev/null; then
               reportCRLDetails "$VECS_HASH"
            else
               case $store-$alias in
                  MACHINE_SSL_CERT-__MACHINE_CERT)
                     EXTRA_INFO='checkCurrentMachineSSLUsage'
                     ;;                  
                  vpxd-extension-vpxd-extension)
                     EXTRA_INFO='checkCurrentExtensionThumbprints'
                     ;;                  
                  *)
                     EXTRA_INFO=''
                     ;;
               esac
               
               reportCertDetails "$VECS_HASH" "$EXTRA_INFO"
            fi
         else
            echo "         |_No certificate found in store" | tee -a $VC_REPORT
         fi
      done
      IFS=$' \t\n'
   done

   echo 'VMware Directory Certificates' | tee -a $VC_REPORT
   echo '   CA Certificates' | tee -a $VC_REPORT
   for CNID in $($DIR_CLI trustedcert list --login "$VMDIR_USER_UPN" --password "$VMDIR_USER_PASSWORD" | grep 'CN(id)' | awk '{print $NF}'); do
      echo "      CN(id): $CNID" | tee -a $VC_REPORT
      VMDIR_CA_HASH=$($DIR_CLI trustedcert get --id $CNID --login "$VMDIR_USER_UPN" --password "$VMDIR_USER_PASSWORD" --outcert /dev/stdout | grep -v 'Certificate retrieved successfully')
      reportCertDetails "$VMDIR_CA_HASH"
   done
   
   echo '   Service Principal (Solution User) Certificates' | tee -a $VC_REPORT
   
   IFS=$'\n'
   for line in $($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=ServicePrincipals,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwServicePrincipal)' userCertificate); do
      if [[ "$line" =~ ^dn: ]]; then
         SERVICE_PRINCIPAL=$(echo "$line" | awk -F':' '{print $NF}' | awk -F',' '{print $1}' | awk -F'=' '{print $NF}')
         echo "      Service Principal: $SERVICE_PRINCIPAL" | tee -a $VC_REPORT
      else
         SERVICE_PRINCIPAL_CERT_HASH=$(echo "$line" | awk '{print $NF}')
         TEMP_CERT=$(buildCertFromHash "$SERVICE_PRINCIPAL_CERT_HASH")
         reportCertDetails "$TEMP_CERT"
      fi           
   done
   IFS=$' \t\n'
   echo '   Single Sign-On Secure Token Service Certificates' | tee -a $VC_REPORT
   TENANT_COUNT=0
   for hash in $($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSTenantCredential)' userCertificate | grep '^userCertificate' | awk '{print $NF}'); do
      TEMP_CERT=$(buildCertFromHash "$hash")
      
      if isCertCA "$TEMP_CERT"; then
         echo "      TenantCredential-$TENANT_COUNT CA Certificate" | tee -a $VC_REPORT
      else
         ((++TENANT_COUNT))
         echo "      TenantCredential-$TENANT_COUNT Signing Certificate" | tee -a $VC_REPORT  
      fi
      reportCertDetails "$TEMP_CERT"
   done
   
   CHAIN_COUNT=0  
   for hash in $($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=TrustedCertificateChains,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSTenantTrustedCertificateChain)' userCertificate | grep '^userCertificate' | awk '{print $NF}'); do
      TEMP_CERT=$(buildCertFromHash "$hash")
      
      if isCertCA "$TEMP_CERT"; then
         echo "      TrustedCertChain-$CHAIN_COUNT CA Certificate" | tee -a $VC_REPORT
      else
         ((++CHAIN_COUNT))
         echo "      TrustedCertChain-$CHAIN_COUNT Signing Certificate" | tee -a $VC_REPORT  
      fi
      reportCertDetails "$TEMP_CERT"
   done
   
   
   CAC_CAS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=DefaultClientCertCAStore,cn=ClientCertAuthnTrustedCAs,cn=Default,cn=ClientCertificatePolicies,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSTenantTrustedCertificateChain)' userCertificate 2>/dev/null | grep '^userCertificate' | awk '{print $NF}')
   
   if [ -n "$CAC_CAS" ]; then
      CAC_ISSUING_CA_COUNT=1
      echo '   Smart Card Issuing CA Certificates' | tee -a $VC_REPORT
      for hash in $CAC_CAS; do
         TEMP_CERT=$(buildCertFromHash "$hash")      
         echo "      Smart Card Issuing CA $CAC_ISSUING_CA_COUNT" | tee -a $VC_REPORT
         reportCertDetails "$TEMP_CERT"
         ((++CAC_ISSUING_CA_COUNT))
      done
   fi
   
   AD_LDAPS_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' userCertificate 2>/dev/null | grep '^userCertificate::' | awk '{print $NF}')
   
   if [ -n "$AD_LDAPS_CERTS" ]; then
      echo '   AD Over LDAPS Domain Controller Certificates' | tee -a $VC_REPORT
      LDAPS_DC_CERT_COUNT=1
      for hash in $AD_LDAPS_CERTS; do
         echo "      Certificate $LDAPS_DC_CERT_COUNT" | tee -a $VC_REPORT
         reportCertDetails "$(buildCertFromHash $hash)"
         ((++LDAPS_DC_CERT_COUNT))
      done
   fi
   
   echo 'Filesystem Certificates' | tee -a $VC_REPORT
   if [ "$NODE_TYPE" != 'management' ]; then
      if [[ "$VC_VERSION" =~ ^6 ]]; then
        echo '   VMware Directory Certificate' | tee -a $VC_REPORT
        echo '      Certificate: /usr/lib/vmware-vmdir/share/config/vmdircert.pem' | tee -a $VC_REPORT
        reportCertDetails "$(cat /usr/lib/vmware-vmdir/share/config/vmdircert.pem)"
      fi
      echo '   VMCA Certificate' | tee -a $VC_REPORT
      echo '      Certificate: /var/lib/vmware/vmca/root.cer' | tee -a $VC_REPORT
      reportCertDetails "$(cat /var/lib/vmware/vmca/root.cer)"
   fi
   if [ "$NODE_TYPE" != 'infrastructure' ]; then
      echo '   Authentication Proxy Certificate' | tee -a $VC_REPORT
      echo '      Certificate: /var/lib/vmware/vmcam/ssl/vmcamcert.pem' | tee -a $VC_REPORT
      reportCertDetails "$(cat /var/lib/vmware/vmcam/ssl/vmcamcert.pem)"
	  
      echo '   Auto Deploy CA Certificate' | tee -a $VC_REPORT
      echo '      Certificate: /etc/vmware-rbd/ssl/rbd-ca.crt' | tee -a $VC_REPORT
      reportCertDetails "$(cat /etc/vmware-rbd/ssl/rbd-ca.crt)"
   fi
   
   if grep '<clientCAListFile>' /etc/vmware-rhttpproxy/config.xml | grep -v '<!--' > /dev/null; then
      echo '   Smart Card Whitelist Certificates' | tee -a $VC_REPORT
      CAC_FILTER_FILE=$(grep '<clientCAListFile>' /etc/vmware-rhttpproxy/config.xml | grep -v '<!--' | awk -F'>' '{print $2}' | awk -F'<' '{print $1}')
      csplit -s -z -f $STAGE_DIR/cac_whitelist_ca- -b %02d.crt $CAC_FILTER_FILE '/-----BEGIN CERTIFICATE-----/' '{*}'
      WHITELIST_CERT_COUNT=1
      for cert in $(ls $STAGE_DIR/cac_whitelist_ca-*); do
         echo "      Certificate $WHITELIST_CERT_COUNT: $CAC_FILTER_FILE" | tee -a $VC_REPORT 
         reportCertDetails "$(cat $cert)"
         ((++WHITELIST_CERT_COUNT))
      done
   fi
   
   if tanzuSupervisorClustersPresent; then
      echo 'Tanzu Supervisor Cluster Certificates' | tee -a $VC_REPORT
      
      IFS=$'\n'
      for line in $(/usr/lib/vmware-wcp/decryptK8Pwd.py | grep -E '^Cluster: |^IP: |^PWD: '); do
         if [[ "$line" =~ ^Cluster ]]; then
            TANZU_CLUSTER_ID=$(echo "$line" | awk '{print $NF}' | awk -F':' '{print $1}' | sed -e 's/domain-c//')
	        TANZU_CLUSTER=$(/opt/vmware/vpostgres/current/bin/psql -d VCDB -U postgres -c "SELECT e.name FROM vpx_entity AS e LEFT JOIN vpx_object_type AS ot ON e.type_id = ot.id WHERE ot.name='CLUSTER_COMPUTE_RESOURCE' AND e.id=$TANZU_CLUSTER_ID" -t | sed -e 's/^[[:space:]]*//g' | grep -v '^$')   
         fi
         if [[ "$line" =~ ^IP ]]; then
            TANZU_CLUSTER_IP=$(echo "$line" | awk '{print $NF}')
         fi
         if [[ "$line" =~ ^PWD ]]; then
            TANZU_CLUSTER_PASSWD=$(echo "$line" | awk '{print $NF}')
            echo "   Cluster: $TANZU_CLUSTER" | tee -a $VC_REPORT
            ssh-keygen -R $TANZU_CLUSTER_IP 2>&1 | logDebug
            TANZU_CLUSTER_CERTIFICATES=$(sshpass -p "$TANZU_CLUSTER_PASSWD" ssh -q -o StrictHostKeyChecking=no -t -t root@$TANZU_CLUSTER_IP 'for cert in $(find / -type f \( -name "*.cert" -o -name "*.crt" \)  -print 2>/dev/null | egrep -v "ca.crt$|ca-bundle.crt$|kubelet\/pods|var\/lib\/containerd|run\/containerd|bootstrapper"); do echo "Certificate: $cert"; hash=$(openssl x509 -in $cert | grep -v '^-----' | tr -d "\n"); echo "Hash: $hash"; done')
			for line2 in $TANZU_CLUSTER_CERTIFICATES; do
			   if [[ "$line2" =~ ^Certificate ]]; then
			      echo "      $line2"
			   else
			      TANZU_CERT_HASH=$(echo "$line2" | awk '{print $NF}')
               TEMP_CERT=$(buildCertFromHash "$TANZU_CERT_HASH")
               reportCertDetails "$TEMP_CERT"
			   fi
			done
         fi
      done
      IFS=$' \t\n'
   fi
   
   echo 'Lookup Service Registration Trust Anchors' | tee -a $VC_REPORT
   
   getSSLTrustAnchorHashes

   for hash in "${CERT_HASHES[@]}"; do
      echo "      Endpoint Certificate $CERT_COUNT" | tee -a $VC_REPORT
      TEMP_CERT=$(buildCertFromHash "$hash")
      
      double_encoded_hash=$(echo "$hash" | tr -d '\n' | sed -e 's/.\{76\}/&\r\n/g' | xargs -0 printf "%s\r\n" | base64 -w 0)     
      
      USED_BY_SERVICE_IDS=$(getSSLTrustAnchorServiceIds "$hash" "$double_encoded_hash")
      NUM_USED_BY_SERVICE_IDS=$(echo "$USED_BY_SERVICE_IDS" | grep -v '^$' | wc -l)

      USED_BY_ENDPOINTS=$(getSSLTrustAnchorEndpoints "$hash" "$double_encoded_hash")   
      NUM_USED_BY_ENDPOINTS=$(echo "$USED_BY_ENDPOINTS" | grep -v '^$' | wc -l)

      ((++CERT_COUNT))
      
      reportTrustAnchorDetails "$TEMP_CERT" "$USED_BY_SERVICE_IDS" "$USED_BY_ENDPOINTS"
   done
   
   enableColor
   
   echo $'\n'"${YELLOW}Certificate report is available at ${VC_REPORT}${NORMAL}"$'\n'
}

#------------------------------
# Generate ESXi certificate report
#------------------------------
#function generateESXiCertificateReport() {
#
#}

#------------------------------
# CRL information for report
#------------------------------
function reportCRLDetails() {
   REPORT_CRL=$1
   REPORT_CRL_INFO=$(viewCRLInfo "$REPORT_CRL")
   REPORT_CRL_ISSUER=$(echo "$REPORT_CRL_INFO" | grep 'Issuer:' | awk -F'Issuer: ' '{print $NF}')
   REPORT_CRL_LAST_UPDATE=$(echo "$REPORT_CRL" | openssl crl -noout -lastupdate 2>/dev/null | sed 's/lastUpdate=//')
   REPORT_CRL_NEXT_UPDATE=$(echo "$REPORT_CRL" | openssl crl -noout -nextupdate 2>/dev/null | sed 's/nextUpdate=//')
   REPORT_CRL_SIGNATURE_ALGORITHM=$(echo "$REPORT_CRL_INFO" | grep 'Signature Algorithm' | head -n1 | awk '{print $NF}')
   REPORT_CRL_AUTH_KEYS=$(echo "$REPORT_CRL_INFO" | grep 'Authority Key Identifier' -A3 | grep -E 'keyid:|DirName:|issuer:' | tr -d ' ')
   
   echo "         Issuer: $REPORT_CRL_ISSUER" | tee -a $VC_REPORT
   echo "            Last Update: $REPORT_CRL_LAST_UPDATE" | tee -a $VC_REPORT
   echo "            Next Update: $REPORT_CRL_NEXT_UPDATE" | tee -a $VC_REPORT
   echo "            Signature Algorithm: $REPORT_CRL_SIGNATURE_ALGORITHM" | tee -a $VC_REPORT
}

#------------------------------
# Certificate information for report
#------------------------------
function reportCertDetails() {
   ISSUER_FOUND_VMDIR=0
   ISSUER_FOUND_VECS=0
   REPORT_CERT=${1}
   if isCertCA "$REPORT_CERT"; then REPORT_CERT_IS_CA='Yes'; else REPORT_CERT_IS_CA='No'; fi
   REPORT_CERT_INFO=$(viewCertificateInfo "$REPORT_CERT")
   REPORT_CERT_SUBJECT=$(echo "$REPORT_CERT_INFO" | grep 'Subject:' | awk -F'Subject: ' '{print $NF}')
   REPORT_CERT_ISSUER=$(echo "$REPORT_CERT_INFO" | grep 'Issuer:' | awk -F'Issuer: ' '{print $NF}')
   REPORT_CERT_VALID_START=$(echo "$REPORT_CERT" | openssl x509 -noout -startdate 2>/dev/null | sed 's/notBefore=//')
   REPORT_CERT_VALID_END=$(echo "$REPORT_CERT" | openssl x509 -noout -enddate 2>/dev/null | sed 's/notAfter=//')
   REPORT_CERT_FINGERPRINT=$(echo "$REPORT_CERT" | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $2}')
   REPORT_CERT_SIGNATURE_ALGORITHM=$(echo "$REPORT_CERT_INFO" | grep 'Signature Algorithm' | head -n1 | awk '{print $NF}')
   REPORT_CERT_SUBJECT_KEY=$(echo "$REPORT_CERT_INFO" | grep 'Subject Key Identifier:' -A1 | tail -n1 | tr -d ' ')
   REPORT_CERT_AUTH_KEYS=$(echo "$REPORT_CERT_INFO" | grep 'Authority Key Identifier' -A3 | grep -E 'keyid:|DirName:|issuer:' | tr -d ' ')
   REPORT_CERT_KEY_USAGE=$(echo "$REPORT_CERT_INFO" | grep 'X509v3 Key Usage' -A1 | tail -n1 | sed -e 's/^ *//g' -e 's/, /\n/g' | grep -v '^$')
   REPORT_CERT_KEY_EXT_USAGE=$(echo "$REPORT_CERT_INFO" | grep 'X509v3 Extended Key Usage' -A1 | tail -n1 | sed -e 's/^ *//g' -e 's/, /\n/g' | grep -v '^$')
   REPORT_CERT_SAN=$(echo "$REPORT_CERT_INFO" | grep 'X509v3 Subject Alternative Name' -A1 | tail -n1 | sed -e 's/^ *//g' -e 's/, /\n/g' | grep -v '^$' | sort)
         
   echo "         Issuer: $REPORT_CERT_ISSUER" | tee -a $VC_REPORT
   echo "         Subject: $REPORT_CERT_SUBJECT" | tee -a $VC_REPORT
   echo "            Not Before: $REPORT_CERT_VALID_START" | tee -a $VC_REPORT
   echo "            Not After : $REPORT_CERT_VALID_END" | tee -a $VC_REPORT
   echo "            SHA1 Fingerprint : $REPORT_CERT_FINGERPRINT" | tee -a $VC_REPORT
   echo "            Signature Algorithm: $REPORT_CERT_SIGNATURE_ALGORITHM" | tee -a $VC_REPORT
   echo "            Subject Key Identifier: $REPORT_CERT_SUBJECT_KEY" | tee -a $VC_REPORT   
   
   if [ -n "$REPORT_CERT_AUTH_KEYS" ]; then
      echo '            Authority Key Identifier:' | tee -a $VC_REPORT
      
	  IFS=$'\n'
      for auth_key in $(echo "$REPORT_CERT_AUTH_KEYS"); do
         echo "               |_$auth_key" | tee -a $VC_REPORT
         if echo "$VMDIR_CA_SUBJECT_IDS" | grep "$auth_key" > /dev/null; then ISSUER_FOUND_VMDIR=1; fi
         if echo "$VECS_CA_SUBJECT_IDS" | grep "$auth_key" > /dev/null; then ISSUER_FOUND_VECS=1; fi 
      done
      IFS=$' \t\n'
   fi
   
   if [[ $ISSUER_FOUND_VMDIR -eq 0 && $ISSUER_FOUND_VECS -eq 0 ]]; then
      if [[ "$REPORT_CERT_SUBJECT" == "$REPORT_CERT_ISSUER" ]]; then
         REPORT_CERT_ISSUER_FOUND='No (Self-Signed)'
      else
         REPORT_CERT_ISSUER_FOUND='No'
      fi
   elif [[ $ISSUER_FOUND_VMDIR -eq 1 && $ISSUER_FOUND_VECS -eq 0 ]]; then
      REPORT_CERT_ISSUER_FOUND='Yes, in VMware Directory'
   elif [[ $ISSUER_FOUND_VMDIR -eq 0 && $ISSUER_FOUND_VECS -eq 1 ]]; then
      REPORT_CERT_ISSUER_FOUND='Yes, in VECS'
   else
      REPORT_CERT_ISSUER_FOUND='Yes, in both'
   fi
   
   echo '            Key Usage:' | tee -a $VC_REPORT
   if [ -n "$REPORT_CERT_KEY_USAGE" ]; then 
      
	  IFS=$'\n'
      for key_usage in $(echo "$REPORT_CERT_KEY_USAGE"); do
         echo "               |_$key_usage" | tee -a $VC_REPORT
      done
      IFS=$' \t\n'
   fi
   echo '            Extended Key Usage:' | tee -a $VC_REPORT
   if [ -n "$REPORT_CERT_KEY_EXT_USAGE" ]; then 
      
	  IFS=$'\n'
      for ext_key_usage in $(echo "$REPORT_CERT_KEY_EXT_USAGE"); do
         echo "               |_$ext_key_usage" | tee -a $VC_REPORT
      done
      IFS=$' \t\n'
   fi
   echo '            Subject Alternative Name entries:' | tee -a $VC_REPORT
   if [ -n "$REPORT_CERT_SAN" ]; then
      
	  IFS=$'\n'
      for san in $(echo "$REPORT_CERT_SAN"); do
         echo "               |_$san" | tee -a $VC_REPORT
      done
      IFS=$' \t\n'
   fi
   
   echo '            Other Information:' | tee -a $VC_REPORT
   echo "               |_Is a Certificate Authority: $REPORT_CERT_IS_CA" | tee -a $VC_REPORT
   echo "               |_Issuing CA in VMware Directory/VECS: $REPORT_CERT_ISSUER_FOUND" | tee -a $VC_REPORT   
   
   if [ -n "$2" ]; then
      CUSTOM_INFO=$(echo "$2" | tr '|' '\n')
      
	  IFS=$'\n'
      for custom_call in $CUSTOM_INFO; do
         FUNCTION_STRING=$(echo "$custom_call" | tr ':' ' ')
         eval $FUNCTION_STRING
      done
      IFS=$' \t\n'
   fi
}

#------------------------------
# Trust Anchor information for report
#------------------------------
function reportTrustAnchorDetails() {
   TRUST_ANCHOR_CERT="$1"
   SERVICE_IDS="$2"
   ENDPOINTS="$3"
   TRUST_ANCHOR_INFO=$(viewCertificateInfo "$TRUST_ANCHOR_CERT")
   TRUST_ANCHOR_SUBJECT=$(echo "$TRUST_ANCHOR_INFO" | grep 'Subject:' | awk -F'Subject: ' '{print $NF}')
   TRUST_ANCHOR_ISSUER=$(echo "$TRUST_ANCHOR_INFO" | grep 'Issuer:' | awk -F'Issuer: ' '{print $NF}')
   TRUST_ANCHOR_VALID_START=$(echo "$TRUST_ANCHOR_CERT" | openssl x509 -noout -startdate 2>/dev/null | sed 's/notBefore=//')
   TRUST_ANCHOR_VALID_END=$(echo "$TRUST_ANCHOR_CERT" | openssl x509 -noout -enddate 2>/dev/null | sed 's/notAfter=//')
   TRUST_ANCHOR_FINGERPRINT=$(echo "$TRUST_ANCHOR_CERT" | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $2}')
   
   
   echo "         Issuer: $TRUST_ANCHOR_ISSUER" | tee -a $VC_REPORT
   echo "         Subject: $TRUST_ANCHOR_SUBJECT" | tee -a $VC_REPORT
   echo "            Not Before: $TRUST_ANCHOR_VALID_START" | tee -a $VC_REPORT
   echo "            Not After : $TRUST_ANCHOR_VALID_END" | tee -a $VC_REPORT
   echo "            SHA1 Fingerprint: $TRUST_ANCHOR_FINGERPRINT" | tee -a $VC_REPORT
   echo '            Service IDs:' | tee -a $VC_REPORT
   
   for service in $SERVICE_IDS; do
      echo "               |_$service" | tee -a $VC_REPORT
   done
   
   echo '            Endpoints:' | tee -a $VC_REPORT
   
   for endpoint in $ENDPOINTS; do
      echo "               |_$endpoint" | tee -a $VC_REPORT
   done
   
   return 0
}

#------------------------------
# Extra information regarding the Machine SSL certificate
#------------------------------
function checkCurrentMachineSSLUsage() {
   RHTTPPROXY_CERT_FINGERPRINT=$(echo | openssl s_client -connect localhost:443 2>/dev/null | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}')
   VPXD_CERT_FINGERPRINT=$(echo | openssl s_client -connect localhost:8089 2>/dev/null | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}')
   echo "               |_Current certificate used by the reverse proxy: $RHTTPPROXY_CERT_FINGERPRINT" | tee -a $VC_REPORT
   echo "               |_Current certificate used by vCenter (vpxd)   : $VPXD_CERT_FINGERPRINT" | tee -a $VC_REPORT
}

#------------------------------
# Extra information regarding the vpxd-extension certificate
#------------------------------
function checkCurrentExtensionThumbprints() {
   EAM_EXT_FINGERPRINT=$($PSQL -d VCDB -U postgres -c "SELECT thumbprint FROM vpx_ext WHERE ext_id = 'com.vmware.vim.eam'" -t | grep -v '^$' | tr -d ' ')
   RBD_EXT_FINGERPRINT=$($PSQL -d VCDB -U postgres -c "SELECT thumbprint FROM vpx_ext WHERE ext_id = 'com.vmware.rbd'" -t | grep -v '^$' | tr -d ' ')
   VUM_EXT_FINGERPRINT=$($PSQL -d VCDB -U postgres -c "SELECT thumbprint FROM vpx_ext WHERE ext_id = 'com.vmware.vcIntegrity'" -t | grep -v '^$' | tr -d ' ')
   VLCM_CLIENT_EXT_FINGERPRINT=$($PSQL -d VCDB -U postgres -c "SELECT thumbprint FROM vpx_ext WHERE ext_id = 'com.vmware.vlcm.client'" -t | grep -v '^$' | tr -d ' ')
   IMAGE_BUILDER_EXT_FINGERPRINT=$($PSQL -d VCDB -U postgres -c "SELECT thumbprint FROM vpx_ext WHERE ext_id = 'com.vmware.imagebuilder'" -t | grep -v '^$' | tr -d ' ')
   echo '               |_Thumbprints in VCDB for extensions that should use the vpxd-extension certificate' | tee -a ${REPORT}
   echo "                  |_com.vmware.vim.eam     : $EAM_EXT_FINGERPRINT" | tee -a $VC_REPORT
   echo "                  |_com.vmware.vcIntegrity : $VUM_EXT_FINGERPRINT" | tee -a $VC_REPORT
   
   if [ -n "$RBD_EXT_FINGERPRINT" ]; then
      echo "                  |_com.vmware.rbd         : $RBD_EXT_FINGERPRINT" | tee -a $VC_REPORT
   fi
   if [ -n "$IMAGE_BUILDER_EXT_FINGERPRINT" ]; then
      echo "                  |_com.vmware.imagebuilder: $IMAGE_BUILDER_EXT_FINGERPRINT" | tee -a $VC_REPORT
   fi
   if [[ "$VC_VERSION" =~ ^8 ]]; then
   echo "                  |_com.vmware.vlcm.client : $VLCM_CLIENT_EXT_FINGERPRINT" | tee -a $VC_REPORT
   fi
}

#------------------------------
# View VECS certificate info
#------------------------------
function viewVECSCertificateInfo() {
   logInfo "Viewing certificate in VECS store $1 with alias $2"
   CERT=$($VECS_CLI entry getcert --store $1 --alias $2 2>/dev/null)
   if [ -n "$CERT" ]; then
      logDetails "$CERT"
      viewCertificateInfo "$CERT" 'view-path'
   else
      logInfo "Unable to view the certificate in VECS store $1 with alias $2"
      echo $'\n'"${YELLOW}Unable to view the $1 certificate.${NORMAL}"
   fi
}

#------------------------------
# View certificate info from a file
#------------------------------
function viewFilesystemCertificateInfo() {
   logInfo "Viewing certificate at $1"
   CERT=$(cat $1 2>/dev/null)
   if [ -n "$CERT" ]; then
      logDetails "$CERT"
      viewCertificateInfo "$CERT" 'view-path'
   else
      logInfo "Unable to view the certificate at $1"
      echo $'\n'"${YELLOW}Unable to view certificate at $1.${NORMAL}"
   fi
}

#------------------------------
# View certificate info from a remote service
#------------------------------
function viewRemoteCertificateInfo() {
   logInfo "Viewing certificate at $1:$2"
   CERT=$(echo | openssl s_client -connect $1:$2 2>/dev/null | openssl x509 2>/dev/null)
   if [ -n "$CERT" ]; then
      logDetails "$CERT"
      viewCertificateInfo "$CERT" 'view-path'
   else
      logInfo "Unable to view certificate at $1:$2"
      echo $'\n'"${YELLOW}Unable to view certificate from $1:$2.${NORMAL}"
   fi
}

#------------------------------
# Manage VMDir CA certificates
#------------------------------
function manageVMDirCACertificates() {
   authenticateIfNeeded
   header 'CA Certificates in VMware Directory'
   listVMDirCACertificates
   
   case $1 in 
      'View')
         read -p $'\nSelect certificate [Return to Main Menu]: ' VIEW_VMDIR_CA_CERT_INPUT
   
         if [ -n "$VIEW_VMDIR_CA_CERT_INPUT" ]; then
            viewVMDirCACertificate "$VIEW_VMDIR_CA_CERT_INPUT"
         fi
      ;;
      
      'Manage')
         header 'Manage Certificates in VMware Directory'
         echo ' 1. Publish CA certificate(s) to VMware Directory'
         echo ' 2. Remove CA certificate(s) from VMware Directory'
         if [ -n "$SDDC_MANAGER" ]; then echo ' 3. Publish CA certificate(s) to SDDC Manager'; fi
   
         read -p $'\nSelect an option [Return to Main Menu]: ' MANAGE_VMDIR_CA_CERT_INPUT
   
         case $MANAGE_VMDIR_CA_CERT_INPUT in
            1)
               publishCACertsVMDir
               ;;
            2)
               removeCACertsVMDir
               ;;
            3)
			      if [ -n "$SDDC_MANAGER" ]; then 
			         publishVMDirCACertSDDCManager
			      else
			         echo $'\n'"${YELLOW}Invalid option.${NORMAL}"
			      fi
			      ;;
         esac
         ;;
   esac  
}

#------------------------------
# Add new STS signing certificate
#------------------------------
function replaceSSOSTSCert() {
   if [ $STS_REPLACE == 'VMCA-SIGNED' ]; then
      STS_CERT=$STAGE_DIR/sso-sts.crt
      STS_PUBKEY=$STAGE_DIR/sso-sts.pub
      STS_KEY=$STAGE_DIR/sso-sts.key

      header 'Replace SSO STS Signing Certificate'      
      generateCertoolConfig 'sts' 'ssoserverSign'
      task 'Regenerate STS signing certificate'
      regenerateVMCASignedCertificate 'sso-sts'
      statusMessage 'OK' 'GREEN'
      TRUSTED_ROOT_CHAIN=$VMCA_CERT
   else
      unset STS_CA_SIGNED_OPTION_INPUT
      echo $'\n1. Generate Certificate Signing Request and Private Key'
      echo '2. Generate Certificate Signing Request and Private Key'
      echo '   from custom OpenSSL configuration file'
      echo '3. Import CA-signed Certificate and Key'
      read -p $'\nSelect an option [Return to Main Menu]: ' STS_CA_SIGNED_OPTION_INPUT
	  
	   if [ -z $STS_CA_SIGNED_OPTION_INPUT ]; then return 1; fi

      if [ "$STS_CA_SIGNED_OPTION_INPUT" == '3' ]; then
         logInfo 'User has chosen to import a CA-signed STS Signing certificate and key'
         read -e -p $'\n'"Provide path to the CA-signed ${CYAN}Machine SSL${NORMAL} certificate: " STS_CERT_INPUT
         while [ ! -f "$STS_CERT_INPUT" ]; do read -e -p "${YELLOW}File not found, please provide path to the STS Signing certificate:${NORMAL} " STS_CERT_INPUT; done
         
         getCorrectCertFormat "$STS_CERT_INPUT" 'STS_CERT'
         STS_CERT_MODULUS_HASH=$(openssl x509 -noout -modulus -in $STS_CERT 2>/dev/null | md5sum | awk '{print $1}')
		 
		   getPrivateKey "$STS_CERT_MODULUS_HASH" "STS" 'STS Signing'     
		   getCAChain "$STS_CERT"

         header 'Certificate Verification'
         task 'Verifying certificate and key'        
        
         logDebug "Using STS Signing cert: $STS_CERT"
         logDebug "Using Private Key: $STS_KEY"
         logDebug "Using trusted root chain: $TRUSTED_ROOT_CHAIN"
        
         verifyCertAndKey "$STS_CERT" "$STS_KEY" 
         statusMessage 'OK' 'GREEN'
        
         task 'Verifying root chain'
         verifyRootChain $STS_CERT $TRUSTED_ROOT_CHAIN || errorMessage 'Certificate Authority chain is not complete'
         statusMessage 'OK' 'GREEN'
                
         header 'Replace SSO STS Signing Certificate'                 
         task 'Publish CA signing certificates'
		   publishCASigningCertificates $TRUSTED_ROOT_CHAIN
      else
         unset STS_CN_INPUT
         STS_CSR=$REQUEST_DIR/sso-sts-$TIMESTAMP.csr
         STS_KEY=$REQUEST_DIR/sso-sts-$TIMESTAMP.key
                  
         if [ "$STS_CA_SIGNED_OPTION_INPUT" == '1' ]; then
            STS_CFG=$REQUEST_DIR/sso-sts.cfg
            logInfo 'User has chosen to generate the STS Signing private key and CSR'
            if [ -z "$CSR_COUNTRY" ]; then getCSRInfo; fi

            read -p "Enter a value for the ${CYAN}CommonName${NORMAL} of the certificate [ssoserver]: " STS_CN_INPUT

            if [ -z "$STS_CN_INPUT" ]; then STS_CN_INPUT='ssoserver'; fi

            checkPSCHA
            defineSANEntries 'sso-sts' 'STS Signing'
            generateOpensslConfig $STS_CN_INPUT $STS_CFG 'sso-sts'
         else
            logInfo 'User has chosen to generate the STS Signing private key and CSR from a custom OpenSSL configuration file'
            read -e -p $'\nEnter path to custom OpenSSL configuration file: ' STS_CFG
            while [ ! -f "$STS_CFG" ]; do read -e -p "${YELLOW}File not found, enter path to custom OpenSSL configuration file:${NORMAL} " STS_CFG; done
         fi
         
         generateCSR $STS_CSR $STS_KEY "$STS_CFG" || errorMessage "Unable to generate Certificate Signing Request and Private Key"
         
         printf "\nCertificate Signing Request generated at ${CYAN}${STS_CSR}${NORMAL}"
         printf "\nPrivate Key generated at ${CYAN}${STS_KEY}${NORMAL}\n"
         
         return 0
      fi
   fi

   task 'Backup and delete tenant credentials'
   TENANT_CREDS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSTenantCredential)' dn | sed 's/^dn: //g')
   i=1
   for credential in $TENANT_CREDS; do
      logInfo "Backing up credential $credential"
      $LDAP_SEARCH -h $PSC_LOCATION -p $VMDIR_PORT -D "cn=${VMDIR_USER},cn=users,${VMDIR_DOMAIN_DN}" -y $STAGE_DIR/.vmdir-user-password -b "$credential" 2>/dev/null > $BACKUP_DIR/TenantCredential-$i.ldif
      logInfo "Deleting credential $credential"
      $LDAP_DELETE -h $PSC_LOCATION -p $VMDIR_PORT -D "cn=${VMDIR_USER},cn=users,${VMDIR_DOMAIN_DN}" -y $STAGE_DIR/.vmdir-user-password "$credential" 2>&1 | logDebug
      ((i++))
   done
   statusMessage 'OK' 'GREEN'
   
   task 'Backup and delete trusted cert chains'
   
   TRUSTED_CHAINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSTenantTrustedCertificateChain)' dn | sed 's/^dn: //g')
   i=1
   for chain in $TRUSTED_CHAINS; do
      logInfo "Backing up chain $chain"
      $LDAP_SEARCH -h $PSC_LOCATION -p $VMDIR_PORT -D "cn=${VMDIR_USER},cn=users,${VMDIR_DOMAIN_DN}" -y $STAGE_DIR/.vmdir-user-password -b "$chain" 2>/dev/null > $BACKUP_DIR/TrustedCertChain-$i.ldif
      logInfo "Deleting chain $chain"
      $LDAP_DELETE -h $PSC_LOCATION -p $VMDIR_PORT -D "cn=${VMDIR_USER},cn=users,${VMDIR_DOMAIN_DN}" -y $STAGE_DIR/.vmdir-user-password "$chain" 2>&1 | logDebug
      ((i++))
   done
   statusMessage 'OK' 'GREEN'

   task 'Add new STS signing certifcate to VMDir'   

   openssl x509 -outform der -in $STS_CERT -out $STAGE_DIR/sso-sts.der 2>/dev/null || errorMessage 'Unable to create binary SSO STS certificate'
   openssl pkcs8 -topk8 -inform pem -outform der -in $STS_KEY -out $STAGE_DIR/sso-sts.key.der -nocrypt 2>/dev/null || errorMessage 'Unable to create binary SSO STS private key'
   
   STS_CA_CERTS=$(csplit -z -f $STAGE_DIR/sts-ca-cert- -b %02d.crt $TRUSTED_ROOT_CHAIN '/-----BEGIN CERTIFICATE-----/' '{*}' | wc -l)
   i=0
   until [ $i -eq $STS_CA_CERTS ]; do
      openssl x509 -outform der -in $STAGE_DIR/sts-ca-cert-0$i.crt -out $STAGE_DIR/sts-ca-cert-0$i.der 2>&1 | logDebug
      ((i++))
   done
   
   echo "dn: cn=TenantCredential-1,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" > $STAGE_DIR/sso-sts.ldif
   echo 'changetype: add' >> $STAGE_DIR/sso-sts.ldif
   echo 'objectClass: vmwSTSTenantCredential' >> $STAGE_DIR/sso-sts.ldif
   echo 'objectClass: top' >> $STAGE_DIR/sso-sts.ldif
   echo 'cn: TenantCredential-1' >> $STAGE_DIR/sso-sts.ldif
   echo "userCertificate:< file://$STAGE_DIR/sso-sts.der" >> $STAGE_DIR/sso-sts.ldif
   
   i=0
   until [ $i -eq $STS_CA_CERTS ]; do
      echo "userCertificate:< file:$STAGE_DIR/sts-ca-cert-0${i}.der" >> $STAGE_DIR/sso-sts.ldif
      ((i++))
   done
   
   echo "vmwSTSPrivateKey:< file://$STAGE_DIR/sso-sts.key.der" >> $STAGE_DIR/sso-sts.ldif
   echo '' >> $STAGE_DIR/sso-sts.ldif
   echo "dn: cn=TrustedCertChain-1,cn=TrustedCertificateChains,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" >> $STAGE_DIR/sso-sts.ldif
   echo 'changetype: add' >> $STAGE_DIR/sso-sts.ldif
   echo 'objectClass: vmwSTSTenantTrustedCertificateChain' >> $STAGE_DIR/sso-sts.ldif
   echo 'objectClass: top' >> $STAGE_DIR/sso-sts.ldif
   echo 'cn: TrustedCertChain-1' >> $STAGE_DIR/sso-sts.ldif
   echo "userCertificate:< file://$STAGE_DIR/sso-sts.der" >> $STAGE_DIR/sso-sts.ldif
   
   i=0
   until [ $i -eq $STS_CA_CERTS ]; do
      echo "userCertificate:< file:$STAGE_DIR/sts-ca-cert-0$i.der" >> $STAGE_DIR/sso-sts.ldif
      ((i++))
   done

   $LDAP_MODIFY -v -h $PSC_LOCATION -p $VMDIR_PORT -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password -f $STAGE_DIR/sso-sts.ldif 2>&1 | logDebug

   statusMessage 'OK' 'GREEN'
}

#------------------------------
# Remove certificates from VMDir
#------------------------------
function removeCACertsVMDir() {
   read -p $'\nEnter the number(s) of the certificate(s) to delete (multiple entries separated by a comma): ' DELETE_VMDIR_CA_LIST
   
   if [ -n "$DELETE_VMDIR_CA_LIST" ]; then
      header 'Removing CA certificates from VMware Directory'
      for index in $(echo "$DELETE_VMDIR_CA_LIST" | tr -d ' ' | sed 's/,/ /g'); do
         skid=${VMDIR_CA_CERT_SKIDS[$((index - 1))]}
         task "Backup $skid"
         if $DIR_CLI trustedcert get --id $skid --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" --outcert $BACKUP_DIR/$skid.crt 2>&1 | logInfo; then
            statusMessage 'OK' 'GREEN'
         else
            errorMessage "Unable to backup certificate with Subject Key ID $skid" 'backup'
         fi
         
         task "Remove $skid"
         $DIR_CLI trustedcert unpublish --cert $BACKUP_DIR/$skid.crt --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>&1 | logInfo
         if [ $? -eq 0 ]; then
            statusMessage 'OK' 'GREEN'
         else
            statusMessage 'FAILED' 'YELLOW'
            task "Remove $skid directly"
            $LDAP_DELETE -h $PSC_LOCATION -p $VMDIR_PORT -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password "cn=$skid,cn=Certificate-Authorities,cn=Configuration,$VMDIR_DOMAIN_DN" 2>&1 | logDebug || errorMessage "Unable to delete certificate with Subject Key ID $skid from VMware Directory"
            statusMessage 'OK' 'GREEN'
         fi
      done
      task 'Refreshing CA certificates to VECS'
      $VECS_CLI force-refresh 2>/dev/null || errorMessage 'Error refreshing CA certificates to VECS'
      statusMessage 'OK' 'GREEN'
   fi
}

#------------------------------
# Publish CA certificate(s) from VMDir to SDDC Manager
#------------------------------
function publishVMDirCACertSDDCManager() {
   read -p $'\n'"Enter the number(s) of the certificate(s) to publish to $SDDC_MANAGER (multiple entries separated by a comma): " PUBLISH_VMDIR_CA_SDDC_LIST
   
   if [ -n "$PUBLISH_VMDIR_CA_SDDC_LIST" ]; then
      header 'Publishing CA certificates to SDDC Manager'
      getSDDCAccessToken
      for index in $(echo "$PUBLISH_VMDIR_CA_SDDC_LIST" | tr -d ' ' | sed 's/,/ /g'); do
         skid=${VMDIR_CA_CERT_SKIDS[$((index - 1))]}
         task 'Export cert from VMware Directory'
         $DIR_CLI trustedcert get --id $skid --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" --outcert $STAGE_DIR/sddc_$skid.crt 2>&1 | logInfo || errorMessage "Unable to backup certificate with Subject Key ID $skid" 'backup'
         statusMessage 'OK' 'GREEN'
		 
         publishCACertsSDDCManager "$STAGE_DIR/sddc_$skid.crt"
      done
      echo $'\n'"${YELLOW}Services will need to be restarted on the SDDC Manager"
      echo "by running /opt/vmware/vcf/operationsmanager/scripts/cli/sddcmanager_restart_services.sh${NORMAL}"
   fi
}

#------------------------------
# View VECS CA certificates
#----------------------------
function manageVECSCACertificates() {
   header 'CA Certificates in TRUSTED_ROOTS store in VECS'
   listVECSCACertificates
   
   case $1 in
      'View')
         read -p $'\nSelect certificate [Return to Main Menu]: ' VIEW_VECS_CA_CERT_INPUT
   
         if [ -n "$VIEW_VECS_CA_CERT_INPUT" ]; then
            viewVECSCACertificate "$VIEW_VECS_CA_CERT_INPUT"
         fi
         ;;
      
      'Manage')
         header 'Manage Certificates in VECS'
         echo ' 1. Remove CA certificate(s) from VMware Directory'
         if [ -n "$SDDC_MANAGER" ]; then echo ' 2. Publish CA certificate(s) to SDDC Manager'; fi
   
         read -p $'\nSelect an option [Return to Main Menu]: ' MANAGE_VECS_CA_CERT_INPUT
		 
         case $MANAGE_VECS_CA_CERT_INPUT in
		 	   1)	 
               removeCACertsVECS
		         ;;			
			   2)
			      if [ -n "$SDDC_MANAGER" ]; then 
			         publishVECSCACertSDDCManager
			      else
			         echo $'\n'"${YELLOW}Invalid option.${NORMAL}"
			      fi
               ;;			
		   esac
         ;;   
   esac
}

#------------------------------
# Remove CA certificates from VECS
#------------------------------
function removeCACertsVECS() {
   echo $'\n'"${CYAN}To add CA certificates to VECS, publish them to VMware Directory.${NORMAL}"
   read -p $'\nEnter the number(s) of the certificate(s) to delete (multiple entries separated by a comma): ' DELETE_VECS_CA_LIST
   
   if [ -n "$DELETE_VECS_CA_LIST" ]; then
      header 'Removing CA certificates from VECS'
      for index in $(echo "$DELETE_VECS_CA_LIST" | tr -d ' ' | sed 's/,/ /g'); do
         alias=${VECS_CA_CERT_ALIASES[$((index - 1))]}
         task "Backup $alias"
		 BACKUP_ALIAS_FILENAME=$(echo "$alias" | tr '/' '_')
         if $VECS_CLI entry getcert --store TRUSTED_ROOTS --alias $alias > $BACKUP_DIR/$BACKUP_ALIAS_FILENAME.crt 2>/dev/null; then
            statusMessage 'OK' 'GREEN'
         else
            errorMessage "Unable to backup certificate with $alias" 'backup'
         fi
         
         task "Remove $alias"
         $VECS_CLI entry delete --store TRUSTED_ROOTS --alias $alias -y 2>/dev/null|| errorMessage "Unable to delete certificate with Alias $alias"
         statusMessage 'OK' 'GREEN'         
      done      
   fi
}

#------------------------------
# Publish CA certificate(s) from VECS to SDDC Manager
#------------------------------
function publishVECSCACertSDDCManager() {
   read -p $'\n'"Enter the number(s) of the certificate(s) to publish to $SDDC_MANAGER (multiple entries separated by a comma): " PUBLISH_VECS_CA_SDDC_LIST
   
   if [ -n "$PUBLISH_VECS_CA_SDDC_LIST" ]; then
      header 'Publishing CA certificates to SDDC Manager'
	  getSDDCAccessToken
	  for index in $(echo "$PUBLISH_VECS_CA_SDDC_LIST" | tr -d ' ' | sed 's/,/ /g'); do
	     alias=${VECS_CA_CERT_ALIASES[$((index - 1))]}
		 task "Export $alias"
		 $VECS_CLI entry getcert --store TRUSTED_ROOTS --alias $alias > $STAGE_DIR/sddc_$alias.crt 2>/dev/null || errorMessage "Unable to backup certificate with alias $alias" 'backup'
		 statusMessage 'OK' 'GREEN'
		 
		 publishCACertsSDDCManager "$STAGE_DIR/sddc_$alias.crt"
	  done
      echo $'\n'"${YELLOW}Services will need to be restarted on the SDDC Manager"
      echo "by running /opt/vmware/vcf/operationsmanager/scripts/cli/sddcmanager_restart_services.sh${NORMAL}"
   fi
}

#------------------------------
# List CA certificates in VMware Directory
#------------------------------
function listVMDirCACertificates() {
   VMDIR_CERTS=()
   VMDIR_CA_CERT_SKIDS=()
   logInfo 'Listing certificate information for CA certificates in VMware Directory'
   for skid in $($DIR_CLI trustedcert list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | tr -d '\t' | awk -F':' '{print $2}'); do
      CA_CERT=$($DIR_CLI trustedcert get --id $skid --outcert $STAGE_DIR/$skid.crt --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" --outcert /dev/stdout | grep -v 'Certificate retrieved successfully')      
      VMDIR_CA_CERT_INFO=$(viewBriefCertificateInfo "$CA_CERT" '' "$skid")
      VMDIR_CERTS+=("$VMDIR_CA_CERT_INFO")
      VMDIR_CA_CERT_SKIDS+=($skid)           
   done
   i=0
   while [ $i -lt "${#VMDIR_CERTS[@]}" ]; do
      n=$((i+1))
      printf "%2s. %s\n\n" $n "${VMDIR_CERTS[$i]}"
      logDetails "${VMDIR_CERTS[$i]}"
      logDetails ' '
      ((++i))
   done
}

#------------------------------
# Publish CA certificate(s) to VMware Directory
#------------------------------
function publishCACertsVMDir() {
   read -e -p $'\nEnter path to CA certificate (or chain): ' CA_CERTS_TO_PUBLISH
   i=0
   NON_CA_FOUND=0
   CA_PUBLISHED=0
   UPDATED_EMBEDDED=0
   while [ ! -f $CA_CERTS_TO_PUBLISH ]; do
      if [ $i -lt 3 ]; then
	      read -e -p 'File not found. Enter path to CA certificate (or chain): ' CA_CERTS_TO_PUBLISH
	   else
	      errorMessage "CA Certificate file not found at $CA_CERTS_TO_PUBLISH"
	   fi
   done
   getCorrectCertFormat "$CA_CERTS_TO_PUBLISH" 'CA_CERTS_TO_PUBLISH'
   logInfo "Provided new CA certificates to publish to VMware Directory:"
   logDetails "$(cat $CA_CERTS_TO_PUBLISH)"
   header 'Publish CA Certificate(s)'
   VMDIR_CA_SKIDS=$($DIR_CLI trustedcert list --login $VMDIR_USER --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | tr -d '\t' | awk -F':' '{print $2}')   
   csplit -s -z -f $STAGE_DIR/ca-to-publish- -b %02d.crt $CA_CERTS_TO_PUBLISH '/-----BEGIN CERTIFICATE-----/' '{*}'
   for cert in $(ls $STAGE_DIR/ca-to-publish-*); do
      if isCertCA "$(cat $cert)"; then
	      CURRENT_SKID=$(openssl x509 -noout -text -in $cert 2>/dev/null | grep -A1 'Subject Key Id' | tail -n1 | tr -d ' ' | sed 's/keyid://' | tr -d ':')
         if echo "$VMDIR_CA_SKIDS" | grep "$CURRENT_SKID" > /dev/null; then
            logInfo "Found CA certificate with Subject Key ID $CURRENT_SKID, unpublishing"
            $DIR_CLI trustedcert get --id $CURRENT_SKID --login $VMDIR_USER --password "$(cat $STAGE_DIR/.vmdir-user-password)" --outcert $STAGE_DIR/signing-ca-old-$CURRENT_SKID.crt 2>&1 | logInfo
            $DIR_CLI trustedcert unpublish --login $VMDIR_USER --password "$(cat $STAGE_DIR/.vmdir-user-password)" --cert $STAGE_DIR/signing-ca-old-$CURRENT_SKID.crt 2>&1 | logInfo
         fi
         logInfo "Publishing CA certificate with Subject Key ID $CURRENT_SKID"
         if $DIR_CLI trustedcert publish --cert $cert --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>&1 | logInfo; then
            checkUpdateEmbeddedCAChain "$cert"
            ((++CA_PUBLISHED))
         fi
      else
         ((++NON_CA_FOUND))
      fi 
	done	
	
   echo "Published ${GREEN}${CA_PUBLISHED}${NORMAL} certificates to VMware Directory"
   statusMessage 'OK' 'GREEN'
	
   if [ $NON_CA_FOUND -gt 0 ]; then
	   echo $'\n'"Found ${YELLOW}${NON_CA_FOUND}${NORMAL} non-CA certificates in the provided file."
		echo 'These certificates were not published to VMware Directory.'
   fi

   if [ $CA_PUBLISHED -gt 0 ]; then
      task 'Refreshing CA certificates to VECS'
      $VECS_CLI force-refresh 2>/dev/null || errorMessage 'Unable to perform a force-refresh of CA certificates to VECS'
      statusMessage 'OK' 'GREEN'
   fi
   
   if [ $UPDATED_EMBEDDED -gt 0 ]; then
      echo $'\n'"${YELLOW}Certificate(s) with an embedded CA chain have been updated.${NORMAL}"
      promptRestartVMwareServices
   fi
   
   rm $STAGE_DIR/ca-to-publish-*
}

#------------------------------
# Check if we need to update CA cert in embedded chain
#------------------------------
function checkUpdateEmbeddedCAChain() {
   CURRENT_SKID=$(cat "$1" | openssl x509 -noout -text 2>/dev/null | grep -A1 'Subject Key Id' | tail -n1)
   
   MACHINE_SSL_CERT=$($VECS_CLI entry getcert --store MACHINE_SSL_CERT --alias __MACHINE_CERT)
   if [ $(echo "$MACHINE_SSL_CERT" | grep 'BEGIN CERTIFICATE' | wc -l) -gt 1 ]; then
      if echo "$MACHINE_SSL_CERT" | openssl crl2pkcs7 -nocrl -certfile /dev/stdin 2>/dev/null | openssl pkcs7 -print_certs -noout -text 2>/dev/null | grep "$CURRENT_SKID" > /dev/null; then
         updateEmbeddedCACertVECS "$CURRENT_SKID" "$1" 'MACHINE_SSL_CERT' '__MACHINE_CERT'
      fi
   fi
   
   for soluser in "${SOLUTION_USERS[@]}"; do
      SOLUTION_USER_CERT=$($VECS_CLI entry getcert --store $soluser --alias $soluser)
	  if [ $(echo "$SOLUTION_USER_CERT" | grep 'BEGIN CERTIFICATE' | wc -l) -gt 1 ]; then
	     if echo "$SOLUTION_USER_CERT" | openssl crl2pkcs7 -nocrl -certfile /dev/stdin 2>/dev/null | openssl pkcs7 -print_certs -noout -text 2>/dev/null | grep "$CURRENT_SKID" > /dev/null; then
            updateEmbeddedCACertVECS "$CURRENT_SKID" "$1" "$soluser" "$soluser"
         fi
	  fi
   done
   
   AUTH_PROXY_CERT='/var/lib/vmware/vmcam/ssl/rui.crt'
   if [ $(cat "$AUTH_PROXY_CERT" | grep 'BEGIN CERTIFICATE' | wc -l) -gt 1 ]; then
      if cat "$AUTH_PROXY_CERT" | openssl crl2pkcs7 -nocrl -certfile /dev/stdin 2>/dev/null | openssl pkcs7 -print_certs -noout -text 2>/dev/null | grep "$CURRENT_SKID" > /dev/null; then
         updateEmbeddedCACertFile "$CURRENT_SKID" "$1" "$AUTH_PROXY_CERT" 'Auth Proxy Cert'
      fi
   fi
   
   AUTO_DEPLOY_CA_CERT='/etc/vmware-rbd/ssl/rbd-ca.crt'
   if [ $(cat "$AUTO_DEPLOY_CA_CERT" | grep 'BEGIN CERTIFICATE' | wc -l) -gt 1 ]; then
      if cat "$AUTH_PROXY_CERT" | openssl crl2pkcs7 -nocrl -certfile /dev/stdin 2>/dev/null | openssl pkcs7 -print_certs -noout -text 2>/dev/null | grep "$CURRENT_SKID" > /dev/null; then
         updateEmbeddedCACertFile "$CURRENT_SKID" "$1" "$AUTO_DEPLOY_CA_CERT" 'Auto Deploy CA Cert'
      fi
   fi
   
   if [ $(cat "$VMCA_CERT" | grep 'BEGIN CERTIFICATE' | wc -l) -gt 1 ]; then
      if cat "$VMCA_CERT" | openssl crl2pkcs7 -nocrl -certfile /dev/stdin 2>/dev/null | openssl pkcs7 -print_certs -noout -text 2>/dev/null | grep "$CURRENT_SKID" > /dev/null; then
         updateEmbeddedCACertFile "$CURRENT_SKID" "$1" "$VMCA_CERT" 'VMCA Cert'
      fi
   fi
   
   if [[ "$VC_VERSION" =~ ^6 ]]; then
      VMDIR_CERT='/usr/lib/vmware-vmdir/share/config/vmdircert.pem'
      if [ $(cat "$VMDIR_CERT" | grep 'BEGIN CERTIFICATE' | wc -l) -gt 1 ]; then
         if cat "$VMDIR_CERT" | openssl crl2pkcs7 -nocrl -certfile /dev/stdin 2>/dev/null | openssl pkcs7 -print_certs -noout -text 2>/dev/null | grep "$CURRENT_SKID" > /dev/null; then
            updateEmbeddedCACertFile "$CURRENT_SKID" "$1" "$VMDIR_CERT" 'VMDir Cert'
         fi
      fi
   fi
}

#------------------------------
# Update CA cert in embedded chain in VECS
#------------------------------
function updateEmbeddedCACertVECS() {
   NEW_CA_SERIAL=$(openssl x509 -noout -serial -in $2 2>/dev/null)
   NEW_CA_SUBJECT=$(openssl x509 -noout -subject -in $2 2>/dev/null)
   NEEDS_UPDATING=0
   logInfo "Splitting certs from alias $4 in store $3"
   $VECS_CLI entry getcert --store "$3" --alias "$4" | csplit -s -z -f $STAGE/update-embedded-ca- -b %02d.crt /dev/stdin '/-----BEGIN CERTIFICATE-----/' '{*}'
   
   for cert in $(ls -d $STAGE/update-embedded-ca-*.crt); do
      CURRENT_CERT_SERIAL=$(openssl x509 -noout -serial -in $cert 2>/dev/null)      
      if openssl x509 -noout -text -in $cert 2>/dev/null | grep -A1 'Subject Key Id' | tail -n1 | grep "$1" > /dev/null; then
         if [ "$NEW_CA_SERIAL" != "$CURRENT_CERT_SERIAL" ]; then
            NEEDS_UPDATING=1
            logInfo "Checking new CA cert ($2) against embedded CA file ($cert)"
            logInfo "Updating CA cert $NEW_CA_SUBJECT in alias $4, store $3"
            logInfo "Checking $NEW_CA_SUBJECT checksum ($NEW_CA_SERIAL) against embedded CA checksum ($CURRENT_CERT_SERIAL)"
         fi
         cat $2 >> $STAGE/update-embedded-cert.pem		 
      else
         cat $cert >> $STAGE/update-embedded-cert.pem
      fi
   done
   
   if [ $NEEDS_UPDATING -gt 0 ]; then
      task 'Updating embedded CA cert in VECS'
      $VECS_CLI entry getkey --store "$3" --alias "$4" > $STAGE/update-embedded-key.key 2>/dev/null || errorMessage "Unable to export key from store $3, alias $4"
      $VECS_CLI entry delete --store "$3" --alias "$4" -y 2>/dev/null || errorMessage "Unable to delete alias $4 from store $3"
      $VECS_CLI entry create --store "$3" --alias "$4" --cert $STAGE/update-embedded-cert.pem --key $STAGE/update-embedded-key.key 2>/dev/null || errorMessage "Unable to create alias $4 in store $3"
      statusMessage 'OK' 'GREEN'
      UPDATED_EMBEDDED=1
   fi
   
   rm $STAGE/update-embedded-* 2>&1 | logDebug
}

#------------------------------
# Update CA cert in embedded chain in a file
#------------------------------
function updateEmbeddedCACertFile() {
   NEW_CA_SERIAL=$(openssl x509 -noout -serial -in $2 2>/dev/null)
   NEW_CA_SUBJECT=$(openssl x509 -noout -subject -in $2 2>/dev/null)
   NEEDS_UPDATING=0
   csplit -s -z -f $STAGE/update-embedded-ca- -b %02d.crt $3 '/-----BEGIN CERTIFICATE-----/' '{*}'
   
   for cert in $(ls -d $STAGE/update-embedded-ca-*.crt); do
      if openssl x509 -noout -text -in $cert 2>/dev/null | grep -A1 'Subject Key Id' | tail -n1 | grep "$1" > /dev/null; then
	     CURRENT_CERT_SERIAL=$(openssl x509 -noout -serial -in $cert 2>/dev/null)      
		 if [ "$NEW_CA_SERIAL" != "$CURRENT_CERT_SERIAL" ]; then
		    NEEDS_UPDATING=1					 
		 fi
		 cat $2 >> $STAGE/update-embedded-cert.pem
		 logInfo "Updating CA cert $NEW_CA_SUBJECT in $3"
	  else
	     cat $cert >> $STAGE/update-embedded-cert.pem
	  fi
   done
   
   if [ $NEEDS_UPDATING -gt 0 ]; then
      task "Updating embedded CA cert in $4"
      cp $3 $BACKUP_DIR/$(basename $3) 2>/dev/null || errorMessage "Unable to backup $3"
      cp $STAGE/update-embedded-cert.pem $3 2>/dev/null || errorMessage "Unable to copy new certificate to $3"
      statusMessage 'OK' 'GREEN'
      UPDATED_EMBEDDED=1
   fi
   
   rm $STAGE/update-embedded-*
}

#------------------------------
# List CA certificates in VECS
#------------------------------
function listVECSCACertificates() {
   VECS_CERTS=()
   VECS_CA_CERT_ALIASES=()   
   for alias in $($VECS_CLI entry list --store TRUSTED_ROOTS | grep '^Alias' | awk -F"[[:space:]]:[[:space:]]" '{print $NF}'); do
      CA_CERT=$($VECS_CLI entry getcert --store TRUSTED_ROOTS --alias $alias 2>/dev/null)
      VECS_CA_CERT_INFO=$(viewBriefCertificateInfo "$CA_CERT" "$alias")
      VECS_CERTS+=("$VECS_CA_CERT_INFO")
      VECS_CA_CERT_ALIASES+=($alias)
   done
   i=0
   while [ $i -lt "${#VECS_CERTS[@]}" ]; do
      n=$((i+1))
      printf "%2s. %s\n\n" $n "${VECS_CERTS[$i]}"
      ((++i))
   done
}

#------------------------------
# View VMDir CA certificate info
#------------------------------
function viewVMDirCACertificate() {
   skid=${VMDIR_CA_CERT_SKIDS[$(($1- 1))]}
   CERT=$($DIR_CLI trustedcert get --id $skid --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" --outcert /dev/stdout | grep -v 'Certificate retrieved successfully')
   if [ -n "$CERT" ]; then
      viewCertificateInfo "$CERT" 'view-path'
   else
      echo $'\n'"${YELLOW}Unable to view the CA certificate with Subject Key ID $skid.${NORMAL}"
   fi
}

#------------------------------
# View VECS CA certificate info
#------------------------------
function viewVECSCACertificate() {
   alias=${VECS_CA_CERT_ALIASES[$(($1 - 1))]}
   viewVECSCertificateInfo 'TRUSTED_ROOTS' "$alias"
}

#------------------------------
# Build certificate from hash
#------------------------------
function buildCertFromHash() {
   if [[ "$1" =~ ^MII ]]; then
      hash=$(echo "$1" | fold -c64)
   elif [[ "$1" =~ ^LS0 ]]; then
      echo "$hash" | base64 -d
      return 0
   else
      hash=$(echo $1 | base64 -d | tr -d '\r\n')
   fi
   
   TEMP_CERT=$'-----BEGIN CERTIFICATE-----\n'
   TEMP_CERT+=$hash
   TEMP_CERT+=$'\n-----END CERTIFICATE-----'
   echo "$TEMP_CERT"
}

#------------------------------
# View certificate info
#------------------------------
function viewCertificateInfo() {
   header 'Certificate Information'
   CERT_INFO=$(echo "$1" | openssl x509 -text -noout -fingerprint -sha1 2>/dev/null)
   logDetails "$CERT_INFO"
   echo "$CERT_INFO"
   if [ -n "$2" ] && [ "$2" == 'view-path' ]; then printCertificationPath "$1"; fi
}

#------------------------------
# Print certification path info
#------------------------------
function printCertificationPath() {
   header 'Certificaton Path'
   CERT_IDS=()
   VMDIR_CA_SKIDS=$($DIR_CLI trustedcert list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | tr -d '\t' | awk -F':' '{print $2}' 2>&1)
   for skid in $($DIR_CLI trustedcert list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | tr -d '\t' | awk -F':' '{print $2}' 2>&1); do
      logDebug "Exporting CA certificate with Subject Key ID $skid"
      $DIR_CLI trustedcert get --id $skid --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" --outcert $STAGE_DIR/vmdir-ca-$skid.crt 2>&1 | logDebug
   done
   
   CHAIN_COMPLETE=1
   
   CURRENT_SUBJECT=$(echo "$1" | openssl x509 -noout -text 2>/dev/null | grep 'Subject:' | sed -e 's/[[:space:]]*//' -e 's/Subject: //')
   CURRENT_ISSUER=$(echo "$1" | openssl x509 -noout -text 2>/dev/null | grep 'Issuer:' | sed -e 's/[[:space:]]*//' -e 's/Issuer: //')
   CURRENT_CERT_ID=$(certificationPathCertIdentifier "$CURRENT_SUBJECT")
   NEXT_CERT_ID=$(certificationPathCertIdentifier "$CURRENT_ISSUER")
   CURRENT_AKID=$(echo "$1" | openssl x509 -noout -text 2>/dev/null | grep -A1 'Authority Key Id' | tail -n1 | tr -d ' ' | sed 's/keyid://' | tr -d ':')
   CERT_IDS+=("$CURRENT_CERT_ID")
   if [ "$CURRENT_SUBJECT" != "$CURRENT_ISSUER" ]; then 
      CERT_IDS+=("$NEXT_CERT_ID")
      CHAIN_COMPLETE=0
   fi
   
   while true; do
      CURRENT_CA_CERT="$STAGE_DIR/vmdir-ca-$CURRENT_AKID.crt"
      if [ -f $CURRENT_CA_CERT ]; then	  
         CURRENT_SUBJECT=$(openssl x509 -noout -text -in $CURRENT_CA_CERT 2>/dev/null | grep 'Subject:' | sed -e 's/[[:space:]]*//' -e 's/Subject: //')
         CURRENT_ISSUER=$(openssl x509 -noout -text -in $CURRENT_CA_CERT 2>/dev/null | grep 'Issuer:' | sed -e 's/[[:space:]]*//' -e 's/Issuer: //')
         if [ "$CURRENT_SUBJECT" == "$CURRENT_ISSUER" ]; then
            CHAIN_COMPLETE=1
            break			
         else		    
            CURRENT_AKID=$(openssl x509 -noout -text -in $CURRENT_CA_CERT 2>/dev/null | grep -A1 'Authority Key Id' | tail -n1 | tr -d ' ' | sed 's/keyid://' | tr -d ':')
         fi
         NEXT_CERT_ID=$(certificationPathCertIdentifier "$CURRENT_ISSUER")
         CERT_IDS+=("$NEXT_CERT_ID")		 
      else
         break
      fi
   done
   i=$((${#CERT_IDS[@]} - 1))
   p=0
   CERTIFICATION_PATH=''
   while [ $i -ge 0 ]; do
      if [ $p -gt 0 ]; then
         b=$((p-1))
         s=$(( (p*2)+(b*2) ))
         for (( c=1; c<=$s; c++)); do CERTIFICATION_PATH+=' '; done
         CERTIFICATION_PATH+=$'|_'
      fi
      if [ $p -eq 0 ] && [ $CHAIN_COMPLETE -eq 0 ]; then
         CERTIFICATION_PATH+="[ ${RED}!${NORMAL} ] ${CERT_IDS[$i]}"$'\n'
      else
         CERTIFICATION_PATH+="[ ${GREEN}+${NORMAL} ] ${CERT_IDS[$i]}"$'\n'
      fi
      ((--i))
      ((++p))
   done
   echo "$CERTIFICATION_PATH"
   logDetails "$CERTIFICATION_PATH"
   /usr/bin/rm $STAGE_DIR/vmdir-ca-*.crt 2>&1 | logDebug
}

#------------------------------
# Print certification path info for missing CAs
#------------------------------
function printCertificationPathMissingCA() {
   logInfo 'Printing Certification Path of Missing CA certificates'
   CERT_IDS=()
   CERT_SUBJECTS=()
   CURRENT_SUBJECT=$(openssl x509 -noout -text -in "$1" 2>/dev/null | grep 'Subject:' | sed -e 's/[[:space:]]*//' -e 's/Subject: //')
   CURRENT_ISSUER=$(openssl x509 -noout -text -in "$1" 2>/dev/null | grep 'Issuer:' | sed -e 's/[[:space:]]*//' -e 's/Issuer: //')
   CURRENT_AKID=$(openssl x509 -noout -text -in "$1" 2>/dev/null | grep -A1 'Authority Key Id' | tail -n1 | tr -d ' ' | sed 's/keyid://' | tr -d ':')
   CURRENT_CERT_ID=$(certificationPathCertIdentifier "$CURRENT_SUBJECT")
   csplit -s -z -f $STAGE_DIR/validate-root-chain-tmp- -b %02d.crt $2 '/-----BEGIN CERTIFICATE-----/' '{*}' 2>&1 | logDebug
   for ca in $(ls $STAGE_DIR/validate-root-chain-tmp-*); do
      CURRENT_SKID=$(openssl x509 -noout -text -in $ca | grep -A1 'Subject Key Id' | tail -n1 | tr -d ': ')
      mv $ca $STAGE_DIR/validate-root-chain-ca-$CURRENT_SKID.crt
   done
   CERT_IDS+=("$CURRENT_CERT_ID")
   CERT_SUBJECTS+=("$CURRENT_SUBJECT")
   
   while true; do
      CURRENT_CA_CERT="$STAGE_DIR/validate-root-chain-ca-$CURRENT_AKID.crt"
      if [ -f "$CURRENT_CA_CERT" ]; then
         CURRENT_SUBJECT=$(openssl x509 -noout -text -in $CURRENT_CA_CERT 2>/dev/null | grep 'Subject:' | sed -e 's/[[:space:]]*//' -e 's/Subject: //')
         CURRENT_ISSUER=$(openssl x509 -noout -text -in $CURRENT_CA_CERT 2>/dev/null | grep 'Issuer:' | sed -e 's/[[:space:]]*//' -e 's/Issuer: //')
         CURRENT_AKID=$(openssl x509 -noout -text -in $CURRENT_CA_CERT 2>/dev/null | grep -A1 'Authority Key Id' | tail -n1 | tr -d ' ' | sed 's/keyid://' | tr -d ':')
         CURRENT_CERT_ID=$(certificationPathCertIdentifier "$CURRENT_SUBJECT")
         CERT_IDS+=("$CURRENT_CERT_ID")
         CERT_SUBJECTS+=("$CURRENT_SUBJECT")
      else
         CURRENT_CERT_ID=$(certificationPathCertIdentifier "$CURRENT_ISSUER")
         CERT_IDS+=("$CURRENT_CERT_ID")
         CERT_SUBJECTS+=("$CURRENT_ISSUER")		   
         break
      fi
   done
   
   i=$((${#CERT_IDS[@]} - 1))
   last_index=$i
   p=0
   CERTIFICATION_PATH=''
   while [ $i -ge 0 ]; do
      if [ $p -gt 0 ]; then
         b=$((p-1))
         s=$(( (p*2)+(b*2) ))
         for (( c=1; c<=$s; c++)); do CERTIFICATION_PATH+=' '; done
         CERTIFICATION_PATH+=$'|_'
      fi
      if [ $p -eq 0 ]; then
         CERTIFICATION_PATH+="[ ! ] ${CERT_IDS[$i]}"
      else
         CERTIFICATION_PATH+="[ + ] ${CERT_IDS[$i]}"
      fi	  
      ((--i))
      ((++p))
   done
   CERTIFICATION_PATH+=$'\nPlease ensure that the following certificate (and its issuers, if any) are included in the signing CA chain:'
   CERTIFICATION_PATH+="   Subject: ${CERT_SUBJECTS[$last_index]}"
   echo "$CERTIFICATION_PATH"
   logDetails "$CERTIFICATION_PATH"
   /usr/bin/rm $STAGE_DIR/validate-root-chain-ca-* 2>&1 | logDebug
}

#------------------------------
# Get Certificate Identifier from Subject string
#------------------------------
function certificationPathCertIdentifier() {
   CERT_CN=$(echo "$1" | sed 's/, /\n/g' | grep 'CN=' | awk -F'=' '{print $NF}')
   if [ -z "$CERT_CN" ]; then
      CERT_LAST_OU=$(echo "$1" | sed 's/, /\n/g' | grep 'OU=' | tail -n1 | awk -F'=' '{print $NF}')
	  if [ -z "$CERT_LAST_OU" ]; then
	     CERT_LAST_O=$(echo "$1" | sed 's/, /\n/g' | grep 'O=' | tail -n1 | awk -F'=' '{print $NF}')
		 if [ -z "$CERT_LAST_O" ]; then
		    echo "<unknown>"
		 else
		    echo "$CERT_LAST_O"
		 fi
	  else
	     echo "$CERT_LAST_OU"
	  fi
   else
      echo "$CERT_CN"
   fi
}

#------------------------------
# View brief certificate info
#------------------------------
function viewBriefCertificateInfo() {
   CERT_SUBJECT=$(echo "$1" | openssl x509 -noout -subject 2>/dev/null | sed 's/subject= //')
   CERT_ISSUER=$(echo "$1" | openssl x509 -noout -issuer 2>/dev/null | sed 's/issuer= //')
   CERT_ENDDATE=$(echo "$1" | openssl x509 -noout -enddate 2>/dev/null | awk -F'=' '{print $NF}')
   CERT_SKID=$(echo "$1" | openssl x509 -noout -text 2>/dev/null | grep -A1 'Subject Key' | tail -n1 | tr -d '[:space:]')
   
   if [ -z "$CERT_SKID" ] && [ -n "$3" ]; then
      CERT_SKID="$3 (computed)"
   fi
   if ! echo "$1" | openssl x509 -noout -subject -serial 2>/dev/null > /dev/null; then
      CERT_INFO="${YELLOW}Invalid format, certificate cannot be parsed!${NORMAL}"
      CERT_INFO+=$'\n'"    Subject Key ID: $CERT_SKID"
   else
      if isCertCA "$1"; then
         CERT_CA='Yes'
      else
         CERT_CA='No'
      fi
      if [ -n "$2" ]; then
         CERT_INFO="Alias: $2"
         CERT_INFO+=$'\n'"    Subject: $CERT_SUBJECT"
      else
         CERT_INFO="Subject: $CERT_SUBJECT"
      fi      
      CERT_INFO+=$'\n'"    Issuer: $CERT_ISSUER"
      CERT_INFO+=$'\n'"    End Date: $CERT_ENDDATE"
      CERT_INFO+=$'\n'"    Subject Key ID: $CERT_SKID"
      CERT_INFO+=$'\n'"    Is CA Cert: $CERT_CA"
   fi
   echo "$CERT_INFO"
}

#------------------------------
# View CRL info
#------------------------------
function viewCRLInfo() {
   echo "$1" | openssl crl -text -noout 2>&1 | logDebug
}

#------------------------------
# View vCenter Extension thumbprints
#------------------------------
function manageVCExtensionThumbprints() {
   header "$1 vCenter Extension Thumbprints"
   if [ "$NODE_TYPE" != 'infrastructure' ] && ! checkService 'vmware-vpostgres'; then
      echo "${YELLOW}The vPostgres service is stopped!"
      echo "Please ensure this service is running before updating vCenter extension thumbprints."
      echo "Hint: Check the number of CRL entries in VECS${NORMAL}"
      return 1 
   fi
   VC_EXT_THUMBPRINT_MISMATCH=0
   VPXD_EXT_THUMBPRINT=$($VECS_CLI entry getcert --store vpxd-extension --alias vpxd-extension 2>/dev/null | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}')
   MACHINE_SSL_THUMBPRINT=$($VECS_CLI entry getcert --store MACHINE_SSL_CERT --alias __MACHINE_CERT 2>/dev/null | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}')
   VMCAM_THUMBPRINT=$(openssl x509 -noout -fingerprint -sha1 -in /var/lib/vmware/vmcam/ssl/vmcamcert.pem 2>/dev/null | awk -F'=' '{print $NF}')
   VCENTER_EXTENSIONS="'com.vmware.vsan.health','com.vmware.vcIntegrity','com.vmware.rbd','com.vmware.imagebuilder','com.vmware.vmcam','com.vmware.vim.eam'"
   if [[ "$VC_VERSION" =~ ^8 ]]; then
      if [ $VC_BUILD -ge 22385739 ]; then
         VCENTER_EXTENSIONS="'com.vmware.vsan.health','com.vmware.vcIntegrity','com.vmware.imagebuilder','com.vmware.vmcam','com.vmware.vim.eam'"
      fi
      VCENTER_EXTENSIONS+=",'com.vmware.vlcm.client'"
   fi
   
   IFS=$'\n'

   for record in $($PSQL -d VCDB -U postgres -c "SELECT ext_id,thumbprint FROM vpx_ext WHERE ext_id IN ($VCENTER_EXTENSIONS) ORDER BY ext_id" -t); do
      extension=$(echo $record | awk -F'|' '{print $1}' | tr -d ' ')
      thumbprint=$(echo $record | awk -F'|' '{print $NF}' | tr -d ' ')
      
      case $extension in
         'com.vmware.vmcam')
            COMPARE_TO=$VMCAM_THUMBPRINT
            EXPECTED_CERT='Authentication Proxy'
         ;;
         
         'com.vmware.vsan.health')
            COMPARE_TO=$MACHINE_SSL_THUMBPRINT
            EXPECTED_CERT='Machine SSL'
         ;;
         
         *)
            COMPARE_TO=$VPXD_EXT_THUMBPRINT
            EXPECTED_CERT='vpxd-extension'
         ;;
      esac
      
      case $1 in
         'Checking'|'Check')
            task "$extension ($EXPECTED_CERT)"
            logInfo "Comparing $extension thumbprint of '$thumbprint' to '$COMPARE_TO'"
            if [ "$thumbprint" = "$COMPARE_TO" ]; then
               statusMessage 'MATCHES' 'GREEN'
            else
               VC_EXT_THUMBPRINT_MISMATCH=1
               statusMessage 'MISMATCH' 'YELLOW'
            fi            
         ;;
         
         'View')
            echo "$extension ($EXPECTED_CERT)"
            echo "   $thumbprint"
         ;;
         
         'Update')
            task "$extension ($EXPECTED_CERT)"
            logInfo "Comparing $extension thumbprint of '$thumbprint' to '$COMPARE_TO'"
            if [ "$thumbprint" = "$COMPARE_TO" ]; then
               statusMessage 'MATCHES' 'GREEN'
            else
               $PSQL -d VCDB -U postgres -c "UPDATE vpx_ext SET thumbprint = '$COMPARE_TO' WHERE ext_id = '$extension'" > /dev/null 2>&1 || errorMessage "Unable to update $extension extension thumbprint in VCDB"
               statusMessage 'UPDATED' 'GREEN'
            fi
         ;;
      
      esac
   done
   IFS=$' \t\n'
   if [ "$1" == 'Check' ] && [ "$VC_EXT_THUMBPRINT_MISMATCH" == '1' ]; then 
      unset UPDATE_THUMBPRINTS_INPUT
      echo $'\n'"${YELLOW}------------------------!!! Attention !!!------------------------"
      echo "Mismatched thumbprints detected.${NORMAL}"
      read -p $'\nUpdate extension thumbprints? [n]: ' UPDATE_THUMBPRINTS_INPUT
      
      if [ -z $UPDATE_THUMBPRINTS_INPUT ]; then UPDATE_THUMBPRINTS_INPUT='n'; fi
      
      if [[ "$UPDATE_THUMBPRINTS_INPUT" =~ ^[Yy] ]]; then manageVCExtensionThumbprints 'Update'; fi
   fi
}

#------------------------------
# View VMCA configuration options in VCDB
#------------------------------
function checkVMCADatabaseConfig() {
   VMCA_CONFIG_OUTPUT=''
   header 'Checking VMCA Configurations in VCDB'
   if [ "$NODE_TYPE" != 'infrastructure' ] && ! checkService 'vmware-vpostgres'; then
      task 'Connect to vPostgres database'
	   statusMessage 'ERROR' 'YELLOW'	  
   else
      VMCA_CONFIGS=$($PSQL -d VCDB -U postgres -c "SELECT name,value FROM vpx_parameter WHERE name='vpxd.certmgmt.mode' OR name LIKE 'vpxd.certmgmt.certs.cn.%'" -t)
	   logDetails "$VMCA_CONFIGS"
	   IFS=$'\n'
	   for line in $VMCA_CONFIGS; do
	      config=$(echo $line | awk -F'|' '{print $1}' | sed -e 's/^[[:space:]]*//' | sed -e 's/[[:space:]]*$//')
		   value=$(echo $line | awk -F'|' '{print $NF}' | sed -e 's/^[[:space:]]*//')
		   if [ -z $value ]; then
		      value="${YELLOW}EMPTY${NORMAL}"
			   CERT_STATUS_VMCA_EMPTY_CONFIG=1
		   else
		      if [ "$config" = 'vpxd.certmgmt.mode' ] && [ "$value" = 'thumbprint' ]; then
			      value="${YELLOW}'${value}'${NORMAL}"
			      CERT_STATUS_VMCA_MODE=1
			   else
		         value="${GREEN}'${value}'${NORMAL}"
			   fi
		   fi
         VMCA_CONFIG_OUTPUT+=$'\n'"$config: $value"
      done
      echo "$VMCA_CONFIG_OUTPUT" | column -t -s ':'
      IFS=$' \t\n'
   fi
}

#------------------------------
# Generate configuration file for certool utility
#------------------------------
function generateCertoolConfig() {
   if [[ " ${SOLUTION_USERS[*]} " =~ " $1 " ]]; then
      task "$1"
   else
      task 'Generate certool configuration'
   fi
   case $1 in
      'auth proxy')
         echo "Country = $CSR_COUNTRY" > $STAGE_DIR/auth-proxy.cfg
         echo "Organization = $CSR_ORG" >> $STAGE_DIR/auth-proxy.cfg
         echo "OrgUnit = $CSR_ORG_UNIT" >> $STAGE_DIR/auth-proxy.cfg
         echo "Name = $HOSTNAME" >> $STAGE_DIR/auth-proxy.cfg
         echo "Hostname = $HOSTNAME" >> $STAGE_DIR/auth-proxy.cfg
      ;;
      
      'vmdir')
         echo "Country = $CSR_COUNTRY" > $STAGE_DIR/vmdir.cfg
         echo "Name = $HOSTNAME" >> $STAGE_DIR/vmdir.cfg
         echo "Hostname = $HOSTNAME" >> $STAGE_DIR/vmdir.cfg
      ;;
      
      'sts')
         echo 'Name = ssoserverSign' > $STAGE_DIR/sso-sts.cfg
         echo "Hostname = $HOSTNAME" >> $STAGE_DIR/sso-sts.cfg         
      ;;
      
      *)
         echo "Country = $CSR_COUNTRY" > $STAGE_DIR/$1.cfg
         echo "Name = $2" >> $STAGE_DIR/$1.cfg
         echo "Organization = $CSR_ORG" >> $STAGE_DIR/$1.cfg
         echo "OrgUnit = $CSR_ORG_UNIT" >> $STAGE_DIR/$1.cfg
         echo "State = $CSR_STATE" >> $STAGE_DIR/$1.cfg
         echo "Locality = $CSR_LOCALITY" >> $STAGE_DIR/$1.cfg
   
         if [ "$2" == $IP ]; then
            echo "IPAddress = $2" >> $STAGE_DIR/$1.cfg
         elif [ -n "$CSR_IP" ]; then 
            echo "IPAddress = $CSR_IP" >> $STAGE_DIR/$1.cfg
         fi
   
         if [ -n "$CSR_EMAIL" ]; then echo "Email = $CSR_EMAIL" >> $STAGE_DIR/$1.cfg; fi
   
         printf "Hostname = $HOSTNAME" >> $STAGE_DIR/$1.cfg
   
         if [ "$HOSTNAME_LC" != "$PNID_LC" ] && [ "$IP" != "$PNID" ]; then
            printf ",$PNID" >> $STAGE_DIR/$1.cfg
         fi
   
         if [ -n "$CSR_ADDITIONAL_DNS" ]; then
		    CSR_ADDITIONAL_DNS=$(echo "$CSR_ADDITIONAL_DNS" | tr -d ' ')
            printf ",$CSR_ADDITIONAL_DNS" >> $STAGE_DIR/$1.cfg
         fi
      ;;
   esac
   statusMessage 'OK' 'GREEN'
}

#------------------------------
# Generate a VMCA-signed certificate
#------------------------------
function regenerateVMCASignedCertificate() {
   $CERTOOL --genkey --privkey=$STAGE_DIR/$1.key --pubkey=$STAGE_DIR/$1.pub --server=$PSC_LOCATION > /dev/null 2>&1 || errorMessage "Unable to genereate new keys for $1"
   $CERTOOL --gencert --privkey=$STAGE_DIR/$1.key --cert=$STAGE_DIR/$1.crt --config=$STAGE_DIR/$1.cfg --server=$PSC_LOCATION > /dev/null 2>&1 || errorMessage "Unable to generate self-signed certificate for $1"
}

#------------------------------
# Generate a certificate Signing Request
#------------------------------
function generateCSR() {
   openssl req -new -newkey rsa:2048 -nodes -out $1 -keyout $2 -config $3 > /dev/null 2>&1 || errorMessage "Unable to generate Certificate Signing Request and Private Key"
   return 0
}

#------------------------------
# Reset Certificate Signing Request fields
#------------------------------
function clearCSRInfo() {
   logInfo 'Clearing CSR fields'
   CSR_COUNTRY=''
   CSR_ORG=''
   CSR_ORG_UNIT=''
   CSR_STATE=''
   CSR_LOCALITY=''
   CSR_IP=''
   CSR_EMAIL=''
   CSR_ADDITIONAL_DNS=''
}

#------------------------------
# Collect information for a Certificate Signing Request
#------------------------------
function getCSRInfo() {
   unset CSR_COUNTRY_INPUT
   unset CSR_ORG_INPUT
   unset CSR_ORG_UNIT_INPUT
   unset CSR_STATE_INPUT
   unset CSR_LOCALITY_INPUT
   unset CSR_IP_INPUT
   unset CSR_EMAIL_INPUT
   unset CSR_SAN_INPUT
   if [ -z "$1" ]; then
      header 'Certificate Signing Request Information'
   else
      header "Certificate Signing Request Information [$1]"
   fi
   read -p "Enter the country code [$CSR_COUNTRY_DEFAULT]: " CSR_COUNTRY_INPUT
   
   if [ -z "$CSR_COUNTRY_INPUT" ]; then 
      CSR_COUNTRY=$CSR_COUNTRY_DEFAULT
   else
      while [ "${#CSR_COUNTRY_INPUT}" != 2 ]; do read -p "Please enter the two-character country code [$CSR_COUNTRY_DEFAULT]: " CSR_COUNTRY_INPUT; done
      CSR_COUNTRY=$CSR_COUNTRY_INPUT
	   CSR_COUNTRY_DEFAULT=$CSR_COUNTRY_INPUT
   fi

   if [ -n "$1" ] && [ "$1" == 'VMCA' ]; then
      CSR_ORG_DEFAULT_OPTION="$VMDIR_FQDN"
   else
      CSR_ORG_DEFAULT_OPTION="$CSR_ORG_DEFAULT"
   fi

   read -p "Enter the Organization name [$CSR_ORG_DEFAULT_OPTION]: " CSR_ORG_INPUT
   
   if [ -z "$CSR_ORG_INPUT" ]; then
      CSR_ORG="$CSR_ORG_DEFAULT_OPTION"
   else
      CSR_ORG="$CSR_ORG_INPUT"
	   CSR_ORG_DEFAULT="$CSR_ORG_INPUT"
   fi

   read -p "Enter the Organizational Unit name [$CSR_ORG_UNIT_DEFAULT]: " CSR_ORG_UNIT_INPUT
   
   if [ -z "$CSR_ORG_UNIT_INPUT" ]; then
      CSR_ORG_UNIT="$CSR_ORG_UNIT_DEFAULT"
   else
      CSR_ORG_UNIT="$CSR_ORG_UNIT_INPUT"
	   CSR_ORG_UNIT_DEFAULT="$CSR_ORG_UNIT_INPUT"
   fi

   read -p "Enter the state [$CSR_STATE_DEFAULT]: " CSR_STATE_INPUT
   
   if [ -z "$CSR_STATE_INPUT" ]; then
      CSR_STATE="$CSR_STATE_DEFAULT"
   else
      CSR_STATE="$CSR_STATE_INPUT"
	   CSR_STATE_DEFAULT="$CSR_STATE_INPUT"
   fi

   read -p "Enter the locality (city) name [$CSR_LOCALITY_DEFAULT]: " CSR_LOCALITY_INPUT
   
   if [ -z "$CSR_LOCALITY_INPUT" ]; then
      CSR_LOCALITY="$CSR_LOCALITY_DEFAULT"
   else
      CSR_LOCALITY="$CSR_LOCALITY_INPUT"
	   CSR_LOCALITY_DEFAULT="$CSR_LOCALITY_INPUT"
   fi

   read -p 'Enter the IP address (optional): ' CSR_IP_INPUT

   if [ -n "$CSR_IP_INPUT" ]; then 
      while ! validateIp $CSR_IP_INPUT; do echo "${YELLOW}Invalid IP address, enter valid IP address: " CSR_IP_INPUT; done
      CSR_IP=$CSR_IP_INPUT
   fi

   read -p 'Enter an email address (optional): ' CSR_EMAIL_INPUT
   
   if [ -n "$CSR_EMAIL_INPUT" ]; then CSR_EMAIL=$CSR_EMAIL_INPUT; fi
   
   if [ -n "$1" ]; then
      read -p 'Enter any additional hostnames for SAN entries (comma separated value): ' CSR_SAN_INPUT
   
      if [ -n "$CSR_SAN_INPUT" ]; then CSR_ADDITIONAL_DNS=$CSR_SAN_INPUT; fi      
   fi
}

#------------------------------
# Define the SAN entries for creating an openssl configuration file
#------------------------------
function defineSANEntries() {
   unset ADDITIONAL_SAN_ITEMS
   unset INCLUDE_SHORTNAME_INPUT
   if [ "$1" == 'ESXi' ]; then
      DEFAULT_SANS=("$3")
      if ! echo "$3" | awk -F '.' '{print $1}' | grep '^[0-9]' > /dev/null; then
         ESXi_SHORT=$(echo "$3" | awk -F '.' '{print $1}')
         read -p "Include host short name (${CYAN}${ESXi_SHORT}${NORMAL}) as a Subject Alternative Name entry? [n]: " INCLUDE_SHORTNAME_INPUT
         SHORTNAME_TO_INCLUDE=$ESXi_SHORT
      fi
   else
      DEFAULT_SANS=("$HOSTNAME")
      read -p "Include host short name (${CYAN}${HOSTNAME_SHORT}${NORMAL}) as a Subject Alternative Name entry? [n]: " INCLUDE_SHORTNAME_INPUT
      SHORTNAME_TO_INCLUDE=$HOSTNAME_SHORT
   fi

   if [[ "$INCLUDE_SHORTNAME_INPUT" =~ ^[Yy] ]]; then
      DEFAULT_SANS+=("$SHORTNAME_TO_INCLUDE")
   fi
   if [ "$HOSTNAME_LC" != "$PNID_LC" ]; then
      DEFAULT_SANS+=("$PNID")
   fi

   echo $'\n'"The following items will be added as Subject Alternative Name entries on the '$2' Certificate Signing Request:"
   echo "$CYAN"
   
   for san in "${DEFAULT_SANS[@]}"; do
      echo "$san"
   done

   if [ -n "$CSR_IP" ]; then echo "$CSR_IP"; fi   
   if [ -n "$CSR_EMAIL" ]; then echo "$CSR_EMAIL"; fi
   echo -n "$NORMAL"
   if [ "$1" == "machine-ssl" ] && [ $NODE_TYPE = 'infrastructure' ] && [ -n "$PSC_LB" ]; then
      cat << EOF
${YELLOW}-------------------------!!! WARNING !!!-------------------------"
  This PSC is detected to be in an HA configuration.
  Make sure to add the hostnames of the additional PSCs
  as Subject Alternative Name entries.	  
EOF
      echo $'\n'
   fi

   read -p $'\nIf you want any additional items added as Subject Alternative Name entries, enter them as a comma-separated list (optional): ' ADDITIONAL_SAN_ITEMS
}

#------------------------------
# Generate a configuration file to be used with the openssl commands
#------------------------------
function generateOpensslConfig() {
   echo '[ req ]' > $2
   echo 'prompt = no' >> $2
   echo 'default_bits = 2048' >> $2
   echo 'distinguished_name = req_distinguished_name' >> $2
   echo 'req_extensions = v3_req' >> $2
   echo '' >> $2
   echo '[ req_distinguished_name ]' >> $2
   echo "C = $CSR_COUNTRY" >> $2
   echo "ST = $CSR_STATE" >> $2
   echo "L = $CSR_LOCALITY" >> $2
   echo "O = $CSR_ORG" >> $2
   echo "OU = $CSR_ORG_UNIT" >> $2
   echo "CN = $1" >> $2
   echo '' >> $2
   echo '[ v3_req ]' >> $2
   printf 'subjectAltName = ' >> $2
   
   echo -n "DNS:${DEFAULT_SANS[@]}" | sed 's/ /, DNS:/g' >> $2
   
   for item in $(echo "$ADDITIONAL_SAN_ITEMS" | tr -d ' ' | sed -e 's/,/\n/g'); do
      if [[ $item =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
         printf ", IP:$item" >> $2
      else
         printf ", DNS:$item" >> $2
      fi      
   done

   if [ -n "$CSR_IP" ]; then printf ", IP:$CSR_IP" >> $2; fi

   if [ -n "$CSR_EMAIL" ]; then printf ", email:$CSR_EMAIL" >> $2; fi
}

#------------------------------
# Check if certificate is in DER (binary) format
#------------------------------
function isCertKeyDER() {
   if [ $(file $1 | awk -F':' '{print $NF}' | tr -d ' ') == 'data' ]; then
      logInfo "Provided certificate/keystore $1 is a binary file"
      return 0
   else
      logInfo "Provided certificate/keystore $1 is NOT a binary file"
      return 1
   fi
}

#------------------------------
# Check if certificate is in the correct format (PEM Base64), and convert if necessary
#------------------------------
function getCorrectCertFormat() {
   if isCertKeyDER $1; then
      if openssl x509 -noout -text -inform der -in $1 > /dev/null 2>&1; then
         openssl x509 -inform der -in $1 -outform pem -out $1-converted.pem > /dev/null 2>&1
         logInfo "Converting DER certificate to PEM format: $1-converted.pem"
         declare -g "$2"="$1-converted.pem"
         return 0
      fi
  
      if openssl pkcs7 -print_certs -inform der -in $1 > /dev/null 2>&1; then 
         openssl pkcs7 -print_certs -inform der -in $1 2>/dev/null | grep -vE '^subject|^issuer|^$' > $1-converted.pem
         logInfo "Converting DER PKCS#7 certificate to PEM mulit-cert format: $1-converted.pem"
         declare -g "$2"="$1-converted.pem"
         return 0
      fi

      if openssl pkcs12 -nokeys -info -in $1 -passin pass: 2>&1 | grep 'BEGIN CERTIFICATE' > /dev/null; then
         openssl pkcs12 -nokeys -info -in $1 -passin pass: 2>/dev/null | sed -n '/^-----BEGIN CERTIFICATE-----/,/^-----END CERTIFICATE-----/p' > $1-converted.pem
         openssl pkcs12 -nocerts -nodes -info -in $1 -passin pass: 2>/dev/null | sed -n '/^-----BEGIN PRIVATE KEY-----/,/^-----END PRIVATE KEY-----/p' > $REQUEST_DIR/pkcs12-extracted-key.key
         if [ -s $-converted.pem ]; then logInfo "Extracting certificates from PKCS#12 keystore to $1-converted.pem"; fi
         if [ -s $REQUEST_DIR/pkcs12-extracted-key.key ]; then logInfo "Extracting private key from PKCS#12 keystore to $REQUEST_DIR/pkcs12-extracted-key.key"; fi
         declare -g "$2"="$1-converted.pem"
         return 0
      elif openssl pkcs12 -nokeys -info -in $1 -passin pass:Antidisestablishmentarianism123581321 2>&1 | grep 'invalid password' > /dev/null 2>&1; then
         read -r -s -p $'\n'"Enter password for PKCS#12 keystore: " PKCS12_KEYSTORE_PASSWORD
         PKCS12_KEYSTORE_PASSWORD_VALIDATED=0
         i=0
         while [ $i -lt 3 ]; do
            if [ -z "$PKCS12_KEYSTORE_PASSWORD" ]; then
               read -r -s -p $'\n'"${YELLOW}Enter password for PKCS#12 keystore:${NORMAL} " PKCS12_KEYSTORE_PASSWORD
            elif openssl pkcs12 -nokeys -info -in $1 -passin pass:$PKCS12_KEYSTORE_PASSWORD 2>&1 | grep 'invalid password' > /dev/null 2>&1; then
               read -r -s -p $'\n'"${YELLOW}Invalid password, enter password for PKCS#12 keystore:${NORMAL} " PKCS12_KEYSTORE_PASSWORD
            else
               PKCS12_KEYSTORE_PASSWORD_VALIDATED=1
               break
            fi
            ((++i))
         done
         
         if [ $PKCS12_KEYSTORE_PASSWORD_VALIDATED -eq 0 ]; then errorMessage 'Unable to validate keystore password' '-' '-'; fi

         openssl pkcs12 -nokeys -info -in $1 -passin pass:$PKCS12_KEYSTORE_PASSWORD 2>/dev/null | sed -n '/^-----BEGIN CERTIFICATE-----/,/^-----END CERTIFICATE-----/p' > $1-converted.pem
         openssl pkcs12 -nocerts -nodes -info -in $1 -passin pass:$PKCS12_KEYSTORE_PASSWORD 2>/dev/null | sed -n '/^-----BEGIN PRIVATE KEY-----/,/^-----END PRIVATE KEY-----/p' > $REQUEST_DIR/pkcs12-extracted-key.key
         if [ -s $-converted.pem ]; then logInfo "Extracting certificates from PKCS#12 keystore to $1-converted.pem"; fi
         if [ -s $REQUEST_DIR/pkcs12-extracted-key.key ]; then logInfo "Extracting private key from PKCS#12 keystore to $REQUEST_DIR/pkcs12-extracted-key.key"; fi
         declare -g "$2"="$1-converted.pem"
         echo ''
         return 0
      fi
   else
      if openssl x509 -noout -text -in $1 > /dev/null 2>&1; then
         logInfo "No conversion necessary for $1"
         declare -g "$2"="$1"
         return 0
      fi
      
      if openssl pkcs7 -print_certs -in $1 > /dev/null 2>&1; then 
         openssl pkcs7 -print_certs -in $1 2>/dev/null | grep -vE '^subject|^issuer|^$' > $1-converted.pem
         logInfo "Converting PKCS#7 certificate to PEM multi-cert format: $1-converted.pem"
         declare -g "$2"="$1-converted.pem"
         return 0
      fi 
   fi
   logInfo "Unknown certificate format for $1"
   declare -g "$2"="Unknown format"
   return 0
}

#------------------------------
# Check if private key is in the correct format (PEM Base64), and convert if necessary
#------------------------------
function getCorrectKeyFormat() {
   unset key_path
   unset PKCS12_KEYSTORE_PASSWORD
   if isCertKeyDER $1; then
      logInfo "Converting private key from DER to PEM format: $1-converted.key"
      if openssl rsa -inform der -in $1 > $1-converted.key > /dev/null 2>&1; then
         key_path="$1-converted.key"
      elif openssl pkcs12 -nokeys -info -in $1 -passin pass:VMware123! 2>&1 | grep 'invalid password' > /dev/null 2>&1; then
         read -r -s -p $'\n'"Enter password for PKCS#12 keystore: " PKCS12_KEYSTORE_PASSWORD
         while [ -z "$PKCS12_KEYSTORE_PASSWORD" ]; do read -r -s -p $'\n'"${YELLOW}Enter password for PKCS#12 keystore:${NORMAL} " PKCS12_KEYSTORE_PASSWORD; done
         openssl pkcs12 -nocerts -nodes -info -in $1 -passin pass:$PKCS12_KEYSTORE_PASSWORD 2>/dev/null | sed -n '/^----------BEGIN PRIVATE KEY----------/,/^-----END PRIVATE KEY-----/p' > $REQUEST_DIR/pkcs12-extracted-key.key
         if [ -s $REQUEST_DIR/pkcs12-extracted-key.key ]; then 
            key_path="$REQUEST_DIR/pkcs12-extracted-key.key"
         else
            errorMessage "Unable to export private key from PKCS#12 keystore" '-' '-'
         fi
      else
         errorMessage "Unable to convert private key $1 to PEM format" '-' '-'
      fi
   else
      if grep 'ENCRYPTED' $1 > /dev/null 2>&1; then
         i=1
         read -s -p $'\n'"Private key is encrypted, enter pass phrase to decrypt: " PRIVATE_KEY_PASSPHRASE
         while [ -z "$PRIVATE_KEY_PASSPHRASE" ]; do
            if [ $i -gt 3 ]; then errorMessage "No pass phrase provided" '-' '-'; fi
            read -s -p $'\n'"${YELLOW}Private key is encrypted, enter pass phrase to decrypt:${NORMAL} " PRIVATE_KEY_PASSPHRASE
            ((++i))
         done
         logInfo "Decrypting private key: $1"
         if openssl rsa -in $1 -out $1-decrypted.key -passin pass:$PRIVATE_KEY_PASSPHRASE > /dev/null 2>&1; then
            key_path="$1-decrypted.key"
            echo ''
         else
            openssl rsa -in $1 -out $1-decrypted.key -passin pass:$PRIVATE_KEY_PASSPHRASE > $STAGE_DIR/private-key-decrypt-failure.stdout 2>&1
            if grep 'disabled for fips' $STAGE_DIR/private-key-decrypt-failure.stdout > /dev/null; then
               errorMessage "Private key encrypted with cipher not supported by FIPS, cannot decrypt key" '-' '-'
            else
               errorMessage "Unable to decrypt private key $1" '-' '-'
            fi
         fi
      elif grep 'BEGIN RSA PRIVATE KEY' $1 > /dev/null 2>&1; then
         logInfo "Converting private key $1 from PKCS#1 to PKCS#8"
         if openssl rsa -in $1 -out $1-converted.key > /dev/null 2>&1; then
            key_path="$1-converted.key"
         else
            errorMessage "Unable to convert private key $1 from PKCS#1 to PKCS#8" '-' '-'
         fi
      else
         logInfo "No conversion necessary for $1"
         key_path="$1"
      fi
   fi
   declare -g CURRENT_PRIVATE_KEY_PATH="$key_path"
}

#------------------------------
# Check if certificate contains complete CA signing chain
#------------------------------
function checkEmbeddedCAChain() {
   if [ "$(grep 'BEGIN CERTIFICATE' $1 | wc -l)" -gt 1 ]; then
      CHAIN_START=$(grep -n -m2 'BEGIN CERTIFICATE' $1 | tail -n1 | cut -d':' -f1)
      tail -n+$CHAIN_START $1 > $STAGE_DIR/embedded-root-chain.pem
      
      echo "$STAGE_DIR/embedded-root-chain.pem"
   fi
}

#------------------------------
# Get CA chain from certificate file, or by prompt
#------------------------------
function getCAChain() {
   TRUSTED_ROOT_CHAIN=$(checkEmbeddedCAChain "$1")
         
   if [ -z "$TRUSTED_ROOT_CHAIN" ]; then 
      read -e -p 'Provide path to the Certificate Authority chain: ' TRUSTED_ROOT_CHAIN_INPUT
      while [ ! -f "$TRUSTED_ROOT_CHAIN_INPUT" ]; do read -e -p $'\n'"${YELLOW}File not found, please provide path to the CA-signed Certificate Authority chain:${NORMAL} " TRUSTED_ROOT_CHAIN_INPUT; done
      getCorrectCertFormat "$TRUSTED_ROOT_CHAIN_INPUT" 'TRUSTED_ROOT_CHAIN'
      while true; do
         if ! verifyRootChain "$1" "$TRUSTED_ROOT_CHAIN"; then
            MISSING_CA_OUTPUT=$(printCertificationPathMissingCA "$1" "$TRUSTED_ROOT_CHAIN")
            cat << EOF
$YELLOW
The provided certificate signing chain is not complete!		

$MISSING_CA_OUTPUT
$NORMAL 
EOF
            read -e -p 'Provide path to the Certificate Authority chain: ' TRUSTED_ROOT_CHAIN_INPUT
            while [ ! -f "$TRUSTED_ROOT_CHAIN_INPUT" ]; do read -e -p $'\n'"${YELLOW}File not found, please provide path to the CA-signed Certificate Authority chain:${NORMAL} " TRUSTED_ROOT_CHAIN_INPUT; done
            getCorrectCertFormat "$TRUSTED_ROOT_CHAIN_INPUT" 'TRUSTED_ROOT_CHAIN'
         else
            break
         fi
      done
   fi
}

#------------------------------
# Get location of private key for CA-signed certs
#------------------------------
function getPrivateKey() {
   logInfo "Looking for private key for $2"
   dynamic_key="$2_KEY"
   KEY_FOUND=0
   
   if [ -s $REQUEST_DIR/pkcs12-extracted-key.key ]; then
      CURRENT_KEY_MODULUS_HASH=$(openssl rsa -noout -modulus -in $REQUEST_DIR/pkcs12-extracted-key.key 2>logInfo | md5sum | awk '{print $1}')
      logInfo "Checking modulus of $REQUEST_DIR/pkcs12-extracted-key.key ($CURRENT_KEY_MODULUS_HASH) against '$1'"
      if [ "$1" == "$CURRENT_KEY_MODULUS_HASH" ]; then 
         KEY_FOUND=1;
         getCorrectKeyFormat "$REQUEST_DIR/pkcs12-extracted-key.key"
         declare -g "$dynamic_key"="$CURRENT_PRIVATE_KEY_PATH"
         logInfo "Found private key at $CURRENT_PRIVATE_KEY_PATH"
      fi
   fi

   if [ $KEY_FOUND -eq 0 ]; then
      for key in $(find $TOP_DIR -wholename '*/requests/*.key'); do      
         CURRENT_KEY_MODULUS_HASH=$(openssl rsa -noout -modulus -in $key 2>logInfo | md5sum | awk '{print $1}')
         logInfo "Checking modulus of $key ($CURRENT_KEY_MODULUS_HASH) against '$1'"
         if [ "$1" == "$CURRENT_KEY_MODULUS_HASH" ]; then 
            KEY_FOUND=1;
            getCorrectKeyFormat "$key"
            declare -g "$dynamic_key"="$CURRENT_PRIVATE_KEY_PATH"
            logInfo "Found private key at $CURRENT_PRIVATE_KEY_PATH"
         fi
      done
   fi
   
   if [ $KEY_FOUND -eq 0 ]; then
      if $VECS_CLI entry list --store MACHINE_SSL_CERT | grep '__MACHINE_CSR' > /dev/null; then
         CURRENT_KEY_MODULUS_HASH=$($VECS_CLI entry getkey --store MACHINE_SSL_CERT --alias __MACHINE_CSR | openssl rsa -noout -modulus 2>/dev/null | md5sum | awk '{print $1}')
         logInfo "Checking modulus of key in __MACHINE_CSR alias ($CURRENT_KEY_MODULUS_HASH) against '$1'"
         if [ "$1" == "$CURRENT_KEY_MODULUS_HASH" ]; then
            KEY_FOUND=1;
            $VECS_CLI entry getkey --store MACHINE_SSL_CERT --alias __MACHINE_CSR > $STAGE_DIR/vmca_issued_key.key
            getCorrectKeyFormat "$STAGE_DIR/vmca_issued_key.key"
            declare -g "$dynamic_key"="$CURRENT_PRIVATE_KEY_PATH"
            logInfo 'Found private key in the __MACHINE_CSR entry in VECS'
	     fi
      fi
   fi
		 
   if [ $KEY_FOUND -eq 0 ]; then
      unset CURRENT_PRIVATE_KEY_PATH
      read -e -p "Provide path to the ${CYAN}$3${NORMAL} private key: " PRIVATE_KEY_INPUT
      while [ ! -f "$PRIVATE_KEY_INPUT" ]; do read -e -p $'\n'"${YELLOW}File not found, please provide path to the $3 private key:${NORMAL} " PRIVATE_KEY_INPUT; done
      getCorrectKeyFormat "$PRIVATE_KEY_INPUT"
      declare -g "$dynamic_key"="$CURRENT_PRIVATE_KEY_PATH"
      logInfo "Customer provided key: $CURRENT_PRIVATE_KEY_PATH"
   fi
}

#------------------------------
# Verify a certificate, private key
#------------------------------
function verifyCertAndKey() {
   if [ ! -f $1 ]; then errorMessage "Could not locate certificate $1"; fi
   if [ ! -f $2 ]; then errorMessage "Could not locate private key $2"; fi
   
   CERT_HASH=$(openssl x509 -noout -modulus -in $1 2>/dev/null | md5sum)
   KEY_HASH=$(openssl rsa -noout -modulus -in $2 2>/dev/null| md5sum)
   
   logInfo "Modulus of $1: $CERT_HASH"
   logInfo "Modulus of $2: $KEY_HASH"
   if [ "$CERT_HASH" != "$KEY_HASH" ]; then errorMessage "The private key $2 does not correspond to the certificate $1"; fi
}

#------------------------------
# Verifies root chain by subject/issuer strings
#------------------------------
function verifyRootChain() {
   unset EXPIRED_CA_CERTS
   rm $STAGE_DIR/root-chain-cert-*.crt 2>/dev/null
   csplit -s -z -f $STAGE_DIR/root-chain-cert- -b %02d.crt $2 '/-----BEGIN CERTIFICATE-----/' '{*}' 2>&1 | logDebug
   
   logInfo "Contents of trusted root chain $2"
   openssl crl2pkcs7 -nocrl -certfile $2 2>/dev/null | openssl pkcs7 -print_certs -noout 2>&1 | logInfo
   
   ISSUER_TO_CHECK=$(openssl x509 -noout -issuer -in $1 2>/dev/null | sed -e 's/issuer= //')
   FOUND_ROOT=0
   NUM_CA_CERTS=$(ls $STAGE_DIR/root-chain-cert-* | wc -l)
   i=0
   
   while [ $i -lt $NUM_CA_CERTS ]; do
      logInfo "Looking for issuing CA '$ISSUER_TO_CHECK'"
      for cert in $(ls $STAGE_DIR/root-chain-cert-*); do
         CURRENT_SUBJECT=$(openssl x509 -noout -subject -in $cert 2>/dev/null | sed -e 's/subject= //')
         if [ "$ISSUER_TO_CHECK" == "$CURRENT_SUBJECT" ]; then
            logInfo "Found issuing CA '$ISSUER_TO_CHECK' in $cert"
            if isExpired "$cert" 'file'; then
               logInfo "CA Certificate $CURRENT_SUBJECT is expired!"
               EXPIRED_CA_CERTS+=$'\n\t'"$CURRENT_SUBJECT"
            fi         
            ISSUER_TO_CHECK=$(openssl x509 -noout -issuer -in $cert 2>/dev/null | sed -e 's/issuer= //')
            if [ "$ISSUER_TO_CHECK" == "$CURRENT_SUBJECT" ]; then FOUND_ROOT=1; fi
            break
         fi         
      done
      ((++i))
   done
   if [ -n "$EXPIRED_CA_CERTS" ]; then
      ERROR_MESSAGE='The following provided CA certificates are expired:'
      ERROR_MESSAGE+=$'\n'$(echo "$EXPIRED_CA_CERTS" | sort | uniq | grep -vE '^$')
      ERROR_MESSAGE+=$'\n\nInstallation of the certificates cannot continue'
      errorMessage "$ERROR_MESSAGE" 2
   fi
   if [ $FOUND_ROOT == 0 ]; then
      return 1
   else
      return 0   
   fi
}

#------------------------------
# Verifies CA certificates in signing chain are present in VMware Directory
#------------------------------
function checkCACertsPresent() {
   CHAIN_COMPLETE=0
   CHECK=1
   CURRENT_CERT=$1
   while [ $CHECK -gt 0 ]; do
      CURRENT_SUBJECT=$(echo "$CURRENT_CERT" | openssl x509 -noout -subject 2>/dev/null | sed -e 's/subject= //')
      CURRENT_ISSUER=$(echo "$CURRENT_CERT" | openssl x509 -noout -issuer 2>/dev/null | sed -e 's/issuer= //')   
      if [ "$CURRENT_SUBJECT" != "$CURRENT_ISSUER" ]; then
	     CURRENT_AUTH_KEY_ID=$(echo "$CURRENT_CERT" | openssl x509 -noout -text 2>/dev/null | grep -A1 'Authority Key Id' | grep 'keyid' | sed 's/keyid://' | tr -d ': ')
		 if $DIR_CLI trustedcert list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>/dev/null | grep 'CN(id)' | grep "$CURRENT_AUTH_KEY_ID" > /dev/null; then
		    CURRENT_CERT=$($DIR_CLI trustedcert get --id $CURRENT_AUTH_KEY_ID --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)"  --outcert /dev/stdout 2>/dev/null)
	     else
		    CHECK=0
		 fi
	  else
	     CHAIN_COMPLETE=1
         CHECK=0
	  fi
   done
   
   if [ $CHAIN_COMPLETE == 1 ]; then
      return 0
   else
      return 1
   fi
}

#------------------------------
# Check for expired CA certificates in certificate chain
#------------------------------
function checkEmbeddedChain() {
   NUM_CERTS=$(echo "$1" | grep 'BEGIN CERTIFICATE' | wc -l)
   if [ $NUM_CERTS -gt 1 ]; then
      echo "$1" > $STAGE_DIR/embedded-chain-check-cert.pem
      csplit -z -s -f $STAGE_DIR/embedded-chain-check- -b %02d.crt $STAGE_DIR/embedded-chain-check-cert.pem '/-----BEGIN CERTIFICATE-----/' '{*}'
	  
      for cert in $(ls $STAGE_DIR/embedded-chain-check-*.crt); do
         logInfo "Running check on embedded CA cert against $cert"
         if isExpired "$cert" 'file'; then
            logInfo "The CA cert $cert is expired"
            rm $STAGE_DIR/embedded-chain-check-* 2>&1 | logDebug
	        return 1
         fi
      done
      rm $STAGE_DIR/embedded-chain-check-* 2>&1 | logDebug
      return 0
   else
      return 0
   fi
}

#------------------------------
# Prompt options for replacing VMCA certificate
#------------------------------
function promptReplaceVMCA() {
   VMCA_REPLACE='SELF-SIGNED'
   unset VMCA_REPLACE_INPUT
   VMCA_REGENERATE_CERTIFICATES=1
   
   header 'Select VMCA Certificate Replacement Method'
   echo '1. Replace VMCA certificate with a self-signed certificate'
   echo '2. Replace VMCA certificate with a self-signed certificate'
   echo '   and regenerate all certificates'
   echo '3. Replace VMCA certificate with a CA-signed certificate'
   read -p $'\nSelect an option [Return to Main Menu]: ' VMCA_REPLACE_INPUT
   
   if [ -z "$VMCA_REPLACE_INPUT" ]; then return 1; fi

   if [ "$VMCA_REPLACE_INPUT" == '1' ]; then VMCA_REGENERATE_CERTIFICATES=0; fi
   if [ "$VMCA_REPLACE_INPUT" == '3' ]; then VMCA_REPLACE='CA-SIGNED'; fi

   logInfo "User selected to replace VMCA certificate with a $VMCA_REPLACE certificate"
   if [ "$VMCA_REGENERATE_CERTIFICATES" == '1' ]; then
      logInfo 'Certificates will be regenerated and signed by the VMCA'
   else
      logInfo 'Certificates will NOT be regenerated'
   fi
   return 0
}

#------------------------------
# Replace all certificates with VMCA-signed certs
#------------------------------
function resetAllCertificates() {
   STS_REPLACE='VMCA-SIGNED'
   MACHINE_SSL_REPLACE='VMCA-SIGNED'
   SOLUTION_USER_REPLACE='VMCA-SIGNED'
   AUTH_PROXY_REPLACE='VMCA-SIGNED'
   AUTO_DEPLOY_CA_REPLACE='SELF-SIGNED'
   
   authenticateIfNeeded
   getCSRInfo
   case $NODE_TYPE in
      embedded|infrastructure)
         replaceMachineSSLCert
         replaceSolutionUserCerts
         if [ $NODE_TYPE = 'embedded' ]; then
            replaceDataEnciphermentCertificate 'replace-only'
            replaceAuthProxyCert
            replaceAutoDeployCACert
            if [[ "$VC_VERSION" =~ ^8 ]]; then replaceSMSCertificate 'VMCA-signed' 'sps-extesion' 'no-restart'; fi
            manageVCExtensionThumbprints 'Update'
            updateAutoDeployDB
         fi
		   if [[ "$VC_VERSION" =~ ^6 ]]; then replaceVMDirCert; fi
         replaceSSOSTSCert
         SSLTrustAnchorSelf
         updateSSLTrustAnchors
         clearCSRInfo         
         promptRestartVMwareServices
         ;;

      management)
         replaceMachineSSLCert
         replaceSolutionUserCerts
         replaceDataEnciphermentCertificate 'replace-only'
         replaceAuthProxyCert
         replaceAutoDeployCACert
         if [[ "$VC_VERSION" =~ ^8 ]]; then replaceSMSCertificate 'VMCA-signed' 'sps-extesion' 'no-restart'; fi
         manageVCExtensionThumbprints 'Update'
         updateAutoDeployDB
         SSLTrustAnchorSelf
         updateSSLTrustAnchors
         clearCSRInfo
         promptRestartVMwareServices
         ;;
   esac   
}

#------------------------------
# Prompt options for replacing STS Signing certificate
#------------------------------
function promptReplaceSTS() {
   STS_REPLACE='VMCA-SIGNED'
   header 'Select STS Signing Certificate Replacement Method'
   echo '1. Replace STS Signing certificate with a VMCA-signed certificate'
   echo '2. Replace STS Signing certificate with a CA-signed certificate'
   read -p $'\nSelect an option [Return to Main Menu]: ' STS_REPLACE_INPUT

   if [ -z "$STS_REPLACE_INPUT" ]; then return 1; fi

   if [ "$STS_REPLACE_INPUT" == '2' ]; then STS_REPLACE='CA-SIGNED'; fi

   logInfo "User selected to replace STS Signing certificate with a $STS_REPLACE certificate"

   retun 0
}

#------------------------------
# Prompt options for replacing Machine SSL certificate
#------------------------------
function promptReplaceMachineSSL() {
   MACHINE_SSL_REPLACE='VMCA-SIGNED'
   unset MACHINE_SSL_REPLACE_INPUT

   header 'Select Machine SSL Certificate Replacement Method'
   echo ' 1. Replace Machine SSL certificate with a VMCA-signed certificate'
   echo ' 2. Replace Machine SSL certificate with a CA-signed certificate'
   read -p $'\nSelect an option [Return to Main Menu]: ' MACHINE_SSL_REPLACE_INPUT

   if [ -z "$MACHINE_SSL_REPLACE_INPUT" ]; then
      return 1
   else
      if [ "$MACHINE_SSL_REPLACE_INPUT" == '2' ]; then MACHINE_SSL_REPLACE='CA-SIGNED'; fi

      logInfo "User selected to replace Machine SSL certificate with a $MACHINE_SSL_REPLACE certificate"
      return 0
   fi
}

#------------------------------
# Prompt options for replacing Solution User certificates
#------------------------------
function promptReplaceSolutionUsers() {
   SOLUTION_USER_REPLACE='VMCA-SIGNED'
   unset SOLUTION_USER_REPLACE_INPUT

   header 'Select Solution User Certificate Replacement Method'
   echo ' 1. Replace Solution User certificates with VMCA-signed certificates'
   echo ' 2. Replace Solution User certificates with CA-signed certificates (not recommended)'
   read -p $'\nSelect an option [Return to Main Menu]: ' SOLUTION_USER_REPLACE_INPUT

   if [ -z "$SOLUTION_USER_REPLACE_INPUT" ]; then return 1; fi

   if [ "$SOLUTION_USER_REPLACE_INPUT" == '2' ]; then
      unset SOLUTION_USER_REPLACE_INPUT
      cat << EOF
${YELLOW}
-------------------------!!! STOP !!!----------------------------

Solution User certificates are used by services internally to 
authenticate into vCenter and the SSO instance.

These certificates are not presented externally (as of 7.0 and later)
and it is VMware's recommendation that they be VMCA-signed
certificates.

The replacement options, again, are:

 1. Replace Solution User certificates with VMCA-signed certificates
 2. Replace Solution User certificates with CA-signed certificates (not recommended)${NORMAL}
EOF
      read -p $'\n'"${YELLOW}Please confirm your selection [Return to Main Menu]:${NORMAL} " SOLUTION_USER_REPLACE_INPUT
   
      if [ -z "$SOLUTION_USER_REPLACE_INPUT" ]; then return 1; fi
   fi

   if [ "$SOLUTION_USER_REPLACE_INPUT" == '2' ]; then
      SOLUTION_USER_REPLACE='CA-SIGNED'
   fi

   logInfo "User selected to replace Solution User certificates with $SOLUTION_USER_REPLACE certificates"
   return 0
}

#------------------------------
# Prompt options for replacing Authentication Proxy certificate
#------------------------------
function promptReplaceAuthProxy() {
   AUTH_PROXY_REPLACE='VMCA-SIGNED'
   unset AUTH_PROXY_REPLACE_INPUT

   header 'Select Authentication Proxy Certificate Replacement Method'
   echo '1. Replace Authentication Proxy certificate with VMCA-signed certificate'
   echo '2. Replace Authentication Proxy certificate with CA-signed certificate'
   read -p $'\nSelect an option [Return to Main Menu]: ' AUTH_PROXY_REPLACE_INPUT

   if [ -z "$AUTH_PROXY_REPLACE_INPUT" ]; then return 1; fi

   if [ "$AUTH_PROXY_REPLACE_INPUT" == '2' ]; then AUTH_PROXY_REPLACE='CA-SIGNED'; fi

   logInfo "User selected to replace Authentication Proxy certifcate with a $AUTH_PROXY_REPLACE certificate"
   return 0
}

#------------------------------
# Prompt options for replacing Auto Deploy CA certificate
#------------------------------
function promptReplaceAutoDeployCA() {
   AUTO_DEPLOY_CA_REPLACE='SELF-SIGNED'
   unset AUTO_DEPLOY_CA_REPLACE_INPUT

   header 'Select Auto Deploy CA Certificate Replacement Method'
   echo '1. Replace Auto Deploy CA certificate with a self-signed certificate'
   echo '2. Replace Auto Deploy CA certificate with a CA-signed certificate'
   read -p $'\nSelect an option [Return to Main Menu]: ' AUTO_DEPLOY_CA_REPLACE_INPUT

   if [ -z "$AUTO_DEPLOY_CA_REPLACE_INPUT" ]; then return 1; fi

   if [ "$AUTO_DEPLOY_CA_REPLACE_INPUT" == '2' ]; then AUTO_DEPLOY_CA_REPLACE='CA-SIGNED'; fi
   
   logInfo "User selected to replace Auto Deploy CA certificate with a $AUTO_DEPLOY_CA_REPLACE certificate"
   return 0
}

#------------------------------
# Prompt options for replacing VMDir certificate
#------------------------------
function promptReplaceVMDir() {
   VMDIR_REPLACE='VMCA-SIGNED'
   unset VMDIR_REPLACE_INPUT

   header 'Select VMDir Certificate Replacement Method'
   echo '1. Replace VMware Directory Service certificate with a VMCA-signed certificate'
   echo '2. Replace VMware Directory Service certificate with a CA-signed certificate'
   read -p $'\nSelect an option [Return to Main Menu]: ' VMDIR_REPLACE_INPUT

   if [ -z "$VMDIR_REPLACE_INPUT" ]; then return 1; fi

   if [ "$VMDIR_REPLACE_INPUT" == '2' ]; then VMDIR_REPLACE='CA-SIGNED'; fi
   
   logInfo "User selected to replace VMDir certificate with a $VMDIR_REPLACE certificate"
   return 0
}

#------------------------------
# Remove legacy VMDir certificate
#------------------------------
function removeVMDirCert() {
   if [ -f /usr/lib/vmware-vmdir/share/config/vmdircert.pem ]; then
      header 'Manage VMware Directory Certificate'
      echo $'\n'"The certificate at ${CYAN}/usr/lib/vmware-vmdir/share/config/vmdircert.pem${NORMAL}"
      echo 'is no longer used in vCenter 7.0 and later.'
      read -p $'\nRemove this certificate and private key [n]: ' REMOVE_LEGACY_VMDIR_CERT_INPUT

      if [ -z $REMOVE_LEGACY_VMDIR_CERT_INPUT ]; then REMOVE_LEGACY_VMDIR_CERT_INPUT='n'; fi

      if [[ "$REMOVE_LEGACY_VMDIR_CERT_INPUT" =~ ^[Yy] ]]; then
         header 'Remove Legacy VMware Directory Certificate'
         backupFilesystemCertKey '/usr/lib/vmware-vmdir/share/config/vmdircert.pem' '/usr/lib/vmware-vmdir/share/config/vmdirkey.pem' 'vmdir'

         task 'Remove legacy VMDir certificate'
         rm /usr/lib/vmware-vmdir/share/config/vmdircert.pem 2>>$LOG || errorMessage 'Unable to remove legacy VMDir certificate'
         statusMessage 'OK' 'GREEN'

         if [ -f /usr/lib/vmware-vmdir/share/config/vmdirkey.pem ]; then
            task 'Remove legacy VMDir private key'
            rm /usr/lib/vmware-vmdir/share/config/vmdirkey.pem 2>>$LOG || errorMessage 'Unable to remove legacy VMDir private key'
            statusMessage 'OK' 'GREEN'
         fi
      fi
   fi
}

#------------------------------
# Replace the VMCA certificate
#------------------------------
function replaceVMCACert() {
   if [ $VMCA_REPLACE == 'SELF-SIGNED' ]; then
      unset VMCA_CN_INPUT
      NEW_VMCA_CERT=$STAGE_DIR/vmca.crt
      NEW_VMCA_KEY=$STAGE_DIR/vmca.key
      
      if [ -z "$CSR_COUNTRY" ]; then getCSRInfo 'VMCA'; fi
      
      read -p $'\n'"Enter a value for the ${CYAN}CommonName${NORMAL} of the certificate [$VMCA_CN_DEFAULT]: " VMCA_CN_INPUT
         
      if [ -z "$VMCA_CN_INPUT" ]; then VMCA_CN_INPUT="$VMCA_CN_DEFAULT"; fi
     
      header 'Replace VMCA Certificate'
   
      generateCertoolConfig 'vmca' "$VMCA_CN_INPUT"
      
      task 'Generate VMCA certificate'
      $CERTOOL --genselfcacert --outcert=$NEW_VMCA_CERT --outprivkey=$NEW_VMCA_KEY --config=$STAGE_DIR/vmca.cfg > /dev/null 2>&1 || errorMessage 'Unable to generate new VMCA certificate'
      statusMessage 'OK' 'GREEN'	  
   else
      unset VMCA_CA_SIGNED_OPTION_INPUT
      header 'Replace VMCA Certificate'   
      echo $'\n1. Generate Certificate Signing Request and Private Key'
      echo '2. Generate Certificate Signing Request and Private Key'
      echo '   from custom OpenSSL configuration file'
      echo '3. Import CA-signed Certificate and Key'
      read -p $'\nSelect an option [Return to Main Menu]: ' VMCA_CA_SIGNED_OPTION_INPUT
	  
	  if [ -z $VMCA_CA_SIGNED_OPTION_INPUT ]; then return 1; fi

      if [ "$VMCA_CA_SIGNED_OPTION_INPUT" == '3' ]; then
         unset VMCA_CERT_INPUT	  
         read -e -p "Provide path to the CA-signed ${CYAN}VMCA${NORMAL} certificate: " VMCA_CERT_INPUT
         while [ ! -f "$VMCA_CERT_INPUT" ]; do read -e -p $'\n'"${YELLOW}File not found, please provide path to the CA-signed VMCA certificate:${NORMAL} " VMCA_CERT_INPUT; done
         getCorrectCertFormat "$VMCA_CERT_INPUT" 'NEW_VMCA_CERT'
         NEW_VMCA_CERT_MODULUS_HASH=$(openssl x509 -noout -modulus -in $NEW_VMCA_CERT 2>/dev/null | md5sum | awk '{print $1}')
		   logInfo "Provided new VMCA certificate:"
         logDetails "$(cat $NEW_VMCA_CERT)"
         logInfo "New VMCA certificate details:"
         logDetails "$(openssl x509 -noout -text -fingerprint -sha1 -in $NEW_VMCA_CERT)"

         getPrivateKey "$NEW_VMCA_CERT_MODULUS_HASH" "NEW_VMCA" 'VMCA'
         getCAChain "$NEW_VMCA_CERT"
         
		   header 'Certificate Verification'
         task 'Verifying certificate and key: '
         verifyCertAndKey $NEW_VMCA_CERT $NEW_VMCA_KEY
         statusMessage 'OK' 'GREEN'
         
         task 'Verifying CA certificate: '
         isCertCA "$(cat $NEW_VMCA_CERT)" || errorMessage "The provided certificate $NEW_VMCA_CERT is not a CA certificate."
		   openssl x509 -in $NEW_VMCA_CERT | cat /dev/stdin $TRUSTED_ROOT_CHAIN > $STAGE_DIR/vmca-complete-chain.pem
		   NEW_VMCA_CERT="$STAGE_DIR/vmca-complete-chain.pem"
         statusMessage 'OK' 'GREEN' 
      else
	      unset VMCA_CN_INPUT
         NEW_VMCA_CSR=$REQUEST_DIR/vmca-$TIMESTAMP.csr
         NEW_VMCA_KEY=$REQUEST_DIR/vmca-$TIMESTAMP.key
         if [ "$VMCA_CA_SIGNED_OPTION_INPUT" == '1' ]; then
            NEW_VMCA_CFG=$REQUEST_DIR/vmca.cfg         
            if [ -z "$CSR_COUNTRY" ]; then getCSRInfo 'VMCA'; fi         
            read -p $'\n'"Enter a value for the ${CYAN}CommonName${NORMAL} of the certificate [CA]: " VMCA_CN_INPUT
            if [ -z "$VMCA_CN_INPUT" ]; then VMCA_CN_INPUT='CA'; fi
            defineSANEntries 'vmca' 'VMCA'
            generateOpensslConfig "$VMCA_CN_INPUT" $NEW_VMCA_CFG 'vmca'
         else
            logInfo 'User has chosen to generate the VMCA private key and CSR from a custom OpenSSL configuration file'
            read -e -p $'\nEnter path to custom OpenSSL configuration file: ' NEW_VMCA_CFG
            while [ ! -f "$NEW_VMCA_CFG" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to custom OpenSSL configuration file:${NORMAL} " NEW_VMCA_CFG; done
            logDetails "$(cat $NEW_VMCA_CFG)"
         fi
         generateCSR $NEW_VMCA_CSR $NEW_VMCA_KEY "$NEW_VMCA_CFG" || errorMessage "Unable to generate Certificate Signing Request and Private Key"
         
         printf "\nCertificate Signing Request generated at ${CYAN}${NEW_VMCA_CSR}${NORMAL}"
         printf "\nPrivate Key generated at ${CYAN}${NEW_VMCA_KEY}${NORMAL}\n"
         
         return 1
      fi	  
   fi
   
   backupFilesystemCertKey '/var/lib/vmware/vmca/root.cer' '/var/lib/vmware/vmca/privatekey.pem' 'VMCA'
   
   task 'Reconfigure VMCA'
   $CERTOOL --rootca --cert=$NEW_VMCA_CERT --privkey=$NEW_VMCA_KEY > /dev/null 2>&1 || errorMessage 'Unable to reconfigure the VMCA with the new certificate'
   statusMessage 'OK' 'GREEN'
    
   if [ $VMCA_REPLACE == 'CA-SIGNED' ]; then
      task 'Publish CA certificates to VMDir'
      publishCASigningCertificates $TRUSTED_ROOT_CHAIN
   fi
   
   if [ -f /etc/vmware-sso/keys/ssoserverRoot.crt ]; then
      task 'Update VMCA certificate on filesystem'
      mv /etc/vmware-sso/keys/ssoserverRoot.crt /etc/vmware-sso/keys/ssoserverRoot.crt.old > /dev/null 2>&1 || errorMessage 'Unable to backup old SSO server root certificate'
      cp $VMCA_CERT /etc/vmware-sso/keys/ssoserverRoot.crt > /dev/null 2>&1 || errorMessage 'Unable to update SSO server root certificate'
      statusMessage 'OK' 'GREEN'
   fi
   
   return 0
}

#------------------------------
# Replace the Machine SSL certificate
#------------------------------
function replaceMachineSSLCert() {
   if [ "$NODE_TYPE" != 'infrastructure' ] && ! checkService 'vmware-vpostgres'; then
      echo "${YELLOW}The vPostgres service is stopped!"
      echo "Please ensure this service is running before replacing the Machine SSL certificate."
      echo "Hint: Check the number of CRL entries in VECS${NORMAL}"
      return 1 
   fi
   if [ $MACHINE_SSL_REPLACE == 'VMCA-SIGNED' ]; then
      MACHINE_SSL_CERT=$STAGE_DIR/machine-ssl.crt
      MACHINE_SSL_PUBKEY=$STAGE_DIR/machine-ssl.pub
      MACHINE_SSL_KEY=$STAGE_DIR/machine-ssl.key  

      if [ -z "$CSR_COUNTRY" ]; then getCSRInfo 'Machine SSL'; fi
      	 
	   checkPSCHA
	  	  	  
	   if [ -n "$PSC_LB" ]; then
	      unset PSC_LB_ADDITIONAL_HOSTS_INPUT
		   cat << EOF
${YELLOW}-------------------------!!! WARNING !!!-------------------------"
  This PSC is detected to be in an HA configuration!${NORMAL}"
  The Load Balancer address is detected to be: ${CYAN}${PSC_LB}${NORMAL}"
  This hostname will be added to the Subject Altnernative Name field.		 
EOF
         read -p $'\n'"Please add the hostnames of the additional PSCs behind the load balancer (comma-separated list): " PSC_LB_ADDITIONAL_HOSTS_INPUT			
			
		   if [ -z "$PSC_LB_ADDITIONAL_HOSTS_INPUT" ]; then
		      CSR_ADDITIONAL_DNS="$PSC_LB"			   
		   else
		      CSR_ADDITIONAL_DNS="$PSC_LB,$PSC_LB_ADDITIONAL_HOSTS_INPUT"
		   fi		 
	   fi
	  
	   header 'Replace Machine SSL Certificate'
      generateCertoolConfig 'machine-ssl' $PNID 
      
      task 'Regenerate Machine SSL certificate'
      regenerateVMCASignedCertificate 'machine-ssl'
      statusMessage 'OK' 'GREEN'
   else
      unset MACHINE_SSL_CA_SIGNED_OPTION_INPUT
      echo $'\n1. Generate Certificate Signing Request and Private Key'
      echo '2. Generate Certificate Signing Request and Private Key'
      echo '   from custom OpenSSL configuration file'
      echo '3. Import CA-signed Certificate and Key'
      read -p $'\nSelect an option [Return to Main Menu]: ' MACHINE_SSL_CA_SIGNED_OPTION_INPUT
	  
	   if [ -z $MACHINE_SSL_CA_SIGNED_OPTION_INPUT ]; then return 1; fi

      if [ "$MACHINE_SSL_CA_SIGNED_OPTION_INPUT" == '3' ]; then
         logInfo 'User has chosen to import a CA-signed Machine SSL certificate and key'
         read -e -p $'\n'"Provide path to the CA-signed ${CYAN}Machine SSL${NORMAL} certificate: " MACHINE_SSL_CERT_INPUT
         while [ ! -f "$MACHINE_SSL_CERT_INPUT" ]; do read -e -p $'\n'"${YELLOW}File not found, please provide path to the Machine SSL certificate:${NORMAL} " MACHINE_SSL_CERT_INPUT; done
         
         getCorrectCertFormat "$MACHINE_SSL_CERT_INPUT" 'MACHINE_SSL_CERT'
         MACHINE_SSL_CERT_MODULUS_HASH=$(openssl x509 -noout -modulus -in $MACHINE_SSL_CERT 2>/dev/null | md5sum | awk '{print $1}')
		 
         if ! checkCertSignatureAlgorithm "$(cat $MACHINE_SSL_CERT)"; then
            errorMessage 'Certificate is using an unsupported signature algorithm' 'machine-ssl' 'no-task'
         fi
         
         logInfo "Provided new Machine SSL certificate:"
         logDetails "$(cat $MACHINE_SSL_CERT)"
         logInfo "New Machine SSL certificate details:"
         logDetails "$(openssl x509 -noout -text -fingerprint -sha1 -in $MACHINE_SSL_CERT)"

		   getPrivateKey "$MACHINE_SSL_CERT_MODULUS_HASH" "MACHINE_SSL" 'Machine SSL'     
		   getCAChain "$MACHINE_SSL_CERT"

         header 'Certificate Verification'
         task 'Verifying certificate and key'        
        
         logInfo "Using Machine SSL cert: $MACHINE_SSL_CERT"
         logInfo "Using Private Key: $MACHINE_SSL_KEY"
         logInfo "Using trusted root chain: $TRUSTED_ROOT_CHAIN"
        
         verifyCertAndKey "$MACHINE_SSL_CERT" "$MACHINE_SSL_KEY" 
         statusMessage 'OK' 'GREEN'
        
         task 'Verifying root chain'
         verifyRootChain $MACHINE_SSL_CERT $TRUSTED_ROOT_CHAIN || errorMessage 'Certificate Authority chain is not complete'
         statusMessage 'OK' 'GREEN'
                
         task 'Verify PNID included in SAN'
         cat "$MACHINE_SSL_CERT" | openssl x509 -noout -text 2>/dev/null | grep -A1 'Subject Alternative Name' | grep -i "$PNID" > /dev/null || errorMessage 'The Primary Network Identifier (PNID) is not included in the Subject Alternative Name field'
         statusMessage 'OK' 'GREEN'       
        
         header 'Replace Machine SSL Certificate'       
         
         task 'Publish CA signing certificates'
		   publishCASigningCertificates $TRUSTED_ROOT_CHAIN        
      else
	      unset MACHINE_SSL_CN_INPUT
         MACHINE_SSL_CSR=$REQUEST_DIR/machine-ssl-$TIMESTAMP.csr
         MACHINE_SSL_KEY=$REQUEST_DIR/machine-ssl-$TIMESTAMP.key
                  
         if [ "$MACHINE_SSL_CA_SIGNED_OPTION_INPUT" == '1' ]; then
            MACHINE_SSL_CFG=$REQUEST_DIR/machine-ssl.cfg
            logInfo 'User has chosen to generate the Machine SSL private key and CSR'
            if [ -z "$CSR_COUNTRY" ]; then getCSRInfo 'Machine SSL'; fi

            read -p "Enter a value for the ${CYAN}CommonName${NORMAL} of the certificate [$HOSTNAME]: " MACHINE_SSL_CN_INPUT

            if [ -z "$MACHINE_SSL_CN_INPUT" ]; then MACHINE_SSL_CN_INPUT=$HOSTNAME; fi

            checkPSCHA
            defineSANEntries 'machine-ssl' 'Machine SSL'
            generateOpensslConfig $MACHINE_SSL_CN_INPUT $MACHINE_SSL_CFG 'machine-ssl'
         else
            logInfo 'User has chosen to generate the Machine SSL private key and CSR from a custom OpenSSL configuration file'
            read -e -p $'\nEnter path to custom OpenSSL configuration file: ' MACHINE_SSL_CFG
            while [ ! -f "$MACHINE_SSL_CFG" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to custom OpenSSL configuration file:${NORMAL} " MACHINE_SSL_CFG; done
            logDetails "$(cat $MACHINE_SSL_CFG)"
         fi
         
         generateCSR $MACHINE_SSL_CSR $MACHINE_SSL_KEY "$MACHINE_SSL_CFG" || errorMessage "Unable to generate Certificate Signing Request and Private Key"
         
         printf "\nCertificate Signing Request generated at ${CYAN}${MACHINE_SSL_CSR}${NORMAL}"
         printf "\nPrivate Key generated at ${CYAN}${MACHINE_SSL_KEY}${NORMAL}\n"
         
         return 1
      fi
	  
   fi
   
   backupVECSCertKey 'machine-ssl'
   updateVECS 'machine-ssl'

   if checkVECSStore 'STS_INTERNAL_SSL_CERT'; then
      updateVECS 'legacy-lookup-service' 'machine-ssl'
   fi
 
   UPDATED_MACHINE_SSL=1

   return 0
}

#------------------------------
# Replace Solution User certificates
#------------------------------
function replaceSolutionUserCerts() {
   if [ "$NODE_TYPE" != 'infrastructure' ] && ! checkService 'vmware-vpostgres'; then
      echo "${YELLOW}The vPostgres service is stopped!"
      echo "Please ensure this service is running before replacing the Solution User certificates."
      echo "Hint: Check the number of CRL entries in VECS${NORMAL}"
      return 1 
   fi
   
   if [ $SOLUTION_USER_REPLACE == 'VMCA-SIGNED' ]; then
      for soluser in "${SOLUTION_USERS[@]}"; do
		   soluser_fix=$(echo $soluser | sed 's/-/_/g')
		   dynamic_cert="${soluser_fix^^}_CERT"
		   dynamic_key="${soluser_fix^^}_KEY"
		   dynamic_pubkey="${soluser_fix^^}_PUBKEY"
		   declare "$dynamic_cert"=$STAGE_DIR/$soluser.crt
		   declare "$dynamic_key"=$STAGE_DIR/$soluser.key
 		   declare "$dynamic_pubkey"=$STAGE_DIR/$soluser.pub
	   done

      header 'Replace Solution User Certificates'
      
	   checkServicePrincipals
	  
	   echo 'Generate new certificates and keys:'
      for soluser in "${SOLUTION_USERS[@]}"; do	  
         task "   $soluser"
		   soluser_fix=$(echo $soluser | sed 's/-/_/g')
		   dynamic_cert="${soluser_fix^^}_CERT"
		   dynamic_key="${soluser_fix^^}_KEY"
		   dynamic_pubkey="${soluser_fix^^}_PUBKEY"
		   $CERTOOL --genkey --privkey=${!dynamic_key} --pubkey=${!dynamic_pubkey} > /dev/null 2>&1 || errorMessage "Unable to generate a key pair for $soluser"
		   if [ $soluser == 'wcp' ]; then
		      $CERTOOL --gencert --server=$PSC_LOCATION --Name=$soluser --genCIScert --dataencipherment --privkey=${!dynamic_key} --cert=${!dynamic_cert} --config=/dev/null --Country="$CSR_COUNTRY_DEFAULT" --State="$CSR_STATE_DEFAULT" --Locality="$CSR_LOCALITY_DEFAULT" --Organization="$CSR_ORG_DEFAULT"  --OrgUnit="mID-$MACHINE_ID" > /dev/null 2>&1 || errorMessage "Unable to generate a VMCA-signed cert for $soluser"
		   else
		      $CERTOOL --gencert --server=$PSC_LOCATION --Name=$soluser --genCIScert --privkey=${!dynamic_key} --cert=${!dynamic_cert}  --config=/dev/null --Country="$CSR_COUNTRY_DEFAULT" --State="$CSR_STATE_DEFAULT" --Locality="$CSR_LOCALITY_DEFAULT" --Organization="$CSR_ORG_DEFAULT" --OrgUnit="mID-$MACHINE_ID" --FQDN=$PNID > /dev/null 2>&1 || errorMessage "Unable to generate a VMCA-signed cert for $soluser"
		   fi
         statusMessage 'OK' 'GREEN'		 
      done	  	 	 
   else
      unset SOLUTION_USERS_CA_SIGNED_OPTION_INPUT
      echo $'\n1. Generate Certificate Signing Requests and Private Keys'
      echo '2. Generate Certificate Signing Request and Private Keys'
      echo '   from custom OpenSSL configuration files'
      echo '3. Import CA-signed Certificates and Keys'
      read -p $'\nSelect an option [Return to Main Menu]: ' SOLUTION_USERS_CA_SIGNED_OPTION_INPUT

      if [ -z $SOLUTION_USERS_CA_SIGNED_OPTION_INPUT ]; then return 1; fi

      if [ "$SOLUTION_USERS_CA_SIGNED_OPTION_INPUT" == '3' ]; then
         logInfo 'User has chosen to import a CA-signed Solution User certificates and keys'
	  
	      for soluser in "${SOLUTION_USERS[@]}"; do
	         unset SOLUTION_USER_CERT_INPUT
		      unset SOLUTION_USER_KEY_INPUT
		      soluser_fix=$(echo $soluser | sed 's/-/_/g')
		      dynamic_cert="${soluser_fix^^}_CERT"
		      dynamic_key="${soluser_fix^^}_KEY"
			   dynamic_cert_modulus="${soluser_fix^^}_MODULUS_HASH"
			
		      read -e -p $'\n'"Provide path to the CA-signed ${CYAN}${soluser}${NORMAL} certificate: " SOLUTION_USER_CERT_INPUT
            while [ ! -f "$SOLUTION_USER_CERT_INPUT" ]; do read -e -p $'\n'"${YELLOW}File not found, please provide path to the ${soluser} certificate:${NORMAL} " SOLUTION_USER_CERT_INPUT; done
            getCorrectCertFormat "$SOLUTION_USER_CERT_INPUT" "$dynamic_cert"

            if ! checkCertSignatureAlgorithm "$(cat ${!dynamic_cert})"; then
               errorMessage 'Certificate is using an unsupported signature algorithm' 'soluser' 'no-task'
            fi

            declare "$dynamic_cert_modulus"=$(openssl x509 -noout -modulus -in ${!dynamic_cert} 2>/dev/null | md5sum | awk '{print $1}')
         
            logInfo "Provided new $soluser certificate:"
            logDetails "$(cat ${!dynamic_cert})"
            logInfo "New $soluser certificate details:"
            logDetails "$(openssl x509 -noout -text -fingerprint -sha1 -in ${!dynamic_cert})"

            getPrivateKey "${!dynamic_cert_modulus}" "${soluser_fix^^}" "$soluser"			
         done	          
         echo ''
         getCAChain "$MACHINE_CERT"      
         
		   header 'Replace Solution User Certificates'
		 
		   checkServicePrincipals
		 
		   echo 'Verify certificates and keys:'
         		 
		   for soluser in "${SOLUTION_USERS[@]}"; do
		      soluser_fix=$(echo $soluser | sed 's/-/_/g')
		      dynamic_cert="${soluser_fix^^}_CERT"
		      dynamic_key="${soluser_fix^^}_KEY"
			   task "   $soluser"
			   verifyCertAndKey "${!dynamic_cert}" "${!dynamic_key}"
			   statusMessage 'OK' 'GREEN'
		   done		         
         
         task 'Verifying root chain'
         verifyRootChain $MACHINE_CERT $TRUSTED_ROOT_CHAIN || errorMessage 'Certificate Authority chain is not complete'
         statusMessage 'OK' 'GREEN'
		 
		   task 'Publish CA signing certificates'
		   publishCASigningCertificates $TRUSTED_ROOT_CHAIN
      else
	      for soluser in "${SOLUTION_USERS[@]}"; do
		      soluser_fix=$(echo $soluser | sed 's/-/_/g')
		      dynamic_csr="${soluser_fix^^}_CSR"
		      dynamic_key="${soluser_fix^^}_KEY"
			   declare "$dynamic_csr"=$REQUEST_DIR/$soluser-$TIMESTAMP.csr
			   declare "$dynamic_key"=$REQUEST_DIR/$soluser-$TIMESTAMP.key
            if [ "$SOLUTION_USERS_CA_SIGNED_OPTION_INPUT" == '1' ]; then
               dynamic_cfg="${soluser_fix^^}_CFG"			   
			      declare "$dynamic_cfg"=$REQUEST_DIR/$soluser-$TIMESTAMP.cfg
            fi
	      done         

         if [ "$SOLUTION_USERS_CA_SIGNED_OPTION_INPUT" == '1' ] && [ -z "$CSR_COUNTRY" ]; then getCSRInfo 'Solution Users'; fi
         defineSANEntries 'soluser' 'Solution User'
         for soluser in "${SOLUTION_USERS[@]}"; do
		      soluser_fix=$(echo $soluser | sed 's/-/_/g')
		      dynamic_csr="${soluser_fix^^}_CSR"
		      dynamic_key="${soluser_fix^^}_KEY"
            dynamic_cfg="${soluser_fix^^}_CFG"			      
            if [ "$SOLUTION_USERS_CA_SIGNED_OPTION_INPUT" == '1' ]; then
			      generateOpensslConfig "$soluser-$MACHINE_ID" "${!dynamic_cfg}" "$soluser"
            else
               logInfo "User has chosen to generate the $soluser private key and CSR from a custom OpenSSL configuration file"
               read -e -p $'\n'"Enter path to custom OpenSSL configuration file for ${CYAN}${soluser}${NORMAL}: " SOLUTION_USER_CFG
               while [ ! -f "$SOLUTION_USER_CFG" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to custom OpenSSL configuration file for ${soluser}:${NORMAL} " SOLUTION_USER_CFG; done
               declare "$dynamic_cfg"="$SOLUTION_USER_CFG"               
               logDetails "$(cat ${!dynamic_cfg})"
            fi
			   generateCSR "${!dynamic_csr}" "${!dynamic_key}" "${!dynamic_cfg}" || errorMessage "Unable to generate Certificate Signing Request and Private Key for $soluser"
		   done         
         
         echo $'\nCertificate Signing Requests generated at:'
		 
		   for soluser in "${SOLUTION_USERS[@]}"; do
		      soluser_fix=$(echo $soluser | sed 's/-/_/g')
		      dynamic_csr="${soluser_fix^^}_CSR"
		      echo "${CYAN}${!dynamic_csr}${NORMAL}"
		   done
		 
         echo $'\nPrivate Keys generated at:'
		   for soluser in "${SOLUTION_USERS[@]}"; do
		      soluser_fix=$(echo $soluser | sed 's/-/_/g')
		      dynamic_key="${soluser_fix^^}_KEY"
		      echo "${CYAN}${!dynamic_key}${NORMAL}"
		   done             

         return 1
      fi	  
   fi
   
   echo $'\nBackup certificate and private key:'
   
   for soluser in "${SOLUTION_USERS[@]}"; do
      backupVECSCertKey "$soluser"
   done
   
   echo $'\nUpdating certificates and keys in VECS:'
 
   for soluser in "${SOLUTION_USERS[@]}"; do
      updateVECS "$soluser"
   done

   echo $'\nUpdating solution user certificates in VMware Directory:'
   
   for soluser in "${SOLUTION_USERS[@]}"; do
      soluser_fix=$(echo $soluser | sed 's/-/_/g')
      dynamic_cert="${soluser_fix^^}_CERT"
      replaceServicePrincipalCert "$soluser" "${!dynamic_cert}"	  
   done
}

#------------------------------
# Replace a Solution User certificate in VMDir
#------------------------------
function replaceServicePrincipalCert() {
   task "   $1"
   $DIR_CLI service update --name $1-$MACHINE_ID --cert $2 --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" > /dev/null 2>&1 || errorMessage "Unable to update $1-$MACHINE_ID solution user certificate in VMDir"
   statusMessage 'OK' 'GREEN'
}

#------------------------------
# Replace the Authentication Proxy certificate
#------------------------------
function replaceAuthProxyCert() {
   header 'Replace Authentication Proxy Certificate'

   if [ $AUTH_PROXY_REPLACE = 'VMCA-SIGNED' ]; then            
      generateCertoolConfig 'auth proxy'
      
      task 'Regenerate Authentication Proxy certificate'
      regenerateVMCASignedCertificate 'auth-proxy'
      statusMessage 'OK' 'GREEN'
          
      AUTH_PROXY_CERT=$STAGE_DIR/auth-proxy.crt
      AUTH_PROXY_KEY=$STAGE_DIR/auth-proxy.key
   else
      unset AUTH_PROXY_CA_SIGNED_OPTION_INPUT
      echo $'\n1. Generate Certificate Signing Request and Private Key'
      echo '2. Generate Certificate Signing Request and Private Key'
      echo '   from custom OpenSSL configuration file'
      echo '3. Import CA-signed Certificate and Key'
      read -p $'\nSelect an option [Return to Main Menu]: ' AUTH_PROXY_CA_SIGNED_OPTION_INPUT
          
	   if [ -z $AUTH_PROXY_CA_SIGNED_OPTION_INPUT ]; then return 1; fi
	  
      if [ "$AUTH_PROXY_CA_SIGNED_OPTION_INPUT" == '3' ]; then
	      read -e -p $'\n'"Provide path to CA-signed ${CYAN}Authentication Proxy${NORMAL} certificate: " AUTH_PROXY_CERT_INPUT
         while [ ! -f "$AUTH_PROXY_CERT_INPUT" ]; do read -e -p "${YELLOW}File not found, please provide path to the Authentication Proxy certificate:${NORMAL} " AUTH_PROXY_CERT_INPUT; done
         getCorrectCertFormat "$AUTH_PROXY_CERT_INPUT" 'AUTH_PROXY_CERT'
		   
         if ! checkCertSignatureAlgorithm "$(cat $AUTH_PROXY_CERT)"; then
            errorMessage 'Certificate is using an unsupported signature algorithm' 'auth-proxy' 'no-task'
         fi
         
         AUTH_PROXY_CERT_MODULUS_HASH=$(openssl x509 -noout -modulus -in $AUTH_PROXY_CERT 2>/dev/null | md5sum | awk '{print $1}')
		   logInfo "Provided new Authentication Proxy certificate:"
         logDetails "$(cat $AUTH_PROXY_CERT)"
         logInfo "New Authentication Proxy certificate details:"
         logDetails "$(openssl x509 -noout -text -fingerprint -sha1 -in $AUTH_PROXY_CERT)"
         getPrivateKey "$AUTH_PROXY_CERT_MODULUS_HASH" "AUTH_PROXY" 'Authentication Proxy'
         getCAChain "$AUTH_PROXY_CERT" 
         
         task 'Verifying certificates and keys: '
         verifyCertAndKey $AUTH_PROXY_CERT $AUTH_PROXY_KEY
      else
         AUTH_PROXY_CSR=$REQUEST_DIR/auth-proxy-$TIMESTAMP.csr
         AUTH_PROXY_KEY=$REQUEST_DIR/auth-proxy-$TIMESTAMP.key

         if [ "$AUTH_PROXY_CA_SIGNED_OPTION_INPUT" == '1' ]; then
            AUTH_PROXY_CFG=$REQUEST_DIR/auth-proxy.cfg                 
            if [ -z "$CSR_COUNTRY" ]; then getCSRInfo; fi
            defineSANEntries 'auth-proxy' 'Authentication Proxy'             
            generateOpensslConfig $HOSTNAME $AUTH_PROXY_CFG 'Authentication Proxy'
         else
            logInfo 'User has chosen to generate the Authentication Proxy private key and CSR from a custom OpenSSL configuration file'
            read -e -p $'\nEnter path to custom OpenSSL configuration file: ' AUTH_PROXY_CFG
            while [ ! -f "$AUTH_PROXY_CFG" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to custom OpenSSL configuration file:${NORMAL} " AUTH_PROXY_CFG; done
            logDetails "$(cat $AUTH_PROXY_CFG)"
         fi
         
         generateCSR $AUTH_PROXY_CSR $AUTH_PROXY_KEY "$AUTH_PROXY_CFG" || errorMessage "Unable to generate Certificate Signing Request and Private Key"
		   
         printf "\nCertificate Signing Request generated at ${CYAN}${AUTH_PROXY_CSR}${NORMAL}"
         printf "\nPrivate Key generated at ${CYAN}${AUTH_PROXY_KEY}${NORMAL}\n"

         return 1
      fi
   fi   

   if [ $AUTH_PROXY_REPLACE != 'VMCA-SIGNED' ]; then
      task 'Publish CA signing certificates'
      publishCASigningCertificates $TRUSTED_ROOT_CHAIN
   fi
   
   
   if [ -f /var/lib/vmware/vmcam/ssl/rui.crt ] && [ -f /var/lib/vmware/vmcam/ssl/rui.key ]; then backupFilesystemCertKey '/var/lib/vmware/vmcam/ssl/rui.crt' '/var/lib/vmware/vmcam/ssl/rui.crt' 'auth-proxy'; fi
   
   task 'Replace certificate on filesystem'
   mv /var/lib/vmware/vmcam/ssl/vmcamcert.pem /var/lib/vmware/vmcam/ssl/vmcamcert.pem.old > /dev/null 2>&1 || errorMessage 'Unable to backup Authentication Proxy PEM file'
   
   cp $AUTH_PROXY_CERT /var/lib/vmware/vmcam/ssl/rui.crt > /dev/null 2>&1 || errorMessage 'Unable to update Authentication Proxy certificate'
   cp $AUTH_PROXY_KEY /var/lib/vmware/vmcam/ssl/rui.key > /dev/null 2>&1 || errorMessage 'Unable to update Authentication Proxy private key'
   cat /var/lib/vmware/vmcam/ssl/rui.key <(echo) /var/lib/vmware/vmcam/ssl/rui.crt > /var/lib/vmware/vmcam/ssl/vmcamcert.pem 2>&1 || errorMessage 'Unable to update Authentication Proxy PEM file'
   chmod 600 /var/lib/vmware/vmcam/ssl/*
   statusMessage 'OK' 'GREEN'
            
   return 0
}

#------------------------------
# Replace the Auto Deploy CA certificate
#------------------------------
function replaceAutoDeployCACert() {
   header 'Replace Auto Deploy CA Certificate'
   if [ $AUTO_DEPLOY_CA_REPLACE == 'SELF-SIGNED' ]; then
      task 'Regenerate Auto Deploy CA certificate'
      AUTO_DEPLOY_CA_CONFIG=$(cat << EOF
[ req ]
default_bits        = 2048
distinguished_name  = req_distinguished_name

encrypt_key         = no
prompt              = no

string_mask         = nombstr
x509_extensions     = x509

[ req_distinguished_name ]
O                   = VMware Auto Deploy

[ x509 ]
basicConstraints    = CA:true, pathlen:0
keyUsage            = keyCertSign
   
EOF
)
      openssl req -new -x509 -config <(echo -n "$AUTO_DEPLOY_CA_CONFIG") -days 4200 -sha256 -keyout $STAGE_DIR/auto-deploy-ca.key -out $STAGE_DIR/auto-deploy-ca.crt > /dev/null 2>&1 || errorMessage 'Unable to generate new Auto Deploy CA certificate and private key. See log for details.'
      #openssl req -new -newkey rsa:2048 -nodes -keyout $STAGE_DIR/auto-deploy-ca.key -x509 -out $STAGE_DIR/auto-deploy-ca.crt -subj '/O=VMware Auto Deploy' -days 3650 > /dev/null  2>&1 || errorMessage 'Unable to generate new Auto Deploy CA certificate and private key. See log for details.'
      statusMessage 'OK' 'GREEN'
      AUTO_DEPLOY_CA_CERT=$STAGE_DIR/auto-deploy-ca.crt
      AUTO_DEPLOY_CA_KEY=$STAGE_DIR/auto-deploy-ca.key        
   else
      unset AUTO_DEPLOY_CA_CA_SIGNED_OPTION_INPUT
      echo $'\n1. Generate Certificate Signing Request and Private Key'
      echo '2. Generate Certificate Signing Request and Private Key'
      echo '   from custom OpenSSL configuration file'
      echo '3. Import CA-signed Certificate and Key'
      read -p $'\nChoose option [Return to Main Menu]: ' AUTO_DEPLOY_CA_CA_SIGNED_OPTION_INPUT
	  
	   if [ -z $AUTO_DEPLOY_CA_CA_SIGNED_OPTION_INPUT ]; then return 1; fi

      if [ "$AUTO_DEPLOY_CA_CA_SIGNED_OPTION_INPUT" == '3' ]; then
	      logInfo 'User has chosen to import a CA-signed Auto Deploy certificate and key'
         read -e -p $'\n'"Provide path to CA-signed ${CYAN}Auto Deploy CA${NORMAL} certificate: " AUTO_DEPLOY_CA_CERT_INPUT
         while [ ! -f "$AUTO_DEPLOY_CA_CERT_INPUT" ]; do read -e -p "${YELLOW}File not found, please provide path to the Auto Deploy CA certificate:${NORMAL} " AUTO_DEPLOY_CA_CERT_INPUT; done
         getCorrectCertFormat "$AUTO_DEPLOY_CA_CERT_INPUT" 'AUTO_DEPLOY_CA_CERT'
		   
         if ! checkCertSignatureAlgorithm "$(cat $AUTO_DEPLOY_CA_CERT)"; then
            errorMessage 'Certificate is using an unsupported signature algorithm' 'auto-deploy-ca' 'no-task'
         fi

         AUTO_DEPLOY_CA_CERT_MODULUS_HASH=$(openssl x509 -noout -modulus -in $AUTO_DEPLOY_CA_CERT 2>/dev/null | md5sum | awk '{print $1}')
         logInfo "Provided new Auto Deploy CA certificate:"
         logDetails "$(cat $AUTO_DEPLOY_CA_CERT)"
         logInfo "New Auto Deploy CA certificate details:"
         logDetails "$(openssl x509 -noout -text -fingerprint -sha1 -in $AUTO_DEPLOY_CA_CERT)"
		   getPrivateKey "$AUTO_DEPLOY_CA_CERT_MODULUS_HASH" "AUTO_DEPLOY_CA" 'Auto Deploy CA'
         getCAChain "$AUTO_DEPLOY_CERT"  
         
         task 'Verifying certificates and keys'
         verifyCertAndKey $AUTO_DEPLOY_CA_CERT $AUTO_DEPLOY_CA_KEY
         
         task 'Verifying CA certificate'
         isCertCA "$(cat $AUTO_DEPLOY_CA_CERT)" || errorMessage "The provided certificate ${AUTO_DEPLOY_CA_CERT} is not a CA certificate."
         statusMessage 'OK' 'GREEN'
      else
         AUTO_DEPLOY_CA_CSR=$REQUEST_DIR/auto-deploy-ca-$TIMESTAMP.csr
         AUTO_DEPLOY_CA_KEY=$REQUEST_DIR/auto-deploy-ca-$TIMESTAMP.key

         if [ "$AUTO_DEPLOY_CA_CA_SIGNED_OPTION_INPUT" == '1' ]; then
            AUTO_DEPLOY_CA_CFG=$REQUEST_DIR/auto-deploy-ca.cfg        
            if [ -z "$CSR_COUNTRY" ]; then getCSRInfo; fi
            defineSANEntries 'rbd' 'Auto Deploy CA'
            generateOpensslConfig $HOSTNAME $AUTO_DEPLOY_CA_CFG 'Auto Deploy'
         else
            logInfo 'User has chosen to generate the Auto Deploy CA private key and CSR from a custom OpenSSL configuration file'
            read -e -p $'\nEnter path to custom OpenSSL configuration file: ' AUTO_DEPLOY_CA_CFG
            while [ ! -f "$AUTO_DEPLOY_CA_CFG" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to custom OpenSSL configuration file:${NORMAL} " AUTO_DEPLOY_CA_CFG; done
            logDetails "$(cat $AUTO_DEPLOY_CA_CFG)"
         fi
         
         generateCSR $AUTO_DEPLOY_CA_CSR $AUTO_DEPLOY_CA_KEY "$AUTO_DEPLOY_CA_CFG" || errorMessage "Unable to generate Certificate Signing Request and Private Key"
		   
         printf "\n\nCertificate Signing Request generated at ${CYAN}${AUTO_DEPLOY_CA_CFG}${NORMAL}"
         printf "\nPrivate Key generated at ${CYAN}${AUTO_DEPLOY_CA_KEY}${NORMAL}\n"

         return 1
      fi
   fi
   
   if [ $AUTO_DEPLOY_CA_REPLACE != 'SELF-SIGNED' ]; then
      task 'Publish CA signing certificates'
      publishCASigningCertificates $TRUSTED_ROOT_CHAIN
   fi
   
   backupFilesystemCertKey '/etc/vmware-rbd/ssl/rbd-ca.crt' '/etc/vmware-rbd/ssl/rbd-ca.key' 'auto-deploy-ca'
   
   task 'Replace certificate on filesystem'
   cp $AUTO_DEPLOY_CA_CERT /etc/vmware-rbd/ssl/rbd-ca.crt > /dev/null 2>&1 || errorMessage 'Unable to update Auto Deploy CA certificate'
   cp $AUTO_DEPLOY_CA_KEY /etc/vmware-rbd/ssl/rbd-ca.key > /dev/null 2>&1 || errorMessage 'Unable to update Auto Deploy CA private key'
   statusMessage 'OK' 'GREEN'      
   
   return 0
}

#------------------------------
# Manage SMS certificates
#------------------------------
function manageSMSCertificates() {
   case $1 in
      'View')
         listSMSCertificates 'View'
         read -p $'\nSelect certificate [Return to Main Menu]: ' VIEW_SMS_CERT_INPUT
		 
         if [ -n "$VIEW_SMS_CERT_INPUT" ]; then
            SMS_CERT=${SMS_CERT_HASHES[$((VIEW_SMS_CERT_INPUT - 1))]}
            viewCertificateInfo "$SMS_CERT" 'view-path'
         fi
         ;;
      'Manage')
         listSMSCertificates 'Manage'
         header 'Manage SMS Certificates'
         echo ' 1. Replace SMS self-signed certificate'
         echo ' 2. Replace SMS VMCA-signed certificate'
         echo ' 3. Add VASA Provider certificate'
         echo ' 4. Remove VASA Provider certificate'
         read -p $'\nEnter selection [Return to Main Menu]: ' MANAGE_SMS_INPUT
		 
         if [ -n "$MANAGE_SMS_INPUT" ]; then
            case "$MANAGE_SMS_INPUT" in
               1)
                  replaceSMSCertificate 'self-signed' 'sms_self_signed'
                  ;;
               2)
                  replaceSMSCertificate 'VMCA-signed' 'sps-extension'
                  ;;
               3)
                  addSMSVASACertificate
                  ;;
               4)
                  removeSMSVASACertificate
                  ;;
            esac
         fi
         ;;
   esac
}

#------------------------------
# Replace SMS certificate
#------------------------------
function replaceSMSCertificate() {
   header "Replace SMS $1 certifificate"
   task "Remove current SMS $1 certificate"
   $VECS_CLI entry delete --store SMS --alias $2 -y > /dev/null 2>&1 || errorMessage 'Unable to delete current SMS certificate' 
   statusMessage 'OK' 'GREEN'
   if [ -z $3 ]; then restartVMwareServices 'vmware-sps'; fi
}

#------------------------------
# List SMS certificates
#------------------------------
function listSMSCertificates() {
   header "$1 Certificates in the SMS VECS Store"
   SMS_CERTS=()
   SMS_CERT_HASHES=()
   SMS_ALIASES=()
   for alias in $($VECS_CLI entry list --store SMS | grep Alias | sed -e 's/Alias[[:space:]]:[[:space:]]//g'); do
      SMS_CERT=$($VECS_CLI entry getcert --store SMS --alias "$alias")
	  SMS_CERT_INFO=$(viewBriefCertificateInfo "$SMS_CERT")
	  SMS_CERT_HASHES+=("$SMS_CERT")
	  SMS_CERTS+=("$SMS_CERT_INFO")
	  SMS_ALIASES+=("$alias")	  
   done
   i=0
   while [ $i -lt "${#SMS_CERTS[@]}" ]; do
      n=$((i+1))
      printf "%2s. %s\n    %s\n\n" $n "Alias: ${SMS_ALIASES[$i]}" "${SMS_CERTS[$i]}"
      ((++i))
   done
}

#------------------------------
# Add new VASA provider cert to SMS store
#------------------------------
function addSMSVASACertificate() {
   read -e -p $'\n\nEnter path to new VASA provider certificate: ' NEW_VASA_INPUT
   while [ ! -f "$NEW_VASA_INPUT" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to new VASA provider certificate:${NORMAL} " NEW_VASA_INPUT; done
   read -p 'Enter alias (usually URL for VASA provider): ' NEW_VASA_ALIAS_INPUT
   
   header 'Add New VASA Provider Certificate'
   task 'Add entry to SMS store in VECS'
   $VECS_CLI entry create --store SMS --cert "$NEW_VASA_INPUT" --alias "$NEW_VASA_ALIAS_INPUT" > /dev/null 2>&1 || errorMessage 'Unable to add VASA provider certificate to SMS store'
   statusMessage 'OK' 'GREEN'
}

#------------------------------
# Remove VASA provider cert from SMS store
#------------------------------
function removeSMSVASACertificate() {
   read -p $'\nEnter the number(s) of the VASA provider certificate(s) to remove (comma-separated list): ' REMOVE_VASA_INPUT
   
   if [ -n "$REMOVE_VASA_INPUT" ]; then
      header 'Remove VASA Provider Certificates'
      for index in $(echo "$REMOVE_VASA_INPUT" | tr -d ' ' | sed 's/,/ /g'); do
         vasa_alias=${SMS_ALIASES[$((index - 1))]}
		 task "$vasa_alias"
		 $VECS_CLI entry delete --store SMS --alias "$vasa_alias" -y > /dev/null 2>&1 || errorMessage "Unable to delete alias '$vasa_alias' from SMS store in VECS"
		 statusMessage 'OK' 'GREEN'
      done
   fi
}

#------------------------------
# Replace data-encipherment certificate
#------------------------------
function replaceDataEnciphermentCertificate() {
   if [ "$VC_VERSION" == '6.5' ]; then return 0; fi

   header 'Replace Data Encipherment certificate'
   
   if checkVECSEntry 'data-encipherment' 'data-encipherment'; then
      $VECS_CLI entry getkey --store data-encipherment --alias data-encipherment 2>/dev/null > $STAGE_DIR/data-encipherment.key
   else
      $CERTOOL --genkey --privkey=$STAGE_DIR/data-encipherment.key --pubkey=$STAGE_DIR/data-encipherment.pub
   fi
   
   task 'Generate new Data Enciphermenet certificate'
   $CERTOOL --server=$PSC_LOCATION --genCIScert --privkey=$STAGE_DIR/data-encipherment.key --dataencipherment --cert=$STAGE_DIR/data-encipherment.crt --Name=data-encipherment --FQDN=$HOSTNAME_LC > /dev/null 2>&1 || errorMessage 'Unable to generate new Data Encipherment certificate'
   statusMessage 'OK' 'GREEN'
   
   updateVECS 'data-encipherment' 
   
   if [ -z $1 ]; then promptRestartVMwareServices 'vmware-vpxd'; fi
}

#------------------------------
# Update Machine SSL thumbprint in Auto Deploy DB
#------------------------------
function updateAutoDeployDB() {
   RBD_STARTUP=$($VMON_CLI -s rbd | grep Starttype | awk '{print $NF}')
   if [ "$RBD_STARTUP" = "AUTOMATIC" ]; then 
      header "Updating Auto Deploy Database"
      MACHINE_SSL_THUMB=$($VECS_CLI entry getcert --store MACHINE_SSL_CERT --alias __MACHINE_CERT | openssl x509 -noout -fingerprint -sha1 2>/dev/null | cut -d'=' -f2)
      if checkService 'vmware-rbd-watchdog'; then 
	     task "Stopping Auto Deploy Service"
		 service-control --stop vmware-rbd-watchdog > /dev/null 2>&1 || errorMessage "Unable to stop Auto Deploy service"
		 statusMessage "OK" "GREEN"
	  fi
      task "Updating Machine SSL thumbprint"
	  sqlite3 /var/lib/rbd/db "update vc_servers set thumbprint = '$MACHINE_SSL_THUMB'" || errorMessage "Unable to update Auto Deploy database"
	  statusMessage "OK" "GREEN"
	  task "Starting Auto Deploy Service"
      service-control --start vmware-rbd-watchdog > /dev/null 2>&1 || errorMessage "Unable to start Auto Deploy service"
	  statusMessage "OK" "GREEN"
   fi
}

#------------------------------
# Checks Machine SSL thumbprint in Auto Deploy DB
#------------------------------
function checkAutoDeployDB() {
   RBD_STARTUP=$($VMON_CLI -s rbd | grep Starttype | awk '{print $NF}')
   if [ "$RBD_STARTUP" = "AUTOMATIC" ]; then
      header "Checking Auto Deploy Database"      
      RBD_VC_THUMBPRINT=$(sqlite3 /var/lib/rbd/db "select thumbprint from vc_servers where addr = '$HOSTNAME' collate nocase")
      MACHINE_SSL_THUMB=$($VECS_CLI entry getcert --store MACHINE_SSL_CERT --alias __MACHINE_CERT | openssl x509 -noout -fingerprint -sha1 2>/dev/null | cut -d'=' -f2)
      task "Machine SSL thumbprint"
      if [ -z "$RBD_VC_THUMBPRINT" ]; then
	      statusMessage "BLANK" "GREEN"
      elif [ "$RBD_VC_THUMBPRINT" != "$MACHINE_SSL_THUMB" ]; then
	      statusMessage "MISMATCH" "YELLOW"
		 
		   if [ -n "$1" ]; then
		      read -p $'\nThe Machine SSL thumbprint is mismatched, update thumbprint? [n]: ' UPDATE_RBD_DB_INPUT
			   if [[ "$UPDATE_RBD_DB_INPUT" =~ ^[Yy] ]]; then updateAutoDeployDB; fi
		   fi
	   else
	      statusMessage "MATCHES" "GREEN"
	   fi
   elif [ -n "$1" ]; then
      header "Checking Auto Deploy Database"      
      echo "${YELLOW}The Auto Deploy service is not set to run automatically${NORMAL}"
   fi
}

#------------------------------
# Replace the VMDir certificate
#------------------------------
function replaceVMDirCert() {
   header 'Replace VMware Directory Service Certificate'
   if [ $VMDIR_REPLACE == 'VMCA-SIGNED' ]; then      
      generateCertoolConfig 'vmdir'
      
      task 'Regenerate VMware Directory certificate'
      regenerateVMCASignedCertificate 'vmdir'
      statusMessage 'OK' 'GREEN'

      VMDIR_CERT=$STAGE_DIR/vmdir.crt
      VMDIR_KEY=$STAGE_DIR/vmdir.key
   else
      echo $'\n1. Generate Certificate Signing Request and Private Key'
      echo '2. Generate Certificate Signing Request and Private Key'
      echo '   from custom OpenSSL configuration file'
      echo '3. Import CA-signed Certificate and Key'
      read -p $'\nChoose option [Return to Main Menu]: ' VMDIR_CA_SIGNED_OPTION

      if [ -z "$VMDIR_CA_SIGNED_OPTION" ]; then return 1; fi

      if [ "${VMDIR_CA_SIGNED_OPTION}" == '3' ]; then
	      logInfo 'User has chosen to import a CA-signed VMware Directory certificate and key'
         read -e -p $'\n'"Provide path to CA-signed ${CYAN}VMware Directory Service${NORMAL} certificate: " VMDIR_CERT_INPUT
         while [ ! -f "$VMDIR_CERT_INPUT" ]; do read -e -p "${YELLOW}File not found, please provide path to the VMware Directory Service certificate:${NORMAL} " VMDIR_CERT_INPUT; done
         getCorrectCertFormat "$VMDIR_CERT_INPUT" 'VMDIR_CERT'

         if ! checkCertSignatureAlgorithm "$(cat $VMDIR_CERT)"; then
            errorMessage 'Certificate is using an unsupported signature algorithm' 'vmdir' 'no-task'
         fi

		   VMDIR_CERT_MODULUS_HASH=$(openssl x509 -noout -modulus -in $VMDIR_CERT 2>/dev/null | md5sum | awk '{print $1}')
         
		   getPrivateKey "$VMDIR_CERT_MODULUS_HASH" 'VMDIR' 'VMware Directory Service'    
         getCAChain "$VMDIR_CERT"            
         
         task 'Verifying certificates and keys: '
         verifyCertAndKey $VMDIR_CERT $VMDIR_KEY
      else
         VMDIR_CSR=$REQUEST_DIR/vmdir-$TIMESTAMP.csr
         VMDIR_KEY=$REQUEST_DIR/vmdir-$TIMESTAMP.key

         if [ "${VMDIR_CA_SIGNED_OPTION}" == '1' ]; then
            VMDIR_CFG=$REQUEST_DIR/vmdir.cfg
            if [ -z "$CSR_COUNTRY" ]; then getCSRInfo; fi
            defineSANEntries 'vmdir' 'VMware Directory'
            generateOpensslConfig $HOSTNAME $VMDIR_CFG 'vmdir'
         else
            logInfo 'User has chosen to generate the VMware Directory private key and CSR from a custom OpenSSL configuration file'
            read -e -p $'\nEnter path to custom OpenSSL configuration file: ' VMDIR_CFG
            while [ ! -f "$VMDIR_CFG" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to custom OpenSSL configuration file:${NORMAL} " VMDIR_CFG; done
         fi
         task "Generating Certificate Signing Request"
         generateCSR $VMDIR_CSR $VMDIR_KEY "$VMDIR_CFG" || errorMessage "Unable to generate Certificate Signing Request and Private Key"
         statusMessage "OK" "GREEN"

         printf "\n\nCertificate Signing Request generated at ${CYAN}${VMDIR_CSR}${NORMAL}"
         printf "\nPrivate Key generated at ${CYAN}${VMDIR_KEY}${NORMAL}\n"

         return 1
      fi
   fi

   if [ $VMDIR_REPLACE != 'VMCA-SIGNED' ]; then
      task 'Publish CA signing certificates'
      publishCASigningCertificates $TRUSTED_ROOT_CHAIN
   fi

   backupFilesystemCertKey '/usr/lib/vmware-vmdir/share/config/vmdircert.pem' '/usr/lib/vmware-vmdir/share/config/vmdirkey.pem' 'VMDir'

   task 'Replace certificate on filesystem'
   cp $VMDIR_CERT /usr/lib/vmware-vmdir/share/config/vmdircert.pem > /dev/null 2>&1 || errorMessage 'Unable to update VMware Directory Services certificate'
   cp $VMDIR_KEY /usr/lib/vmware-vmdir/share/config/vmdirkey.pem > /dev/null 2>&1 || errorMessage 'Unable to update VMware Directory Services private key'
   statusMessage 'OK' 'GREEN'
}

#------------------------------
# Backup certificate and key from VECS
#------------------------------
function backupVECSCertKey() {
   case $1 in
      machine-ssl)
         VECS_STORE='MACHINE_SSL_CERT'
         VECS_ALIAS='__MACHINE_CERT'
         ;;
      *)
         VECS_STORE=$1
         VECS_ALIAS=$1
         ;;
   esac
   
   if [ "$1" == 'machine-ssl' ]; then
      task 'Backing up certificate and private key'
   else
      task "   $1"
   fi
   if $VECS_CLI entry list --store $VECS_STORE | grep $VECS_ALIAS > /dev/null; then
      $VECS_CLI entry getcert --store $VECS_STORE --alias $VECS_ALIAS > $BACKUP_DIR/$1-$TIMESTAMP.crt 2>/dev/null || errorMessage "Unable to backup $1 certificate" 'backup'
      $VECS_CLI entry getkey --store $VECS_STORE --alias $VECS_ALIAS > $BACKUP_DIR/$1-$TIMESTAMP.key 2>/dev/null || errorMessage "Unable to backup $1 private key" 'backup'
      if [ -f $BACKUP_DIR/$1-$TIMESTAMP.crt ] && [ -f $BACKUP_DIR/$1-$TIMESTAMP.key ]; then statusMessage 'OK' 'GREEN'; fi
   else
      statusMessage 'NOT FOUND' 'YELLOW'
   fi
}

#------------------------------
# Replace certificate in VECS
#------------------------------
function updateVECS() {
   case $1 in
      machine-ssl)
         VECS_STORE='MACHINE_SSL_CERT'
         VECS_ALIAS='__MACHINE_CERT'
         VECS_CERT_FILE=$MACHINE_SSL_CERT
         VECS_KEY_FILE=$MACHINE_SSL_KEY
         ;;
      legacy-lookup-service)
         VECS_STORE='STS_INTERNAL_SSL_CERT'
         VECS_ALIAS='__MACHINE_CERT'
         VECS_CERT_FILE=$MACHINE_SSL_CERT
         VECS_KEY_FILE=$MACHINE_SSL_KEY
         ;;
	  data-encipherment)
	     VECS_STORE="$1"
		 VECS_ALIAS="$1"
		 VECS_CERT_FILE=$STAGE_DIR/$1.crt
		 VECS_KEY_FILE=$STAGE_DIR/$1.key
	     ;;
      machine)
         VECS_STORE=$1
         VECS_ALIAS=$1
         VECS_CERT_FILE=$MACHINE_CERT
         VECS_KEY_FILE=$MACHINE_KEY
         ;;
      vpxd)
         VECS_STORE=$1
         VECS_ALIAS=$1
         VECS_CERT_FILE=$VPXD_CERT
         VECS_KEY_FILE=$VPXD_KEY
         ;;
      vpxd-extension)
         VECS_STORE=$1
         VECS_ALIAS=$1
         VECS_CERT_FILE=$VPXD_EXTENSION_CERT
         VECS_KEY_FILE=$VPXD_EXTENSION_KEY
         ;;
      vsphere-webclient)
         VECS_STORE=$1
         VECS_ALIAS=$1
         VECS_CERT_FILE=$VSPHERE_WEBCLIENT_CERT
         VECS_KEY_FILE=$VSPHERE_WEBCLIENT_KEY
         ;;
      wcp)
         VECS_STORE=$1
         VECS_ALIAS=$1
         VECS_CERT_FILE=$WCP_CERT
         VECS_KEY_FILE=$WCP_KEY
         ;;
      hvc)
         VECS_STORE=$1
         VECS_ALIAS=$1
         VECS_CERT_FILE=$HVC_CERT
         VECS_KEY_FILE=$HVC_KEY
         ;;
   esac
   
   if [ "$1" == 'machine-ssl' ] || [ "$1" == 'data-encipherment' ]; then
      task "Updating ${VECS_STORE} certificate"
   else
      task "   $1"
   fi
   if $VECS_CLI entry list --store $VECS_STORE | grep 'Alias :' | grep "$VECS_ALIAS" > /dev/null 2>&1; then
      $VECS_CLI entry delete --store $VECS_STORE --alias $VECS_ALIAS -y > /dev/null 2>&1 || errorMessage "Unable to delete entry $VECS_ALIAS in the VECS store $VECS_STORE"
   fi
   $VECS_CLI entry create --store $VECS_STORE --alias $VECS_ALIAS --cert $VECS_CERT_FILE --key $VECS_KEY_FILE > /dev/null 2>&1 || errorMessage "Unable to create entry $VECS_ALIAS in VECS store $VECS_STORE"
   statusMessage 'OK' 'GREEN'
}

#------------------------------
# Replace certificate in VECS
#------------------------------
function manageSSLTrustAnchors() {
   unset TRUST_ANCHORS_INPUT   
   authenticateIfNeeded
   header 'Manage SSL Trust Anchors'
   echo ' 1. Check SSL Trust Anchors'
   echo ' 2. Update SSL Trust Anchors'
   
   read -p $'\nSelect action [Return to Main Menu]: ' TRUST_ANCHORS_INPUT
   
   case $TRUST_ANCHORS_INPUT in
      1)
         checkSSLTrustAnchors
         ;;
      2)
         SSLTrustAnchorsSelectNode
         updateSSLTrustAnchors
         promptRestartVMwareServices
         ;;
   esac
}

#------------------------------
# List all certificates used as SSL trust anchors
#------------------------------
function checkSSLTrustAnchors() {
   TP_ALGORITHM='sha1'
   TP_REGEX_ITER='19'
   OUTPUT_OPTIONS=''
   header 'Check SSL Trust Anchors'   
   cat << EOF
Additional output options:
 1. None
 2. Show associated Service IDs
 3. Show associated endpoint URIs
 4. Show both associated Service IDs and endpoint URIs
 5. Show the SHA256 fingerprint of the certificates   
EOF
   read -p $'\nPlease select additional information options [1]: ' CHECK_TRUST_ANCHOR_OUTPUT_OPTIONS
   
   case $CHECK_TRUST_ANCHOR_OUTPUT_OPTIONS in
      2)
         OUTPUT_OPTIONS='service-ids'
         ;;
	  
	  3)
         OUTPUT_OPTIONS='endpoints'
         ;;
	  
	  4)
         OUTPUT_OPTIONS='service-ids endpoints'
         ;;
	  
	  5)
         TP_ALGORITHM='sha256'
         TP_REGEX_ITER='31'
         ;;
   esac

   printSSLTrustAnchorInfo "$TP_ALGORITHM" "$TP_REGEX_ITER" "$OUTPUT_OPTIONS"

   getSSODomainNodes
   
   echo ''
   for node in "${SSO_NODES[@]}"; do
      echo "${CYAN}-----Machine SSL Certificate-----${NORMAL}"
      echo "${CYAN}${node}${NORMAL}"
      CURRENT_MACHINE_SSL_CERT_INFO=$(echo | openssl s_client -connect $node:443 2>/dev/null | openssl x509 -text -noout -fingerprint -$TP_ALGORITHM 2>/dev/null | grep -E 'Issuer:|Subject:|Validity|Not Before:|Not After :|Fingerprint' | sed -e 's/SHA[0-9]* Fingerprint/\t&/g' -e "s/Subject:/${GREEN}&${NORMAL}/g" -e "s/[[:xdigit:]]\{2\}\(:[[:xdigit:]]\{2\}\)\{${TP_REGEX_ITER}\}/${YELLOW}&${NORMAL}/g")

      if [ -n "$CURRENT_MACHINE_SSL_CERT_INFO" ]; then
         echo 'Certificate Info:'
         if ! isExpired "$(echo | openssl s_client -connect $node:443 2>/dev/null)" 'hash'; then
            echo "$CURRENT_MACHINE_SSL_CERT_INFO"
         else
            echo "$CURRENT_MACHINE_SSL_CERT_INFO" | sed -e "s/Not Before/${RED}&/"
         fi
      else
         echo "${YELLOW}Unable to get certificate from $node on port 443"
         echo "Please make sure the server is up and the reverse proxy service is running.$NORMAL"
      fi
      echo "${CYAN}---------------------------------${NORMAL}"
   done
}

#------------------------------
# Print SSL trust anchors information
#------------------------------
function printSSLTrustAnchorInfo() {
   CERT_COUNT=1
   TP_ALGORITHM="$1"
   TP_REGEX_ITER="$2"
   OUTPUT_OPTIONS="$3"
   echo -n '' > $STAGE_DIR/trust-anchors.raw
   
   getSSLTrustAnchorHashes

   printf "\n"
   for hash in "${CERT_HASHES[@]}"; do
      echo "${CYAN}-----Endpoint Certificate ${CERT_COUNT}-----${NORMAL}" 
      TEMP_CERT=$(buildCertFromHash "$hash")
      double_encoded_hash=$(echo "$hash" | tr -d '\n' | sed -e 's/.\{76\}/&\r\n/g' | xargs -0 printf "%s\r\n" | base64 -w 0)
      
      if ! isExpired "$TEMP_CERT" 'hash'; then
         DATE_COLOR='NORMAL'
      else
         DATE_COLOR='RED'
      fi
	  
      echo "$TEMP_CERT" | openssl x509 -text -noout -fingerprint -$TP_ALGORITHM 2>/dev/null | grep -E 'Issuer:|Subject:|Validity|Not Before:|Not After :|Fingerprint' | sed -e "s/Not Before/${!DATE_COLOR}&/" -e 's/SHA[0-9]* Fingerprint/\t&/g' -e "s/Subject:/${GREEN}&${NORMAL}/g" -e "s/[[:xdigit:]]\{2\}\(:[[:xdigit:]]\{2\}\)\{${TP_REGEX_ITER}\}/${YELLOW}&${NORMAL}/g"
	  
      if echo "$OUTPUT_OPTIONS" | grep 'service-ids' > /dev/null; then
         USED_BY_SERVICE_IDS=$(getSSLTrustAnchorServiceIds "$hash" "$double_encoded_hash")
         NUM_USED_BY_SERVICE_IDS=$(echo "$USED_BY_SERVICE_IDS" | grep -v '^$' | wc -l)
         echo "Used by $NUM_USED_BY_SERVICE_IDS service registrations:"

         for service in $USED_BY_SERVICE_IDS; do
            echo $'\t'"$service"
         done
	  fi
	  
	  if echo "$OUTPUT_OPTIONS" | grep 'endpoints' > /dev/null; then
         USED_BY_ENDPOINTS=$(getSSLTrustAnchorEndpoints "$hash" "$double_encoded_hash")
         NUM_USED_BY_ENDPOINTS=$(echo "$USED_BY_ENDPOINTS" | grep -v '^$' | wc -l)         
         echo "Used by $NUM_USED_BY_ENDPOINTS endpoints:"
         
         for endpoint in $USED_BY_ENDPOINTS; do
            echo $'\t'"$endpoint"
         done
	  fi
	  echo "${CYAN}--------------------------------${NORMAL}"
      ((++CERT_COUNT))
   done
}

#------------------------------
# Get certificate hashes of unique SSL Trust Anchor
#------------------------------
function getSSLTrustAnchorHashes() {
   CERT_HASHES=()
   LDAP_SEARCH_RESULTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=Sites,cn=Configuration,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(|(objectclass=vmwLKUPEndpointRegistration)(objectclass=vmwLKUPServiceEndpoint))' vmwLKUPEndpointSslTrust vmwLKUPSslTrustAnchor vmwLKUPURI)
   
   IFS=$'\n'
   dn=''
   attrs=''
   while read line; do
      if [ -n "$line" ]; then
         if [[ "$line" =~ ^dn: ]]; then
	        dn="$line"
	     else
	        if [ -z "$attrs" ]; then 
		       attrs+="$line"
	        else
		       attrs+=$'\n'"$line"
		    fi
	     fi
      else
         echo "$dn" >> $STAGE_DIR/trust-anchors.raw
         echo "$attrs" | sort >> $STAGE_DIR/trust-anchors.raw
         dn=''
         attrs=''
      fi
   done <<<"$LDAP_SEARCH_RESULTS"
   IFS=$' \t\n'
   
   TRUST_ANCHORS=$(cat $STAGE_DIR/trust-anchors.raw | grep -vE '^dn:|^vmwLKUPURI' | awk -F': ' '{print $NF}' | sort | uniq)
   
   for cert in $TRUST_ANCHORS; do 
      if [[ "$cert" =~ ^TUl ]]; then
         CURRENT_CERT=$(echo $cert | base64 --decode | tr -d '\r\n')
      else
         CURRENT_CERT=($cert)
      fi
      if [[ ! "${CERT_HASHES[@]}" =~ "$CURRENT_CERT" ]]; then
         CERT_HASHES+=($CURRENT_CERT)
      fi
   done
}

#------------------------------
# Get the service IDs using a unique SSL Trust Anchor
#------------------------------
function getSSLTrustAnchorServiceIds() {
   USED_BY_SERVICE_IDS=$(cat $STAGE_DIR/trust-anchors.raw | grep -B1 $1 | grep '^dn:' | awk -F',' '{print $2}' | sed -e 's/cn=//g' | sort | uniq)
   USED_BY_SERVICE_IDS+=$'\n'$(cat $STAGE_DIR/trust-anchors.raw | grep -B1 $2 | grep '^dn:' | awk -F',' '{print $2}' | sed -e 's/cn=//g' | sort | uniq | xargs -0 printf "\n%s")
   
   echo "$USED_BY_SERVICE_IDS"
}

#------------------------------
# Get the endpoint URIs using a unique SSL Trust Anchor
#------------------------------
function getSSLTrustAnchorEndpoints() {
   USED_BY_ENDPOINTS=$(cat $STAGE_DIR/trust-anchors.raw | grep -A1 $1 | grep '^vmwLKUPURI' | sed -e 's/vmwLKUPURI: //g' | sort | uniq)              
   USED_BY_ENDPOINTS+=$'\n'$(cat $STAGE_DIR/trust-anchors.raw | grep -A1 $2 | grep '^vmwLKUPURI' | sed -e 's/vmwLKUPURI: //g' | sort | uniq)
   
   echo "$USED_BY_ENDPOINTS"
}

#------------------------------
# Get the PSC and vCenter nodes in an SSO Domain
#------------------------------
function getSSODomainNodes() {
   SSO_NODES=()
   PSC_NODES=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "ou=Domain Controllers,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=computer)' cn | grep '^cn:' | awk '{print $NF}')
   PSC_COUNT=$(echo "$PSC_NODES" | wc -l)
   VCENTER_NODES=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "ou=Computers,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=computer)' cn | grep '^cn:' | awk '{print $NF}')
   VCENTER_COUNT=$(echo "$VCENTER_NODES" | wc -l)
   
   for psc_node in "$PSC_NODES"; do
      if [[ ! "${SSO_NODES[@]}" =~ "$psc_node" ]]; then SSO_NODES+=($psc_node); fi
   done

   for vc_node in "$VCENTER_NODES"; do
      if [[ ! "${SSO_NODES[@]}" =~ "$vc_node" ]]; then SSO_NODES+=($vc_node); fi
   done
}

#------------------------------
# Select which node to update SSL trust anchors
#------------------------------
function SSLTrustAnchorsSelectNode() {
   getSSODomainNodes
   
   NODE_COUNTER=1
   NODE_DEFAULT=1
   PSC_VIP_COUNTER=0   

   printf "\nNodes in SSO domain '$SSO_DOMAIN'\n"
   
   for node in "${SSO_NODES[@]}"; do
      echo " $NODE_COUNTER. $node"
      if [ $HOSTNAME = $node ]; then NODE_DEFAULT=$NODE_COUNTER; fi
      ((++NODE_COUNTER))
   done
   
   if [[ $VCENTER_COUNT -gt 0 && $PSC_COUNT -gt 1 ]]; then
      echo " $NODE_COUNTER. FQDN of PSC Load Balancer"
      PSC_VIP_COUNTER=$NODE_COUNTER
   fi
   
   echo ' C. Custom hostname or IP address'
   
   read -p $'\n'"Select node to update [${NODE_DEFAULT}]: " NODE_SELECT

   if [ -z $NODE_SELECT ]; then
      NODE_FQDN=${SSO_NODES[$((NODE_DEFAULT - 1))]}
      NODE_SSL_FQDN=$NODE_FQDN
   else
      if [[ $PSC_VIP_COUNTER -gt 0 && "${NODE_SELECT}" == "$PSC_VIP_COUNTER" ]]; then
         read -p 'Enter the FQDN of the PSC Load Balancer: ' PSC_LB_FQDN
         while [ -z $PSC_LB_FQDN ]; do
            read -p 'Enter the FQDN of the PSC Load Balancer: ' PSC_LB_FQDN
         done
         NODE_FQDN=$PSC_LB_FQDN
         NODE_SSL_FQDN=$NODE_FQDN
      elif [[ "$NODE_SELECT" =~ ^[Cc] ]]; then
         echo $'\n'"${YELLOW}Note: This operation is used when the endpoint URIs refer to a hostname or IP address"
         echo "other than the target vCenter/PSC hostname or IP address. These situations are very uncommon."
         echo "Only use this option at the direction of VMware Global Support.${NORMAL}"
         read -p $'\nEnter hostname or IP address of registration endpoint URIs to update: ' CUSTOM_NODE_SELECT
         while [ -z "$CUSTOM_NODE_SELECT" ]; do read -p $'\n'"${YELLOW}Enter hostname or IP address of registration endpoint URIs to update:${NORMAL} " CUSTOM_NODE_SELECT; done
         NODE_FQDN="$CUSTOM_NODE_SELECT"
         read -p $'\n'"Enter the hostname or IP address of the node serving the SSL certificate to update [$NODE_FQDN]: " NODE_SSL_FQDN_INPUT
         if [ -z $NODE_SSL_FQDN_INPUT ]; then
            NODE_SSL_FQDN=$NODE_FQDN
         else
            NODE_SSL_FQDN=$NODE_SSL_FQDN_INPUT
         fi
      else
         NODE_FQDN=${SSO_NODES[$((NODE_SELECT - 1))]}
         NODE_SSL_FQDN=$NODE_FQDN
      fi
   fi

   logInfo "User has selected '$NODE_FQDN'"
   logInfo "SSL certificate to update will be obtained from $NODE_SSL_FQDN:443"

   echo | openssl s_client -connect $NODE_SSL_FQDN:443 2>/dev/null | openssl x509 > $STAGE_DIR/trust-anchor-machine-ssl.crt 2>/dev/null
}

#------------------------------
# Setup environment to update SSL trust anchors for the current node
#------------------------------
function SSLTrustAnchorSelf() {
   openssl x509 -in $MACHINE_SSL_CERT >  $STAGE_DIR/trust-anchor-machine-ssl.crt 2>/dev/null
   NODE_FQDN="$PNID"
}

#------------------------------
# Update the SSL trust anchors
#------------------------------
function updateSSLTrustAnchors() {
   TOTAL_SERVICES_UPDATED=0
   header "Update SSL Trust Anchors ($NODE_FQDN)"

   find $STAGE_DIR -type f -iname 'ls-service-reg-*.ldif' -exec rm {} \;

   if [ "$VMDIR_FQDN" != "$PNID" ]; then
      read -r -s -p $'\n'"Enter the root password for $PSC_LOCATION: " SSHPASS
      PSC_INFO=$(sshpass -p "$SSHPASS" ssh -q -o StrictHostKeyChecking=no -t -t root@$PSC_LOCATION "/opt/likewise/bin/lwregshell list_values '[HKEY_THIS_MACHINE\services\vmdir]' | grep -E 'dcAccountPassword|dcAccountDN'" | grep 'dcAccount')
      
      logInfo "PSC info is: $PSC_INFO"
      
      if [ -z "$PSC_INFO" ]; then
         echo $'\n\n'"${YELLOW}Unable to get machine account password for $PSC_LOCATION."
         echo $'\n'"This is usually because the default shell on the PSC is /bin/appliancesh instead of /bin/bash"
         echo $'\n'"Please change the default shell on $PSC_LOCATION,"
         echo "or run this script on $PSC_LOCATION to update the SSL trust anchors.${NORMAL}"
         
         return 1
      fi
      
      UPDATE_MACHINE_PASSWORD=$(echo "$PSC_INFO" | grep 'dcAccountPassword' | awk -F"  " '{print $NF}' | awk '{print substr($0,2,length($0)-3)}' | sed -e 's/\\"/"/g' -e 's/\\\\/\\/g')
      UPDATE_MACHINE_ACCOUNT_DN=$(echo "$PSC_INFO" | grep 'dcAccountDN' | awk -F"  " '{print $NF}' | awk '{print substr($0,2,length($0)-3)}')
      printf "\n\n"
   else
      UPDATE_MACHINE_ACCOUNT_DN=$VMDIR_MACHINE_ACCOUNT_DN
      UPDATE_MACHINE_PASSWORD=$VMDIR_MACHINE_PASSWORD
   fi

   echo -n "$UPDATE_MACHINE_PASSWORD" > $STAGE_DIR/.update-machine-account-password
   chmod 640 $STAGE_DIR/.update-machine-account-password
   cat $STAGE_DIR/trust-anchor-machine-ssl.crt | grep -vE '^-----' | tr -d '\n' > $STAGE_DIR/trust-anchor-machine-ssl.hash
   openssl x509 -outform der -in $STAGE_DIR/trust-anchor-machine-ssl.crt -out $STAGE_DIR/trust-anchor-machine-ssl.der 2>/dev/null
   SERVICE_REGISTRATION_DNS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=Sites,cn=Configuration,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password "(&(|(vmwLKUPURI=https://$NODE_FQDN:*)(vmwLKUPURI=https://$NODE_FQDN/*))(|(objectclass=vmwLKUPServiceEndpoint)(objectclass=vmwLKUPEndpointRegistration)))" vmwLKUPEndpointSslTrust vmwLKUPSslTrustAnchor | grep '^dn:' | sed -r 's/cn=Endpoint[0-9]+,//g' | sed -e 's/dn: //g' -e 's/, cn=/,cn=/g' | sort | uniq)
   logInfo 'Service Registration DNs to update:'
   logDetails "'$SERVICE_REGISTRATION_DNS'"
   SSO_ALL_SITES=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -b "cn=Sites,cn=Configuration,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password -s one '(objectclass=*)' cn | grep '^cn:' | awk -F': ' '{print $NF}')

   for svc_dn in $SERVICE_REGISTRATION_DNS; do
      LEGACY_REGISTRATION=0
      
      for site in $SSO_ALL_SITES; do
         SVC_LOWER=$(echo "$svc_dn" | awk -F',' '{print $1}' | awk -F'=' '{print $2}' | tr '[:upper:]' '[:lower:]')
         SITE_LOWER=$(echo "$site" | tr '[:upper:]' '[:lower:]')
         if [[ $SVC_LOWER =~ ^$SITE_LOWER: ]]; then LEGACY_REGISTRATION=1; fi
      done
      
      logInfo "Updating service $svc_dn"      
      if [ $LEGACY_REGISTRATION = 1 ]; then
         updateLegacySSLTrustAnchorTargeted $svc_dn
      else
         updateSSLTrustAnchorTargeted $svc_dn
      fi
   done

   echo "Updated $TOTAL_SERVICES_UPDATED service(s)"

   UPDATED_TRUST_ANCHORS=1

   return 0
}

#------------------------------
# Update a legacy SSL trust anchor
#------------------------------
function updateLegacySSLTrustAnchorTargeted() {
   SERVICE_DN=$1
   SERVICE_ID=$(echo "$SERVICE_DN" | awk -F',' '{print $1}' | awk -F'=' '{print $2}')   
   ENDPOINT_INFO=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "$SERVICE_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(|(objectclass=vmwLKUPServiceEndpoint)(objectclass=vmwLKUPEndpointRegistration))' vmwLKUPEndpointSslTrust vmwLKUPSslTrustAnchor 2>/dev/null | sed -e 's/, cn=/,cn=/g' -e '/^$/d')
   
   IFS=$'\n'
   for line in $ENDPOINT_INFO; do
      if [[ $line =~ ^dn: ]]; then
         CURRENT_DN=$line
      elif [[ $line =~ ^vmwLKUP ]]; then
         echo "$CURRENT_DN" >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif
         echo 'changetype: modify' >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif
         if echo $line | grep 'vmwLKUPSslTrustAnchor' > /dev/null; then
            echo 'replace: vmwLKUPSslTrustAnchor' >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif
            echo "vmwLKUPSslTrustAnchor:< file://$STAGE_DIR/trust-anchor-machine-ssl.der" >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif
         else
            echo 'replace: vmwLKUPEndpointSslTrust' >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif
            echo "vmwLKUPEndpointSslTrust:< file://$STAGE_DIR/trust-anchor-machine-ssl.hash" >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif
         fi
         echo '' >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif
      fi
   done
   IFS=$' \t\n'
   if [ -f $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif ]; then
      logInfo "Updating legacy Lookup Service entry: $1"
      logDebug "Contents of LDIF file $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif"
      logDebugDetails "'$(cat $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif)'"
      echo "Updating service: ${SERVICE_ID}"
      if ! $LDAP_MODIFY -v -h $PSC_LOCATION -p $VMDIR_PORT -D "${UPDATE_MACHINE_ACCOUNT_DN}" -y $STAGE_DIR/.update-machine-account-password -f $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif > /dev/null 2>&1; then
         echo 'Error updating service: please check logs for details'
         logError "Error updating service $1, ldapmodify return code: $?"
      else
         logInfo "Service $1 successfully updated"
         ((++TOTAL_SERVICES_UPDATED))
      fi
   fi
}

#------------------------------
# Update an SSL trust anchor
#------------------------------
function updateSSLTrustAnchorTargeted() {
   SERVICE_DN=$1
   SERVICE_ID=$(echo "$SERVICE_DN" | awk -F',' '{print $1}' | awk -F'=' '{print $2}')
   ENDPOINT_INFO=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "$SERVICE_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwLKUPEndpointRegistration)' vmwLKUPEndpointSslTrust 2>/dev/null | sed -e 's/, cn=/,cn=/g' -e '/^$/d')
   
   IFS=$'\n'   
   for line in $ENDPOINT_INFO; do
      if [[ $line =~ ^dn: ]]; then
         CURRENT_DN=$line
      elif [[ $line =~ ^vmwLKUPEndpointSslTrust: ]]; then
         echo "$CURRENT_DN" >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif
         echo 'changetype: modify' >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif
         echo 'replace: vmwLKUPEndpointSslTrust' >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif
         echo "vmwLKUPEndpointSslTrust:< file://$STAGE_DIR/trust-anchor-machine-ssl.hash" >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif
         echo '' >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif
      fi
   done
   IFS=$' \t\n'
   if [ -f $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif ]; then
      logInfo "Updating Lookup Service entry: $1"
      logDebug "Contents of LDIF file $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif"
      logDebugDetails "'$(cat $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif)'"
      echo "Updating service: $SERVICE_ID"
      if ! $LDAP_MODIFY -v -h $PSC_LOCATION -p $VMDIR_PORT -D "$UPDATE_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.update-machine-account-password -f $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif > /dev/null 2>&1; then
         echo 'Error updating service: please check logs for details'
         logError "Error updating service $1, ldapmodify return code: $?"
      else
         logInfo "Service $1 successfully updated"
         ((++TOTAL_SERVICES_UPDATED))
      fi
   fi
}

#------------------------------
# Print configuration check menu
#------------------------------
function checkConfigurationMenu() {
   unset CONFIGURATION_CHECK_INPUT
   header "Configuration Check Menu"
   cat << EOF
 1. Check for SSL Interception
 2. Check STS server certificate configuration
 3. Check SSO Smart Card configuration options
 4. Check VECS store status and permissions
 5. Check Auto Deploy configuration 
EOF
   read -p $'\nSelect an option [Return to Main Menu]: ' CONFIGURATION_CHECK_INPUT
   
   case $CONFIGURATION_CHECK_INPUT in
      1)
         checkSSLInterception
         ;;	  
      2)
         if [ $NODE_TYPE != 'management' ]; then
            checkSTSConfig
         else
            echo $'\n'"${YELLOW}This operation must be done on the Platform Services Controller${NORMAL}"$'\n'
         fi
         ;;	  
      3)
         checkSmartCardOptions
         ;;	  
      4)
         checkVECSStores
         ;;
      5)
         if [ $NODE_TYPE != 'infrastructure' ]; then checkAutoDeployDB 'check'; fi
         ;;
   esac
}

#------------------------------
# Check if SSL Interception is in play
#------------------------------
function checkSSLInterception() {
   header 'Checking for SSL Interception'
   task 'Checking hostupdate.vmware.com'
   HOSTUPDATE_ISSUER=$(echo | openssl s_client -connect hostupdate.vmware.com:443 2>/dev/null | openssl x509 -noout -issuer 2>/dev/null | awk -F'/' '{for(i=1; i<=NF;i++) if($i ~ /^CN/) {print $i}}' |  sed 's/CN=//')
   
   if [ -n "$HOSTUPDATE_ISSUER" ]; then
      statusMessage 'OK' 'GREEN'
      if [ "$HOSTUPDATE_ISSUER" != "$HOSTUPDATE_ISSUER_EXPECTED" ]; then
	     unset DOWNLOAD_PROXY_CA_CERTS_INPUT
         echo $'\n'"Issuing CA for hostupdate.vmware.com is ${YELLOW}${HOSTUPDATE_ISSUER}${NORMAL}"
         echo "The expected issuer is ${GREEN}${HOSTUPDATE_ISSUER_EXPECTED}${NORMAL}"
         echo $'\n'"${YELLOW}SSL Interception is likely taking place.${NORMAL}"
         
         header "SSL Interception Certificate Management"
         echo " 1. Download and import CA certificates from the proxy"
         echo " 2. Import CA certificate(s) from a file"

         read -p $'\nSelect an option [Return to Main Menu]: ' PROXY_CA_CERTS_INPUT
         
         case $PROXY_CA_CERTS_INPUT in
            1)
               downloadProxyCACerts
               ;;
            2)
               provideProxyCACerts
               ;;
         esac
      else
         echo $'\n'"Issuing CA for hostupdate.vmware.com is ${GREEN}${HOSTUPDATE_ISSUER}${NORMAL}"$'\n'
      fi
   else
      statusMessage 'ERROR' 'YELLOW'
      echo $'\n'"${YELLOW}Could not identify the issuer of the certificate for hostupdate.vmware.com"
      echo "Check your network connection and try again.${NORMAL}"$'\n'
   fi
}

#------------------------------
# Download CA certs from proxy used for SSL Interception
#------------------------------
function downloadProxyCACerts() {
   authenticateIfNeeded
   if [ -f $STAGE_DIR/proxy-ca-chain.pem ]; then rm $STAGE_DIR/proxy-ca-chain.pem; fi
   header "Processing Proxy CA certificates"   
   task 'Downloadng certificate chain from the proxy'
   echo | openssl s_client -connect hostupdate.vmware.com:443 2>/dev/null -showcerts | sed -n '/^-----BEGIN CERTIFICATE-----/,/^-----END CERTIFICATE-----/p' | csplit -z -f $STAGE_DIR/proxy-cert- -b%02d.crt /dev/stdin '/-----BEGIN CERTIFICATE-----/' '{*}' 2>&1 | logDebug
   if [ "$(ls -l $STAGE_DIR/proxy-cert* 2>/dev/null)" != '' ]; then
      statusMessage 'OK' 'GREEN'
      for cert in $(ls $STAGE_DIR/proxy-cert-* 2>/dev/null); do 
         if isCertCA "$(cat $cert)"; then cat $cert >> $STAGE_DIR/proxy-ca-chain.pem; fi
      done
      if [ -f $STAGE_DIR/proxy-ca-chain.pem ]; then
         importProxyCACerts

         NUM_PROXY_CA_CERTS=$(ls -l $STAGE_DIR/proxy-cert-* | wc -l)
         CERT_FILE_INDEX=$(printf "%02d" $((NUM_PROXY_CA_CERTS-1)))
         LAST_PROXY_CA_SUBJECT=$(openssl x509 -noout -subject -in $STAGE_DIR/proxy-cert-$CERT_FILE_INDEX.crt 2>/dev/null | sed -e 's/subject= //')
         LAST_PROXY_CA_ISSUER=$(openssl x509 -noout -issuer -in $STAGE_DIR/proxy-cert-$CERT_FILE_INDEX.crt 2>/dev/null | sed -e 's/issuer= //')
            
         if [ "$LAST_PROXY_CA_SUBJECT" != "$LAST_PROXY_CA_ISSUER" ]; then
            echo $'\n'"${YELLOW}There proxy does not provide the Root CA certificate in the chain."
            echo "Please aquire this certificate and import it.${NORMAL}"$'\n'
         fi
      else
         echo $'\n'"${YELLOW}There proxy does not appear to provide any of the CA certificates."
         echo "Please aquire these certificates and import them.${NORMAL}"$'\n'
      fi      
   fi
}

#------------------------------
# Manually provide CA certs that signed proxy certificate used for SSL Interception
#------------------------------
function provideProxyCACerts() {
   authenticateIfNeeded
   if [ -f $STAGE_DIR/proxy-ca-chain.pem ]; then rm $STAGE_DIR/proxy-ca-chain.pem; fi

   read -e -p $'\n'"Enter path to CA certificate (or chain): " PROXY_CA_CERT_INPUT
   while [ ! -f $PROXY_CA_CERT_INPUT ]; do read -e -p $'\n'"${YELLOW}Enter path to CA certificate (or chain):${NORMAL} " PROXY_CA_CERT_INPUT; done

   getCorrectCertFormat "$PROXY_CA_CERT_INPUT" 'PROXY_CA_CERT_FILE'
   csplit -s -z -f $STAGE_DIR/proxy-cert- -b%02d.crt $PROXY_CA_CERT_FILE '/-----BEGIN CERTIFICATE-----/' '{*}'

   if [ "$(ls -l $STAGE_DIR/proxy-cert* 2>/dev/null)" != '' ]; then
      header "Processing Proxy CA certificates"
      task "Checking CA file"
      for cert in $(ls $STAGE_DIR/proxy-cert-* 2>/dev/null); do 
         if isCertCA "$(cat $cert)"; then cat $cert >> $STAGE_DIR/proxy-ca-chain.pem; fi
      done
      if [ -f $STAGE_DIR/proxy-ca-chain.pem ]; then
         statusMessage "OK" "GREEN"
         importProxyCACerts
      else
         errorMessage "No CA certificates found in $PROXY_CA_CERT_INPUT"
      fi
   fi
}

function importProxyCACerts() {
   if [ -f $STAGE_DIR/proxy-ca-chain.pem ]; then
      task 'Publishing certificates to VMware Directory'
      $DIR_CLI trustedcert publish --chain --cert $STAGE_DIR/proxy-ca-chain.pem --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>&1 | logInfo || errorMessage 'Unable to publish proxy CA certificates to VMware Directory'
      statusMessage 'OK' 'GREEN'
      task 'Refreshing CA certificates to VECS'
      $VECS_CLI force-refresh || errorMessage 'Unable to refresh CA certificates in VECS'
      statusMessage 'OK' 'GREEN'

      if [[ "$VC_VERSION" =~ ^[78] ]] && [[ $VC_BUILD -ge 17327517 ]]; then
         if [ ! -f /usr/lib/python3.7/site-packages/certifi/cacert.pem.backup ]; then
            task "Backup Python CA store"
            cp /usr/lib/python3.7/site-packages/certifi/cacert.pem /usr/lib/python3.7/site-packages/certifi/cacert.pem.backup 2>/dev/null || errorMessage "Could not backup the Python CA store at /usr/lib/python3.7/site-packages/certifi/cacert.pem"
            statusMessage "OK" "GREEN"
         fi
         task 'Adding certificates to python CA store'
         cat $STAGE_DIR/proxy-ca-chain.pem >> /usr/lib/python3.7/site-packages/certifi/cacert.pem
         statusMessage 'OK' 'GREEN'

         NUM_PROXY_CERTS_PEM=$(grep 'END CERTIFICATE' $STAGE_DIR/proxy-ca-chain.pem | wc -l)
         NUM_PROXY_CERTS_KEYSTORE=$(keytool -list -keystore /usr/java/jre-vmware/lib/security/cacerts -storepass changeit 2>/dev/null | grep sslproxyca | wc -l)

         if [ ! -f /usr/java/jre-vmware/lib/security/cacerts.backup ]; then
            task "Backup Java keystore"
            cp /usr/java/jre-vmware/lib/security/cacerts /usr/java/jre-vmware/lib/security/cacerts.backup 2>/dev/null || errorMessage "Could not backup the Java keystore at /usr/java/jre-vmware/lib/security/cacerts"
            statusMessage "OK" "GREEN"
         fi
         for N in $(seq 0 $(($NUM_PROXY_CERTS_PEM - 1))); do
            SSL_PROXY_CA_ALIAS="sslproxyca$(($NUM_PROXY_CERTS_KEYSTORE + $N))"
            task "Import $SSL_PROXY_CA_ALIAS to Java keystore"
            cat $STAGE_DIR/proxy-ca-chain.pem | awk "n==$N { print }; /END CERTIFICATE/ { n++ }" | keytool -noprompt -import -file /dev/stdin -trustcacerts -alias "$SSL_PROXY_CA_ALIAS" -storepass changeit -keystore /usr/java/jre-vmware/lib/security/cacerts 2>&1 | logDebug
            statusMessage "OK" "GREEN"
         done
      fi
   fi
}

#------------------------------
# Quick check of the STS server configuration
#------------------------------
function quickCheckSTSConfig() {
   header 'Checking STS Server Configuration'
   task 'Check STS VECS store configuration'
   if quickCheckSTSCertConfig; then
      statusMessage 'OK' 'GREEN'
   else
      statusMessage 'LEGACY' 'YELLOW'
      CERT_STATUS_STS_VECS_CONFIG=1
   fi

   task 'Check STS ConnectionStrings configuration'
   if quickCheckSTSConnectionStrings; then
      statusMessage 'OK' 'GREEN'
   else
      statusMessage 'MISCONFIG' 'YELLOW'
         CERT_STATUS_STS_CONNECTION_STRINGS=1
   fi
}

#------------------------------
# Quick check of the STS server VECS configuration
#------------------------------
function quickCheckSTSCertConfig() {
   STS_LOCALHOST_CONNECTOR_STORE=$(grep 'bio-ssl-localhost.https.port' /usr/lib/vmware-sso/vmware-sts/conf/server.xml | grep 'store=' | awk '{for(i=1;i<=NF;i++) if($i ~ /^store/) {print $i}}' | tr -d '>' | awk -F'=' '{print $NF}' | tr -d '"')
   STS_LOCALHOST_CERTIFICATE_STORE=$(grep -A2 'bio-ssl-localhost.https.port' /usr/lib/vmware-sso/vmware-sts/conf/server.xml | grep 'certificateKeystoreFile=' | awk '{for(i=1;i<=NF;i++) if($i ~ /^certificateKeystoreFile/) {print $i}}' | tr -d '>' | awk -F'=' '{print $NF}' | tr -d '"')
   STS_CLIENTAUTH_CONNECTOR_STORE=$(grep 'bio-ssl-clientauth.https.port' /usr/lib/vmware-sso/vmware-sts/conf/server.xml | grep 'store=' | awk '{for(i=1;i<=NF;i++) if($i ~ /^store/) {print $i}}' | tr -d '>' | awk -F'=' '{print $NF}' | tr -d '"')
   STS_CLIENTAUTH_CERTIFICATE_STORE=$(grep -A2 'bio-ssl-clientauth.https.port' /usr/lib/vmware-sso/vmware-sts/conf/server.xml | grep 'certificateKeystoreFile=' | awk '{for(i=1;i<=NF;i++) if($i ~ /^certificateKeystoreFile/) {print $i}}' | tr -d '>' | awk -F'=' '{print $NF}' | tr -d '"')
   
   if [[ "$VC_VERSION" =~ ^8 ]]; then
      if [ "$STS_LOCALHOST_CONNECTOR_STORE" != 'STS_INTERNAL_SSL_CERT' ] && [ "$STS_LOCALHOST_CERTIFICATE_STORE" != 'STS_INTERNAL_SSL_CERT' ] && [ "$STS_CLIENTAUTH_CONNECTOR_STORE" != 'STS_INTERNAL_SSL_CERT' ] && [ "$STS_CLIENTAUTH_CERTIFICATE_STORE" != 'STS_INTERNAL_SSL_CERT' ]; then
         return 0
      else
         return 1
      fi
   else
      if [ "$STS_LOCALHOST_CONNECTOR_STORE" != 'STS_INTERNAL_SSL_CERT' ] && [ "$STS_LOCALHOST_CERTIFICATE_STORE" != 'STS_INTERNAL_SSL_CERT' ]; then
         return 0
      else
         return 1
      fi
   fi
}

#------------------------------
# Check STS Connection String for ELM configurations
#------------------------------
function quickCheckSTSConnectionStrings() {
   STS_CONNECTION_STRINGS=$($LDAP_SEARCH -LLL -o ldif-wrap=no -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$SSO_DOMAIN,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password vmwSTSConnectionStrings | grep vmwSTSConnectionStrings | awk '{print $2}')
   NUM_PSC_NODES=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "ou=Domain Controllers,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=computer)' cn | grep '^cn:' | wc -l)
   logInfo "vmwSTSConnectionStrings value: $STS_CONNECTION_STRINGS"
   if [ "$STS_CONNECTION_STRINGS" == "ldap://localhost:$VMDIR_PORT" ]; then
      return 0
   else
      if [ "$NUM_PSC_NODES" -gt 1 ]; then
         return 1
      else
         return 0
      fi
   fi   
}

#------------------------------
# Check of the STS server configuration
#------------------------------
function checkSTSConfig() {
   authenticateIfNeeded

   checkSTSCertConfig

   checkSTSConnectionStrings
}

#------------------------------
# Check configuration of the STS server
#------------------------------
function checkSTSCertConfig() {
   VECS_STORES_TO_REPLACE='STS_INTERNAL_SSL_CERT'
   UPDATE_STS_CONFIG=0
   header 'Checking STS server configuration'   
   task 'Checking VECS store configuration'
   #STS_LOCALHOST_CONNECTOR_STORE=$(grep 'bio-ssl-localhost.https.port' /usr/lib/vmware-sso/vmware-sts/conf/server.xml | grep 'store=' | awk '{for(i=1;i<=NF;i++) if($i ~ /^store/) {print $i}}' | tr -d '>' | awk -F'=' '{print $NF}' | tr -d '"')
   #STS_LOCALHOST_CERTIFICATE_STORE=$(grep -A2 'bio-ssl-localhost.https.port' /usr/lib/vmware-sso/vmware-sts/conf/server.xml | grep 'certificateKeystoreFile=' | awk '{for(i=1;i<=NF;i++) if($i ~ /^certificateKeystoreFile/) {print $i}}' | tr -d '>' | awk -F'=' '{print $NF}' | tr -d '"')
   #STS_CLIENTAUTH_CONNECTOR_STORE=$(grep 'bio-ssl-clientauth.https.port' /usr/lib/vmware-sso/vmware-sts/conf/server.xml | grep 'store=' | awk '{for(i=1;i<=NF;i++) if($i ~ /^store/) {print $i}}' | tr -d '>' | awk -F'=' '{print $NF}' | tr -d '"')
   #STS_CLIENTAUTH_CERTIFICATE_STORE=$(grep -A2 'bio-ssl-clientauth.https.port' /usr/lib/vmware-sso/vmware-sts/conf/server.xml | grep 'certificateKeystoreFile=' | awk '{for(i=1;i<=NF;i++) if($i ~ /^certificateKeystoreFile/) {print $i}}' | tr -d '>' | awk -F'=' '{print $NF}' | tr -d '"')
   STS_LOCALHOST_CONNECTOR_STORE=$(xmllint --xpath 'string(//Server/Service/Connector[@port="${bio-ssl-localhost.https.port}"]/@store)' /usr/lib/vmware-sso/vmware-sts/conf/server.xml)
   STS_LOCALHOST_CERTIFICATE_STORE=$(xmllint --xpath 'string(//Server/Service/Connector[@port="${bio-ssl-localhost.https.port}"]/SSLHostConfig/Certificate/@certificateKeystoreFile)' /usr/lib/vmware-sso/vmware-sts/conf/server.xml)
   STS_CLIENTAUTH_CONNECTOR_STORE=$(xmllint --xpath 'string(//Server/Service/Connector[@port="${bio-ssl-clientauth.https.port}"]/@store)' /usr/lib/vmware-sso/vmware-sts/conf/server.xml)
   STS_CLIENTAUTH_CERTIFICATE_STORE=$(xmllint --xpath 'string(//Server/Service/Connector[@port="${bio-ssl-clientauth.https.port}"]/SSLHostConfig/Certificate/@certificateKeystoreFile)' /usr/lib/vmware-sso/vmware-sts/conf/server.xml)
   statusMessage 'OK' 'GREEN'
   
   if [[ "$VC_VERSION" =~ ^8 ]] || ([[ "$VC_VERSION" =~ ^7 ]] && [ $VC_BUILD -ge 20845200 ]); then
      if [ "$STS_LOCALHOST_CONNECTOR_STORE" == 'MACHINE_SSL_CERT' ] && [ "$STS_LOCALHOST_CERTIFICATE_STORE" == 'MACHINE_SSL_CERT' ] && [ "$STS_CLIENTAUTH_CONNECTOR_STORE" == 'MACHINE_SSL_CERT' ] && [ "$STS_CLIENTAUTH_CERTIFICATE_STORE" == 'MACHINE_SSL_CERT' ]; then
         echo $'\n'"The STS server is using the ${GREEN}MACHINE_SSL_CERT${NORMAL} VECS store."$'\n'
      else
         UPDATE_STS_CONFIG=1
         if [ "$STS_LOCALHOST_CONNECTOR_STORE" != 'STS_INTERNAL_SSL_CERT' ]; then VECS_STORES_TO_REPLACE+=" $STS_LOCALHOST_CONNECTOR_STORE"; fi
         if [ "$STS_LOCALHOST_CERTIFICATE_STORE" != 'STS_INTERNAL_SSL_CERT' ]; then VECS_STORES_TO_REPLACE+=" $STS_LOCALHOST_CERTIFICATE_STORE"; fi
         if [ "$STS_CLIENTAUTH_CONNECTOR_STORE" != 'STS_INTERNAL_SSL_CERT' ]; then VECS_STORES_TO_REPLACE+=" $STS_CLIENTAUTH_CONNECTOR_STORE"; fi
         if [ "$STS_CLIENTAUTH_CERTIFICATE_STORE" != 'STS_INTERNAL_SSL_CERT' ]; then VECS_STORES_TO_REPLACE+=" $STS_CLIENTAUTH_CERTIFICATE_STORE"; fi
         echo $'\nThe STS server is using the following VECS stores:'
         echo "Server > Service > Connector (localhost port): ${YELLOW}${STS_LOCALHOST_CONNECTOR_STORE}${NORMAL}"
         echo "Server > Service > Connector (localhost port) > SSLHostConfig > Certificate: ${YELLOW}${STS_LOCALHOST_CERTIFICATE_STORE}${NORMAL}"
         echo "Server > Service > Connector (client auth port): ${YELLOW}${STS_CLIENTAUTH_CONNECTOR_STORE}${NORMAL}"
         echo "Server > Service > Connector (client auth port) > SSLHostConfig > Certificate: ${YELLOW}${STS_CLIENTAUTH_CERTIFICATE_STORE}${NORMAL}"$'\n'
      fi
   else
      if [ "$STS_LOCALHOST_CONNECTOR_STORE" == 'MACHINE_SSL_CERT' ] && [ "$STS_LOCALHOST_CERTIFICATE_STORE" == 'MACHINE_SSL_CERT' ]; then
         echo $'\n'"The STS server is using the ${GREEN}MACHINE_SSL_CERT${NORMAL} VECS store."$'\n'
      else
         UPDATE_STS_CONFIG=1
         if [ "$STS_LOCALHOST_CONNECTOR_STORE" != 'STS_INTERNAL_SSL_CERT' ]; then VECS_STORES_TO_REPLACE+=" $STS_LOCALHOST_CONNECTOR_STORE"; fi
         if [ "$STS_LOCALHOST_CERTIFICATE_STORE" != 'STS_INTERNAL_SSL_CERT' ]; then VECS_STORES_TO_REPLACE+=" $STS_LOCALHOST_CERTIFICATE_STORE"; fi
         echo $'\nThe STS server is using the following VECS stores:'
         echo "Server > Service > Connector (localhost port): ${YELLOW}${STS_LOCALHOST_CONNECTOR_STORE}${NORMAL}"
         echo "Server > Service > Connector (localhost port) > SSLHostConfig > Certificate: ${YELLOW}${STS_LOCALHOST_CERTIFICATE_STORE}${NORMAL}"$'\n'         
      fi
   fi
   if [ $UPDATE_STS_CONFIG -eq 1 ]; then
      read -p $'\n'"Update STS server configuration to use the ${GREEN}MACHINE_SSL_CERT${NORMAL} store? [n]: " UPDATE_STS_CONFIG_INPUT
     
      if [ -z $UPDATE_STS_CONFIG_INPUT ]; then UPDATE_STS_CONFIG_INPUT='n'; fi
     
      if [[ $UPDATE_STS_CONFIG_INPUT =~ ^[Yy] ]]; then
         logInfo 'User has selected to update the STS server configuration'
         header 'Updating STS server configuration'
         task 'Backing up configuration'
         cp /usr/lib/vmware-sso/vmware-sts/conf/server.xml /usr/lib/vmware-sso/vmware-sts/conf/server.xml.backup 2>/dev/null || errorMessage 'Unable to backup /usr/lib/vmware-sso/vmware-sts/conf/server.xml'
         statusMessage 'OK' 'GREEN'

         task 'Backing up certificate'
         $VECS_CLI entry getcert --store STS_INTERNAL_SSL_CERT --alias __MACHINE_CERT > $BACKUP_DIR/STS_INTERNAL_SSL_CERT.crt || errorMessage 'Unable to backup STS_INTERNAL_SSL_CERT certificate'
         statusMessage 'OK' 'GREEN'

         task 'Backing up private key'
         $VECS_CLI entry getkey --store STS_INTERNAL_SSL_CERT --alias __MACHINE_CERT > $BACKUP_DIR/STS_INTERNAL_SSL_CERT.key || errorMessage 'Unable to backup STS_INTERNAL_SSL_CERT private key'
         statusMessage 'OK' 'GREEN'
         
         task 'Changing STS server configuration'
         for store in $VECS_STORES_TO_REPLACE; do
            sed -i "s/$store/MACHINE_SSL_CERT/g" /usr/lib/vmware-sso/vmware-sts/conf/server.xml || errorMessage 'Unable to update STS server configuration'
         done
         statusMessage 'OK' 'GREEN'

         if $VECS_CLI store list | grep 'STS_INTERNAL_SSL_CERT' > /dev/null; then          
            task 'Remove legacy STS VECS store'
            $VECS_CLI store delete --name STS_INTERNAL_SSL_CERT -y > /dev/null|| errorMessage 'Unable to delete VECS store STS_INTERNAL_SSL_CERT'
            statusMessage 'OK' 'GREEN'
         fi

         task 'Stopping STS service'
         service-control --stop vmware-stsd > /dev/null 2>&1 || errorMessage 'Unable to stop the STS service'
         statusMessage 'OK' 'GREEN'
        
         task 'Starting STS service'
         service-control --start vmware-stsd > /dev/null 2>&1 || errorMessage 'Unable to start the STS service'
         statusMessage 'OK' 'GREEN'
      fi
   else
      if $VECS_CLI store list | grep 'STS_INTERNAL_SSL_CERT' > /dev/null; then
         read -p 'Legacy STS SSL certificate store found. Remove store from VECS [n]: ' REMOVE_STS_STORE_INPUT

         if [ -z $REMOVE_STS_STORE_INPUT ]; then REMOVE_STS_STORE_INPUT='n'; fi

         if [[ "$REMOVE_STS_STORE_INPUT" =~ ^[Yy] ]]; then
            echo ''
            task 'Remove legacy STS VECS store'
            $VECS_CLI store delete --name STS_INTERNAL_SSL_CERT -y > /dev/null|| errorMessage 'Unable to delete VECS store STS_INTERNAL_SSL_CERT'
            statusMessage 'OK' 'GREEN'
         else
            echo ''
         fi         
      fi
   fi
}

function checkSTSConnectionStrings() {
   task 'Checking STS ConnectionStrings'
   if quickCheckSTSConnectionStrings; then
      statusMessage 'OK' 'GREEN'
   else
      statusMessage 'MISCONFIG' 'YELLOW'

      read -p $'\n'"Update STS ConnectionStrings value to ${GREEN}ldap://localhost:${VMDIR_PORT}${NORMAL}? [n]: " UPDATE_STS_CONNECTIONSTRINGS

      if [ -z $UPDATE_STS_CONNECTIONSTRINGS ]; then UPDATE_STS_CONNECTIONSTRINGS='n'; fi

      if [[ $UPDATE_STS_CONNECTIONSTRINGS =~ ^[Yy] ]]; then
         header 'Update STS ConnectionStrings'
         task 'Change ConnectionStrings value'
         $LDAP_MODIFY -h $PSC_LOCATION -p $VMDIR_PORT -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password 2>&1 >> $LOG <<EOF
dn: cn=$SSO_DOMAIN,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN
changetype: modify
replace: vmwSTSConnectionStrings
vmwSTSConnectionStrings: ldap://localhost:$VMDIR_PORT
EOF
         if [ $? -eq 0 ]; then
            statusMessage 'OK' 'GREEN'
         else
            errorMessage 'Unable to update the STS ConnectionStrings value. See log for details'
         fi
      fi
   fi
}

#------------------------------
# Check Smart Card configuration options
#------------------------------
function checkSmartCardOptions() {
   header 'Smart Card SSO options'
   echo -n 'Gathering authn SSO options...'
   SC_SSO_CONFIG=$(sso-config.sh -get_authn_policy -t $SSO_DOMAIN 2>/dev/null)
   if [ -n "$SC_SSO_CONFIG" ]; then
      echo -ne "\r"
      SC_SSO_USE_CRL=$(echo "$SC_SSO_CONFIG" | grep useCertCRL | awk '{print $NF}')
      SC_SSO_CRL_URL=$(echo "$SC_SSO_CONFIG" | grep CRLUrl | awk '{print $NF}')
      SC_SSO_CRL_FAILOVER=$(echo "$SC_SSO_CONFIG" | grep useCRLAsFailOver | awk '{print $NF}')
      SC_SSO_USE_OCSP=$(echo "$SC_SSO_CONFIG" | grep useOCSP | awk '{print $NF}')
   
      task 'Use CRL in certificate'
      if [ "$SC_SSO_USE_CRL" == 'false' ]; then
         statusMessage 'FALSE' 'YELLOW'
      else
         statusMessage 'TRUE' 'GREEN'
      fi
   
      task 'CRL override URL'
      if [ "$SC_SSO_CRL_URL" == 'UndefinedConfig' ]; then
         statusMessage 'NONE' 'YELLOW'
      else
         statusMessage "$SC_SSO_CRL_URL" 'GREEN'
      fi
   
      task 'Use CRL as failover'
      if [ "$SC_SSO_CRL_FAILOVER" == 'false' ]; then
         statusMessage 'FALSE' 'YELLOW'
      else
         statusMessage 'TRUE' 'GREEN'
      fi
   
      task 'Use OCSP'
      if [ "$SC_SSO_USE_OCSP" == 'false' ]; then
         statusMessage 'FALSE' 'YELLOW'
      else
         statusMessage 'TRUE' 'GREEN'
      fi
   else
      echo -ne "\r"
	  echo "${YELLOW}Unable to obtain SSO Authn policy options.${NORMAL}"
   fi
}

#------------------------------
# Manage ESXi certificates
#------------------------------
function manageESXiCertificates() {
   authenticateIfNeeded
   unset ESXI_MANAGE_INPUT
   header 'Manage ESXi Certificates'
   echo ' 1. Check ESXi/vCenter certificate trust'
   echo ' 2. Check ESXi certificate against vCenter database'
   echo ' 3. Replace ESXi certificate'
   
   read -p $'\nSelect an option [Return to Main Menu]: ' ESXI_MANAGE_INPUT
   
   case $ESXI_MANAGE_INPUT in
      1)
         checkESXivCenterTrustMenu
         ;;	 
      2)
         checkESXiAgainstVCDBMenu
         ;;
      3)
         replaceESXiCertificate
         ;;
   esac
}

#------------------------------
# Check certificates trust of ESXi hosts and vCenter
#------------------------------
function checkESXivCenterTrustMenu() {
   NUM_HOSTS=$($PSQL -d VCDB -U postgres -c "SELECT COUNT(id) FROM vpx_host WHERE enabled=1" -t | tr -d ' \n')
   
   echo $'\n'"There are ${GREEN}${NUM_HOSTS}${NORMAL} hosts connected to vCenter."$'\n'
   cat << EOF
 1. Perform check on all hosts (requires uniform root password on all hosts)
 2. Perform check on all hosts in a cluster (requires uniform root password on all hosts)
 3. Perform check on single host
EOF
   read -p $'\nSelect an option [Return to Main Menu]: ' ESXI_TRUST_INPUT
   
   CERT_MGMT_MODE=$($PSQL -d VCDB -U postgres -c "SELECT value FROM vpx_parameter WHERE name='vpxd.certmgmt.mode'" -t | grep -v '^$' | tr -d ' ')
   
   case $ESXI_TRUST_INPUT in
      1)
         checkAllESXivCenterTrust "$CERT_MGMT_MODE"
         ;;
      2)
         checkClusteredESXivCenterTrust "$CERT_MGMT_MODE"
         ;;
      3)
         checkSingleESXivCenterTrust "$CERT_MGMT_MODE"
         ;;
   esac
}

#------------------------------
# Check ESXi certificate against vCenter database
#------------------------------
function checkESXiAgainstVCDBMenu() {
   NUM_HOSTS=$($PSQL -d VCDB -U postgres -c "SELECT COUNT(id) FROM vpx_host WHERE enabled=1" -t | tr -d ' \n')
   
   echo $'\n'"There are ${GREEN}${NUM_HOSTS}${NORMAL} hosts connected to vCenter."$'\n'
   cat << EOF
 1. Perform check on all hosts
 2. Perform check on all hosts in a cluster
 3. Perform check on single host
EOF
   read -p $'\nSelect an option [Return to Main Menu]: ' ESXI_CERT_VCDB_INPUT

   CERT_MGMT_MODE=$($PSQL -d VCDB -U postgres -c "SELECT value FROM vpx_parameter WHERE name='vpxd.certmgmt.mode'" -t | grep -v '^$' | tr -d ' ')
   
   case $ESXI_CERT_VCDB_INPUT in
      1)
         checkAllESXiAgainstVCDB "$CERT_MGMT_MODE"
         ;;
      2)
         checkClusteredESXiAgainstVCDB "$CERT_MGMT_MODE"
         ;;
      3)
         checkSingleESXiAgainstVCDB "$CERT_MGMT_MODE"
         ;;
   esac
}

#------------------------------
# List clusters in vCenter inventory
#------------------------------
function listVCClusters() {
   CLUSTER_IDS=()
   CLUSTER_NAMES=()
   unset CHECK_CLUSTER_INPUT
   
   CLUSTERS=$($PSQL -d VCDB -U postgres -c "SELECT ent.id,ent.name FROM vpx_entity AS ent LEFT JOIN vpx_object_type AS obj ON ent.type_id = obj.id WHERE obj.name = 'CLUSTER_COMPUTE_RESOURCE'" -t)
   echo $'\nCompute clusters:'
   i=1
   logInfo 'List of clusters:'
   logDetails $CLUSTERS
   
   IFS=$'\n'
   for cluster in $CLUSTERS; do
      id=$(echo "$cluster" | awk -F'|' '{print $1}' | sed -e 's/[[:space:]]*//')
      name=$(echo "$cluster" | awk -F'|' '{print $2}' | sed -e 's/[[:space:]]*//')
      "$name"
      CLUSTER_IDS+=($id)
      CLUSTER_NAMES+=($name)
      ((++i))
   done
   IFS=$' \t\n'
   
   read -p $'\n\nSelect cluster: ' CHECK_CLUSTER_INPUT
}

#------------------------------
# Check certificates of all ESXi hosts
#------------------------------
function checkAllESXivCenterTrust() {
   HOSTS=$($PSQL -d VCDB -U postgres -c "SELECT id, dns_name, ip_address FROM vpx_host WHERE enabled=1 ORDER BY dns_name ASC, ip_address ASC" -t)
   
   checkESXivCenterTrust "$1" "$HOSTS"
}

#------------------------------
# Check certificates of ESXi hosts in a cluster
#------------------------------
function checkClusteredESXivCenterTrust() {
   listVCClusters
   
   while [ -z $CHECK_CLUSTER_INPUT ]; do read -p $'\n'"${YELLOW}Select cluster:${NORMAL} " CHECK_CLUSTER_INPUT; done
   
   HOSTS=$($PSQL -d VCDB -U postgres -c "SELECT id, dns_name, ip_address FROM vpx_host WHERE id IN (SELECT e.id FROM vpx_entity AS e LEFT JOIN vpx_object_type AS obj ON e.type_id=obj.id WHERE obj.name='HOST' AND e.parent_id=${CLUSTER_IDS[$((CHECK_CLUSTER_INPUT-1))]}) AND enabled=1 ORDER BY dns_name ASC, ip_address ASC" -t)
   
   checkESXivCenterTrust "$1" "$HOSTS"
}

#------------------------------
# Check certificate trust status between vCenter and ESXi hosts
#------------------------------
function checkESXivCenterTrust() {
   NEED_TO_RESTART_SPS=0
   ENTRIES_UPDATED=0
   ENTRIES_CREATED=0   
   HOSTS="$2"
   
   read -r -s -p $'\nEnter root password for all ESXi hosts: ' ESXI_PASSWORD_INPUT
   
   while [ -z $ESXI_PASSWORD_INPUT ]; do read -r -s -p $'\n'"${YELLOW}Enter root password for all ESXi hosts:${NORMAL} " ESXI_PASSWORD_INPUT; done
   echo $'\n\n'"Certificate Management Mode: ${GREEN}$1${NORMAL}"
   
   IFS=$'\n'
   for host_info in $HOSTS; do
      id=$(echo "$host_info" | awk -F'|' '{print $1}' | tr -d ' ')   
      name=$(echo "$host_info" | awk -F'|' '{print $2}' | tr -d ' ')
      ip=$(echo "$host_info" | awk -F'|' '{print $3}' | tr -d ' ')

      if [ -n "$name" ]; then
         viewESXiCertificateTrust "$name" "$1" "$ESXI_PASSWORD_INPUT"
      else
         viewESXiCertificateTrust "$ip" "$1" "$ESXI_PASSWORD_INPUT"
      fi
   done   
   IFS=$' \t\n'
   
   if [ "$1" == 'thumbprint' ]; then
      echo $'\n'"Entries updated: $ENTRIES_UPDATED"
      echo "Entries created: $ENTRIES_CREATED"
	  
      if [ $NEED_TO_RESTART_SPS -gt 0 ]; then
         echo $'\nRestarting the SPS service...'

         restartVMwareServices 'vmware-sps'
      else
         echo $'\nDone!'
fi
   fi
}

#------------------------------
# Check certificates of a single ESXi host
#------------------------------
function checkSingleESXivCenterTrust() {
   header 'ESXi Certificate Trust Check'
   unset ESXI_NAME_INPUT
   read -p 'Enter FQDN or IP of the ESXi host: ' ESXI_NAME_INPUT
   
   while [ -z $ESXI_NAME_INPUT ]; do read -p 'Enter FQDN or IP of the ESXi host: ' ESXI_NAME_INPUT; done
   
   ESXI_VALID_NAME=0

   while [ $ESXI_VALID_NAME -eq 0 ]; do
      if [[ $ESXI_NAME_INPUT =~ ^[a-zA-Z] ]]; then
         logDetails "Verifying name resolution of $ESXI_NAME_INTPUT"
         if ! nslookup $ESXI_NAME_INPUT > /dev/null 2>&1; then 
            read -p $'\n'"${YELLOW}Unable to resolve '$ESXI_NAME_INPUT', enter FQDN or IP of the ESXi host:${NORMAL} " ESXI_NAME_INPUT
         else
            ESXI_VALID_NAME=1
         fi
      else
         if ! validateIp $ESXI_NAME_INPUT; then 
            read -p $'\n'"${YELLOW}Invalid IP address, enter FQDN or IP of the ESXi host:${NORMAL} " ESXI_NAME_INPUT
         else
            ESXI_VALID_NAME=1
         fi
      fi
   done
   
   read -r -s -p $'\n'"Enter root password for $ESXI_NAME_INPUT: " ESXI_PASSWORD_INPUT
   
   while [ -z $ESXI_PASSWORD_INPUT ]; do read -r -s -p $'\n'"${YELLOW}Enter root password for $ESXI_NAME_INPUT:${NORMAL} " ESXI_PASSWORD_INPUT; done
   
   echo $'\n\n'"Certificate Management Mode: ${GREEN}$1${NORMAL}"
   
   viewESXiCertificateTrust "$ESXI_NAME_INPUT" "$1" "$ESXI_PASSWORD_INPUT"
}

#------------------------------
# View trust information between vCenter and a host
#------------------------------
function viewESXiCertificateTrust() {
   echo $'\n'"Host: ${CYAN}$1${NORMAL}"
   HOST_HASH=$(timeout 3 openssl s_client -connect $1:443 2>/dev/null | openssl x509 2>/dev/null)
   
   if [ -n "$HOST_HASH" ]; then
      HOST_CERT_INFO=$(viewCertificateInfo "$HOST_HASH")
      HOST_CERT_ISSUER=$(echo "$HOST_CERT_INFO" | grep 'Issuer:' | awk -F'Issuer: ' '{print $NF}')
      HOST_CERT_SUBJECT=$(echo "$HOST_CERT_INFO" | grep 'Subject:' | awk -F'Subject: ' '{print $NF}')
      HOST_CERT_VALID_START=$(echo "$HOST_HASH" | openssl x509 -noout -startdate 2>/dev/null | sed 's/notBefore=//')
      HOST_CERT_VALID_END=$(echo "$HOST_HASH" | openssl x509 -noout -enddate 2>/dev/null | sed 's/notAfter=//')
      HOST_CERT_FINGERPRINT=$(echo "$HOST_HASH" | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}')
      HOST_CERT_ALGORITHM=$(echo "$HOST_CERT_INFO" | grep 'Signature Algorithm' | head -n1 | awk '{print $NF}')
      HOST_CERT_SAN=$(echo "$HOST_CERT_INFO" | grep 'X509v3 Subject Alternative Name' -A1 | tail -n1 | sed -e 's/^ *//g' -e 's/, /\n/g' | grep -v '^$' | sort)
	  
      echo "   Issuer: $HOST_CERT_ISSUER"
      echo "   Subject: $HOST_CERT_SUBJECT"
      if isExpired "$HOST_HASH" 'hash'; then echo "${RED}"; fi
      echo "      Not Before: $HOST_CERT_VALID_START"
      echo "      Not After : $HOST_CERT_VALID_END"
      echo "      ${NORMAL}SHA1 Fingerprint : $HOST_CERT_FINGERPRINT"
      echo "      Signature Algorithm: $HOST_CERT_ALGORITHM"

      echo '      Subject Alternative Name entries:'
      if [ -n "$HOST_CERT_SAN" ]; then         
         IFS=$'\n'
         for san in $(echo "$HOST_CERT_SAN"); do
            echo "         |_$san"
         done
         IFS=$' \t\n'
      fi

      echo $'\n   Certificate Trusts:'	  
	  
      if [ "$2" = 'thumbprint' ]; then
         CURRENT_HOST_SMS_THUMBPRINT=$($VECS_CLI entry getcert --store SMS --alias "https://$1:9080/version.xml" 2>/dev/null | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}')
		 
         if [ -n "$CURRENT_HOST_SMS_THUMBPRINT" ]; then
            echo '      Host IOFILTER provider found in VECS, checking certificate...'              
      
            if [[ "$CURRENT_SMS_THUMBPRINT" != "$HOST_THUMBPRINT" ]]; then
               echo '      Mismatch found, re-creating entry...'
               if $VECS_CLI entry delete --store SMS --alias "https://$1:9080/version.xml" -y > /dev/null; then
                  if echo | openssl s_client -connect $1:443 2>/dev/null | openssl x509 > $STAGE_DIR/$1.crt 2>/dev/null; then
                     if $VECS_CLI entry create --store SMS --alias "https://$1:9080/version.xml" --cert $STAGE_DIR/$1.crt > /dev/null; then
                        echo '      IOFILTER provider certificate updated!'
                        ((++ENTRIES_UPDATED))
                        NEED_TO_RESTART_SPS=1
                     else
                        echo "      ${YELLOW}Unable to re-create the IOFILTER provider certificate in VECS!${NORMAL}"
                     fi
                  else
                     echo "      ${YELLOW}Unable to obtain host's SSL certificate on port 443!${NORMAL}"
                  fi
               else
                  echo "      ${YELLOW}Unable to delete the IOFILTER provider certificate from VECS!${NORMAL}"
               fi
            else
               echo "      ${GREEN}Certificate matches, no need to update.${NORMAL}" 
            fi
         else
            echo '      Host IOFILTER provider certificate not in VECS. Creating entry...'
            if echo | openssl s_client -connect $1:443 2>/dev/null | openssl x509 > $STAGE_DIR/$1.crt 2>/dev/null; then
               if $VECS_CLI entry create --store SMS --alias "https://$1:9080/version.xml" --cert $STAGE_DIR/$1.crt > /dev/null; then
                  echo '      IOFILTER provider certificate created!'
                  ((++ENTRIES_CREATED))
                  NEED_TO_RESTART_SPS=1
               else
                  echo "      ${YELLOW}Unable to re-create the IOFILTER provider certificate in VECS!${NORMAL}"
               fi
            else
               echo "      ${YELLOW}Unable to obtain host's SSL certificate on port 443!${NORMAL}"
            fi
         fi
      else
         HOST_RHTTPPROXY_CERT=$(echo | openssl s_client -connect $1:443 2>/dev/null | openssl x509 2>/dev/null)
         HOST_IOFILTERVP_CERT=$(echo | openssl s_client -connect $1:9080 2>/dev/null | openssl x509 2>/dev/null)
         VCENTER_MACHINE_SSL_CERT=$($VECS_CLI entry getcert --store MACHINE_SSL_CERT --alias __MACHINE_CERT)
         SPS_CERT=$($VECS_CLI entry getcert --store SMS --alias sms_self_signed)
		 
         echo -n '      Reverse Proxy cert (port 443): '
		 
         SEARCH_CERTS="$(find /etc/vmware-vpx/docRoot/certs/ -type f | grep -v '\.r')"
         if checkForCACerts "$HOST_RHTTPPROXY_CERT"; then
            echo "${GREEN}Trusted by vCenter${NORMAL}"
         else
            echo "${YELLOW}Not trusted by vCenter${NORMAL}"
         fi
		 
         echo -n '      IOFilter VASA provider cert (port 9080): '
         if [ -n "$HOST_IOFILTERVP_CERT" ]; then
            if checkForCACerts "$HOST_IOFILTERVP_CERT"; then
               echo "${GREEN}Trusted by vCenter${NORMAL}"
            else
               echo "${YELLOW}Not trusted by vCenter${NORMAL}"
            fi
         else
	        echo "${YELLOW}unknown${NORMAL}"
         fi
         ESXI_CASTORE_HTTP_CODE=$(curl -k -X GET -u "root:$3" https://$1/host/castore -o $STAGE_DIR/$1-castore.pem -s -w "%{http_code}\n")
         logInfo "Getting castore.pem file from host $1"
         logInfo "Response from host $1 - HTTP status code: $ESXI_CASTORE_HTTP_CODE"
         if [  "$ESXI_CASTORE_HTTP_CODE" == '200' ]; then
            sed -i '/^$/d' $STAGE_DIR/$1-castore.pem
            csplit -s -z -f $STAGE_DIR/$1-ca- -b %02d.crt $STAGE_DIR/$1-castore.pem '/-----BEGIN CERTIFICATE-----/' '{*}'
			
            SEARCH_CERTS=$(ls $STAGE_DIR/$1-ca-*.crt 2>/dev/null)
			
            if [ -n "$SEARCH_CERTS" ]; then
               echo -n '      vCenter Machine SSL cert: '
               if checkForCACerts "$VCENTER_MACHINE_SSL_CERT"; then
                  echo "${GREEN}Trusted by host${NORMAL}"
               else
                  echo "${YELLOW}Not trusted by host${NORMAL}"
               fi
			
               echo -n '      SPS service connection: '
               if checkForCACerts "$SPS_CERT"; then
                  echo "${GREEN}Trusted by host${NORMAL}"
               else
                  echo "${YELLOW}Not trusted by host (maybe)${NORMAL}"
               fi
            else
               echo "      vCenter Machine SSL cert: ${YELLOW}No CA certs in /etc/vmware/ssl/castore.pem${NORMAL}"
               echo "      SPS service connection: ${YELLOW}No CA certs in /etc/vmware/ssl/castore.pem${NORMAL}"
            fi
         elif [  "$ESXI_CASTORE_HTTP_CODE" == '401' ]; then
            echo "      vCenter Machine SSL cert: ${YELLOW}unknown (possible bad ESXi root password)${NORMAL}"			
            echo "      SPS service connection: ${YELLOW}unknown (possible bad ESXi root password)${NORMAL}"
         else
            echo "      vCenter Machine SSL cert: ${YELLOW}unknown${NORMAL}"			
            echo "      SPS service connection: ${YELLOW}unknown${NORMAL}"
         fi
      fi
   else
      echo $'\n'"${YELLOW}Unable to obtain SSL certificate from host $1.${NORMAL}"
   fi
}

#------------------------------
# Check the SSL certificate of all ESXi hosts against VCDB
#------------------------------
function checkAllESXiAgainstVCDB() {
   HOSTS=$($PSQL -d VCDB -U postgres -c "SELECT id, dns_name, ip_address FROM vpx_host WHERE enabled=1 ORDER BY dns_name ASC, ip_address ASC" -t)
   
   checkESXiAgainstVCDB "$1" "$HOSTS"
}

#------------------------------
# Check the SSL certificate of ESXi hosts in a cluster against VCDB
#------------------------------
function checkClusteredESXiAgainstVCDB() {
   listVCClusters
   
   while [ -z $CHECK_CLUSTER_INPUT ]; do read -p $'\n'"${YELLOW}Select cluster:${NORMAL} " CHECK_CLUSTER_INPUT; done
   
   HOSTS=$($PSQL -d VCDB -U postgres -c "SELECT id, dns_name, ip_address FROM vpx_host WHERE id IN (SELECT e.id FROM vpx_entity AS e LEFT JOIN vpx_object_type AS obj ON e.type_id=obj.id WHERE obj.name='HOST' AND e.parent_id=${CLUSTER_IDS[$((CHECK_CLUSTER_INPUT-1))]}) AND enabled=1 ORDER BY dns_name ASC, ip_address ASC" -t)
   
   checkESXiAgainstVCDB "$1" "$HOSTS"
}

#------------------------------
# Check the SSL certificate of a single ESXi host against VCDB
#------------------------------
function checkSingleESXiAgainstVCDB() {
   unset ESXI_NAME_INPUT
   read -p 'Enter FQDN or IP of the ESXi host: ' ESXI_NAME_INPUT
   
   while [ -z $ESXI_NAME_INPUT ]; do read -p 'Enter FQDN or IP of the ESXi host: ' ESXI_NAME_INPUT; done
   
   ESXI_VALID_NAME=0

   while [ $ESXI_VALID_NAME -eq 0 ]; do
      if [[ $ESXI_NAME_INPUT =~ ^[a-zA-Z] ]]; then
         logDetails "Verifying name resolution of $ESXI_NAME_INTPUT"
         if ! nslookup $ESXI_NAME_INPUT > /dev/null 2>&1; then 
            read -p $'\n'"${YELLOW}Unable to resolve '$ESXI_NAME_INPUT', enter FQDN or IP of the ESXi host:${NORMAL} " ESXI_NAME_INPUT
         else
            HOSTS="|$ESXI_NAME_INPUT|"
            ESXI_VALID_NAME=1
         fi
      else
         if ! validateIp $ESXI_NAME_INPUT; then 
            read -p $'\n'"${YELLOW}Invalid IP address, enter FQDN or IP of the ESXi host:${NORMAL} " ESXI_NAME_INPUT
         else
            HOSTS="||$ESXI_NAME_INPUT"
            ESXI_VALID_NAME=1
         fi
      fi
   done

   checkESXiAgainstVCDB "$1" "$HOSTS"

}

#------------------------------
# Output information on SSL certificate of a host compared to VCDB
#------------------------------
function checkESXiAgainstVCDB() {
   header 'Checking ESXi SSL Thumbprints Against vCenter Database'
   if [ "$1" == 'thumbprint' ]; then
      echo "${YELLOW}VMCA Certificate Management Mode is set to 'thumbprint'${NORMAL}"
   else
      IFS=$'\n'
      for host_info in $HOSTS; do
         id=$(echo "$host_info" | awk -F'|' '{print $1}' | tr -d ' ')   
         name=$(echo "$host_info" | awk -F'|' '{print $2}' | tr -d ' ')
         ip=$(echo "$host_info" | awk -F'|' '{print $3}' | tr -d ' ')

         if [ -n "$name" ]; then
            viewESXiAgainstVCDB "$name"
         else
            viewESXiAgainstVCDB "$ip"
         fi
      done   
      IFS=$' \t\n'
   fi
}

#------------------------------
# Output information on SSL certificate of a host compared to VCDB
#------------------------------
function viewESXiAgainstVCDB() {
   echo $'\n'"${CYAN}$1${NORMAL}"
   
   THUMBPRINT_INFO_VCDB=$($PSQL -U postgres -d VCDB -c "SELECT expected_ssl_thumbprint,host_ssl_thumbprint FROM vpx_host WHERE dns_name = '$1' OR ip_address = '$1'" -t)
   EXPECTED_SSL_THUMBPRINT=$(echo "$THUMBPRINT_INFO_VCDB" | awk -F '|' '{print $1}' | tr -d ' ')
   HOST_SSL_THUMBPRINT=$(echo "$THUMBPRINT_INFO_VCDB" | awk -F '|' '{print $2}' | tr -d ' ')
   ACTUAL_SSL_THUMBPRINT=$(echo | openssl s_client -connect $1:443 2>/dev/null | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F '=' '{print $NF}')

   echo "   Expected Thumbprint (VCDB)  : $EXPECTED_SSL_THUMBPRINT"
   echo "   Host Thumbprint (VCDB)      : $HOST_SSL_THUMBPRINT"
   if [ -z "$ACTUAL_SSL_THUMBPRINT" ]; then
      echo "   Actual Thumbprint (openssl) : ${YELLOW}Cannot connect to host${NORMAL}"
      echo "   Status: ${YELLOW}UNKNOWN${NORMAL}"
   else
      echo "   Actual Thumbprint (openssl) : $ACTUAL_SSL_THUMBPRINT"
      if [ "$EXPECTED_SSL_THUMBPRINT" != "$ACTUAL_SSL_THUMBPRINT" ] && [ "$HOST_SSL_THUMBPRINT" != "$ACTUAL_SSL_THUMBPRINT" ] && [ "$EXPECTED_SSL_THUMBPRINT" != "$HOST_SSL_THUMBPRINT" ]; then
         echo "   Status: ${YELLOW}MISMATCH${NORMAL}"
      else
         echo "   Status: ${GREEN}MATCH${NORMAL}"
      fi
   fi
}

#------------------------------
# Check for signing certificates of a leaf certificate
#------------------------------
function checkForCACerts() {
   SEARCH=0
   FOUND_ROOT=0
   FOUND_ANY=0
   SEARCH_ISSUER=$(echo "$1" | openssl x509 -noout -text 2>/dev/null | grep 'Issuer:' | sed -e 's/Issuer: //' -e 's/[[:space:]]*//')
   SEARCH_AUTH_KEY_ID=$(echo "$1" | openssl x509 -noout -text 2>/dev/null | grep -A1 'Authority Key Id' | grep 'keyid:' | sed -e 's/keyid://' | tr -d ' ')
   while [ $SEARCH -eq 0 ]; do
      
	  IFS=$'\n'   
      for cert in $SEARCH_CERTS; do  
         SEARCH_CURRENT_SUBJECT=$(openssl x509 -noout -text -in $cert 2>/dev/null | grep 'Subject:' | sed -e 's/Subject: //' -e 's/[[:space:]]*//')
         SEARCH_CURRENT_ISSUER=$(openssl x509 -noout -text -in $cert 2>/dev/null | grep 'Issuer:' | sed -e 's/Issuer: //' -e 's/[[:space:]]*//')
         SEARCH_SKID=$(openssl x509 -noout -text -in $cert 2>/dev/null | grep -A1 'Subject Key Id' | tail -n1 | tr -d ' ')
         SEARCH_CURRENT_AUTH_KEY_ID=$(openssl x509 -noout -text -in $cert 2>/dev/null | grep -A1 'Authority Key Id' | grep 'keyid:' | sed -e 's/keyid://' -e 's/[[:space:]]*//')
         if [ "$SEARCH_ISSUER" = "$SEARCH_CURRENT_SUBJECT" ] && [ "$SEARCH_AUTH_KEY_ID" = "$SEARCH_SKID" ]; then
            if [ "$SEARCH_CURRENT_SUBJECT" = "$SEARCH_CURRENT_ISSUER" ]; then
               SEARCH=1
               FOUND_ROOT=1
               break 2
            else
               SEARCH_ISSUER=$SEARCH_CURRENT_ISSUER
               SEARCH_AUTH_KEY_ID=$SEARCH_CURRENT_AUTH_KEY_ID
               FOUND_ANY=1
            fi              
         fi
      done
      IFS=$' \t\n'
      if [ $FOUND_ANY -eq 0 ]; then
         SEARCH=1
      else
         FOUND_ANY=0
      fi
   done
   
   if [ $FOUND_ROOT -eq 0 ]; then
      return 1
   else
      return 0
   fi
}

#------------------------------
# Replace SSL certificate, private key, and CA store on ESXi host
#------------------------------
function replaceESXiCertificate() {
   unset ESXI_CA_SIGNED_OPTION_INPUT
   header 'ESXi Certificate Options'
   
   echo $'\n1. Generate Certificate Signing Request and Private Key'
   echo '2. Generate Certificate Signing Request and Private Key'
   echo '   from custom OpenSSL configuration file'
   echo '3. Import CA-signed certificate and key'
   read -p $'\nSelect an option [Return to Main Menu]: ' ESXI_CA_SIGNED_OPTION_INPUT
   
   if [ -z $ESXI_CA_SIGNED_OPTION_INPUT ]; then return 1; fi
   
   if [ "$ESXI_CA_SIGNED_OPTION_INPUT" == '3' ]; then
      authenticateIfNeeded
	  
      logInfo 'User has chosen to import a CA-signed ESXi SSL certificate and key'
      read -p $'\nEnter FQDN or IP of the ESXi host: ' ESXI_NAME_INPUT
   
      ESXI_VALID_NAME=0

      while [ $ESXI_VALID_NAME -eq 0 ]; do 
         if [[ $ESXI_NAME_INPUT =~ ^[a-zA-Z] ]]; then
            if ! nslookup $ESXI_NAME_INPUT > /dev/null 2>&1; then
               read -p $'\n'"${YELLOW}Unable to resolve '$ESXI_NAME_INPUT'${NORMAL}', enter FQDN or IP of the ESXi host:${NORMAL} " ESXI_NAME_INPUT
            else
               ESXI_VALID_NAME=1
            fi
         else
            if ! validateIp $ESXI_NAME_INPUT; then
               read -p $'\n'"${YELLOW}Invalid IP address, enter FQDN or IP of the ESXi host:${NORMAL} " ESXI_NAME_INPUT
            else
               ESXI_VALID_NAME=1
            fi
         fi
      done

   
      read -r -s -p $'\n'"Enter root password for $ESXI_NAME_INPUT: " ESXI_PASSWORD_INPUT   
      while [ -z $ESXI_PASSWORD_INPUT ]; do read -r -s -p $'\n'"${YELLOW}Enter root password for $ESXI_NAME_INPUT:${NORMAL} " ESXI_PASSWORD_INPUT; done
   
      read -e -p $'\n\nEnter path to new ESXi certificate: ' ESXI_NEW_CERT_INPUT   
      while [ ! -f $ESXI_NEW_CERT_INPUT ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to new ESXi certificate:${NORMAL} " ESXI_NEW_CERT_INPUT; done
   
      getCorrectCertFormat "$ESXI_NEW_CERT_INPUT" 'NEW_ESXI_CERT'
      NEW_ESXI_CERT_MODULUS=$(openssl x509 -noout -modulus -in $NEW_ESXI_CERT 2>/dev/null | md5sum | awk '{print $1}')

      getPrivateKey "$NEW_ESXI_CERT_MODULUS" "NEW_ESXI" "ESXi host"
   
      verifyCertAndKey "$NEW_ESXI_CERT" "$NEW_ESXI_KEY"
      getCAChain "$NEW_ESXI_CERT"   
   
      header 'Replace ESXi Certificate'
      task 'Publish CA signing certificates'
      $DIR_CLI trustedcert publish --chain --cert $TRUSTED_ROOT_CHAIN --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>&1 | logInfo || errorMessage 'Unable to publish trusted root chain to VMDir'
	   $VECS_CLI force-refresh > /dev/null 2>&1 || errorMessage 'Unable to perform a force-refresh of CA certificates in VECS'
      statusMessage 'OK' 'GREEN'
   
      SEARCH_CERTS="$(find /etc/vmware-vpx/docRoot/certs/ -type f | grep -v '\.r')"
   
      if [ -f $STAGE_DIR/$ESXI_NAME_INPUT-castore.pem ]; then echo '' > $STAGE_DIR/$ESXI_NAME_INPUT-castore.pem; fi
   
      for cert in "$SEARCH_CERTS"; do
	      if isCertCA "$(cat $cert)"; then cat $cert >> $STAGE_DIR/$ESXI_NAME_INPUT-castore.pem; fi
      done
   
      $VECS_CLI entry getcert --store SMS --alias sms_self_signed >> $STAGE_DIR/$ESXI_NAME_INPUT-castore.pem
   
      task 'Replace ESXi certificate'
      logInfo "Replacing ESXi certificate with $NEW_ESXI_CERT"
      logInfo "Replacing ESXi private key with $CURRENT_PRIVATE_KEY_PATH
      "
      ESXI_CERT_REPLACEMENT_HTTP_CODE=$(curl -k -X PUT -u "root:$ESXI_PASSWORD_INPUT" https://$ESXI_NAME_INPUT/host/ssl_cert --data-binary @$NEW_ESXI_CERT -s -w "%{http_code}\n")
      if [[ "$ESXI_CERT_REPLACEMENT_HTTP_CODE" =~ ^20 ]]; then
         statusMessage 'OK' 'GREEN'
      else
	      errorMessage "Unable to replace certificate, HTTP return code: $ESXI_CERT_REPLACEMENT_HTTP_CODE"
      fi
	  
	   task 'Replace ESXi private key'
	   ESXI_KEY_REPLACEMENT_HTTP_CODE=$(curl -k -X PUT -u "root:$ESXI_PASSWORD_INPUT" https://$ESXI_NAME_INPUT/host/ssl_key --data-binary @$CURRENT_PRIVATE_KEY_PATH -s -w "%{http_code}\n")
	   if [[ "$ESXI_KEY_REPLACEMENT_HTTP_CODE" =~ ^20 ]]; then
	      statusMessage 'OK' 'GREEN'
      else
	      errorMessage "Unable to replace private key, HTTP return code: $ESXI_KEY_REPLACEMENT_HTTP_CODE"
	   fi
	  
	   task 'Replace castore.pem'
      ESXI_CASTORE_REPLACEMENT_HTTP_CODE=$(curl -k -X PUT -u "root:$ESXI_PASSWORD_INPUT" https://$ESXI_NAME_INPUT/host/castore --data-binary @$STAGE_DIR/$ESXI_NAME_INPUT-castore.pem -s -w "%{http_code}\n")
      if [[ "$ESXI_CASTORE_REPLACEMENT_HTTP_CODE" =~ ^20 ]]; then
         statusMessage 'OK' 'GREEN'
      else
         errorMessage "Unable to replace castore.pem, HTTP return code: $ESXI_CASTORE_REPLACEMENT_HTTP_CODE"
      fi
      
	   cat << EOF

${YELLOW}Additional steps are necessary to complete this process:
 1. Run the following command on the ESXi host to save
    the new certificate and key to the bootbank:
	   /bin/auto-backup.sh
 2. Either reboot the ESXi host, or restart the
    Management Agents (rhttpproxy, hostd, vpxa, etc.)
 3. Disconnect and Re-connect the host in vCenter to
    update certificate information in the vCenter database${NORMAL}
EOF
   else
      clearCSRInfo
      read -p $'\n'"Enter a value for the ${CYAN}CommonName${NORMAL} of the certificate: " ESXI_CN_INPUT
	  
	   while [ -z $ESXI_CN_INPUT ]; do read -p $'\n'"${YELLOW}Enter a value for the CommonName of the certificate:${NORMAL} " ESXI_CN_INPUT; done
	  
	   ESXI_CSR=$REQUEST_DIR/$ESXI_CN_INPUT-$TIMESTAMP.csr
      ESXI_KEY=$REQUEST_DIR/$ESXI_CN_INPUT-$TIMESTAMP.key
      
      if [ "$ESXI_CA_SIGNED_OPTION_INPUT" == '1' ]; then
         logInfo 'User has chosen to generate the ESXi SSL private key and CSR'
	      ESXI_CFG=$REQUEST_DIR/$ESXI_CN_INPUT.cfg
         
         if [ -z "$CSR_COUNTRY" ]; then getCSRInfo; fi
         defineSANEntries 'ESXi' 'ESXi' "$ESXI_CN_INPUT"
         generateOpensslConfig $ESXI_CN_INPUT $ESXI_CFG "ESXi"
      else
         logInfo 'User has chosen to generate the ESXi SSL private key and CSR from a custom OpenSSL configuration file'
         read -e -p $'\nEnter path to custom OpenSSL configuration file: ' ESXI_CFG
         while [ ! -f "$ESXI_CFG" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to custom OpenSSL configuration file:${NORMAL} " ESXI_CFG; done
      fi
      header "Replace ESXi Certificate"
      task "Generating Certificate Signing Request"
      generateCSR $ESXI_CSR $ESXI_KEY $ESXI_CFG || errorMessage "Unable to generate Certificate Signing Request and Private Key"
      statusMessage "OK" "GREEN"
         
      printf "\nCertificate Signing Request generated at ${CYAN}${ESXI_CSR}${NORMAL}"
      printf "\nPrivate Key generated at ${CYAN}${ESXI_KEY}${NORMAL}\n"      
   fi
}

#------------------------------
# Prompt to restart VMware services
#------------------------------
function promptRestartVMwareServices() {
   if [ -z $1 ]; then
      read -p $'\nRestart VMware services [no]: ' RESTART_SERVICES_INPUT
   else
      read -p $'\n'"Restart service $1 [no]: " RESTART_SERVICES_INPUT
   fi
   
   if [[ "$RESTART_SERVICES_INPUT" =~ ^[yY] ]]; then 
      if [ -z $1 ]; then
         restartVMwareServices
      else
         restartVMwareServices $1
      fi
      operationMenu
      processOperationMenu
   elif [ -n "$1" ]; then
      operationMenu
      processOperationMenu
   fi
}

#------------------------------
# Menu to restart VMware services
#------------------------------
function restartServicesMenu() {
   header 'Restart VMware Services'
   echo ' 1. Restart all VMware services'
   echo ' 2. Restart specific VMware service'
   
   read -p $'\nSelect an option [Return to Main Menu]: ' RESTART_SERVICES_INPUT
   
   case $RESTART_SERVICES_INPUT in
      1)
         restartVMwareServices
         ;;
      2)
         read -p $'\nEnter VMware service to restart: ' RESTART_SERVICE_INPUT
         while [ -z "$RESTART_SERVICE_INPUT" ]; do read -p $'\n'"${YELLOW}Enter VMware service to restart:${NORMAL} " RESTART_SERVICE_INPUT; done
		 
         if echo "$VMWARE_SERVICES" | grep "^$RESTART_SERVICE_INPUT$" > /dev/null; then
            restartVMwareServices "$RESTART_SERVICE_INPUT"
         else
            echo $'\n'"${YELLOW}Unknown service '$RESTART_SERVICE_INPUT'${NORMAL}"
         fi
         ;;
   esac
}

#------------------------------
# Restart all VMware services
#------------------------------
function restartVMwareServices() {
   header 'Restarting Services'        
   if [ $# -eq 0 ]; then
      task 'Stopping VMware services'
      service-control --stop $VMON_SERVICE_PROFILE > /dev/null 2>&1 || errorMessage 'Unable to stop all VMware services, check log for details'
      statusMessage 'OK' 'GREEN'
   
      task 'Starting VMware services'
      service-control --start $VMON_SERVICE_PROFILE > /dev/null 2>&1 || errorMessage 'Unable to start all VMware services, check log for details'
      statusMessage 'OK' 'GREEN'
  
      if [[ "$VC_VERSION" =~ ^6 ]]; then
         task 'Restarting VAMI service'
         systemctl restart vami-lighttp
         if [ $(systemctl status vami-lighttp | grep 'Active:' | awk '{print $3}') == '(running)' ]; then
            statusMessage 'OK' 'GREEN'
         else
            statusMessage 'ERROR' 'YELLOW'
         fi
      fi
      if [[ $UPDATED_MACHINE_SSL -eq 1 || $UPDATED_TRUST_ANCHORS -eq 1 ]] && [ "$NODE_TYPE" != 'embedded' ]; then
         printf "\n\n${YELLOW}Please restart services on all other vCenter/PSC nodes in this environment.${NORMAL}\n\n"
      fi
   else
      while [ $# -gt 0 ]; do
         task "Stopping $1"
         service-control --stop $1 > /dev/null 2>&1 || errorMessage "Unable to stop service $1, check log for details"
         statusMessage 'OK' 'GREEN'
      
         task "Starting $1"
         service-control --start $1 > /dev/null 2>&1 || errorMessage "Unable to start service $1, check log for details"
         statusMessage 'OK' 'GREEN'
		 shift
	  done
   fi
}

# commands
VECS_CLI='/usr/lib/vmware-vmafd/bin/vecs-cli'
DIR_CLI='/usr/lib/vmware-vmafd/bin/dir-cli'
VMAFD_CLI='/usr/lib/vmware-vmafd/bin/vmafd-cli'
if [ -f '/usr/sbin/vmon-cli' ]; then 
   VMON_CLI='/usr/sbin/vmon-cli'
else
   VMON_CLI='/usr/lib/vmware-vmon/vmon-cli'
fi
CERTOOL='/usr/lib/vmware-vmca/bin/certool'
LDAP_DELETE='/usr/bin/ldapdelete'
LDAP_SEARCH='/usr/bin/ldapsearch'
LDAP_MODIFY='/usr/bin/ldapmodify'
PSQL='/opt/vmware/vpostgres/current/bin/psql'

# variables
VC_VERSION=$(grep 'CLOUDVM_VERSION:' /etc/vmware/.buildInfo | awk -F ':' '{print $NF}' | awk -F'.' '{print $1"."$2}')
VC_BUILD=$(grep '"build":' /etc/applmgmt/appliance/update.conf | awk -F ':' '{print $NF}' | tr -d '", ')
LOG_DIR='/var/log/vmware/vCert'
LOG="$LOG_DIR/vCert.log"
DEBUG=0
CLEANUP=1
EXIT_ON_BACKUP_FAILURE=1
READ_TIMEOUTS=120
VERIFY_PASSED_CREDENTIALS=0
TOP_DIR='/root/vCert-master'
WORK_DIR="$TOP_DIR/$(date +%Y%m%d)"
STAGE_DIR="$WORK_DIR/stage"
REQUEST_DIR="$WORK_DIR/requests"
BACKUP_DIR="$WORK_DIR/backup"
VC_REPORT="$LOG_DIR/vcenter-certificate-report.txt"
#ESXi_REPORT="$LOG_DIR/esxi-certificate-report.txt"
VMCA_CERT='/var/lib/vmware/vmca/root.cer'
VMON_SERVICE_PROFILE='--all'
VMWARE_SERVICES=$(service-control --list | awk '{print $1}' | sort)
NODE_TYPE=$(cat /etc/vmware/deployment.node.type)
HOSTNAME=$(hostname -f)
HOSTNAME_LC=$(echo $HOSTNAME | awk '{print tolower($0)}')
HOSTNAME_SHORT=$(hostname -s)
IP=$(ip a | grep -A 2 eth0 | grep inet | awk '{print $2}' | awk -F'/' '{print $1}')
PNID=$($VMAFD_CLI get-pnid --server-name localhost)
PNID_LC=$(echo $PNID | awk '{print tolower($0)}')
MACHINE_ID=$($VMAFD_CLI get-machine-id --server-name localhost)
SSO_DOMAIN=$($VMAFD_CLI get-domain-name --server-name localhost)
SSO_SITE=$($VMAFD_CLI get-site-name --server-name localhost)
VMDIR_FQDN=$($VMAFD_CLI get-ls-location --server-name localhost | sed -e 's/:443//g' | awk -F'/' '{print $3}')
VMDIR_PORT='389'
VMDIR_DOMAIN_DN="dc=$(echo $SSO_DOMAIN | sed 's/\./,dc=/g')"
VMDIR_MACHINE_PASSWORD=$(/opt/likewise/bin/lwregshell list_values '[HKEY_THIS_MACHINE\services\vmdir]' | grep dcAccountPassword | awk -F'  ' '{print $NF}' | awk '{print substr($0,2,length($0)-2)}' | sed -e 's/\\"/"/g' -e 's/\\\\/\\/g')
VMDIR_MACHINE_ACCOUNT_DN=$(/opt/likewise/bin/lwregshell list_values '[HKEY_THIS_MACHINE\services\vmdir]' | grep '"dcAccountDN"' | awk -F'  ' '{print $NF}' | awk '{print substr($0,2,length($0)-2)}')
VMDIR_USER_UPN_DEFAULT="administrator@$SSO_DOMAIN"
VMDIR_USER=''
VMDIR_USER_UPN=''
VMDIR_USER_PASSWORD=''
if [ $NODE_TYPE != 'management' ]; then
   PSC_LOCATION='localhost'
else
   PSC_LOCATION=$VMDIR_FQDN
fi
HOSTUPDATE_ISSUER_EXPECTED='DigiCert TLS RSA SHA256 2020 CA1'
UNSUPPORTED_SIGNATURE_ALGORITHMS='md2WithRSAEncryption|md5WithRSAEncryption|RSASSA-PSS|dsaWithSHA1|ecdsa_with_SHA1|sha1WithRSAEncryption'
CERT_HASHES=()
TRUSTED_ROOT_CHAIN=''
VMCA_REPLACE='SELF-SIGNED'
STS_REPLACE='VMCA-SIGNED'
MACHINE_SSL_REPLACE='VMCA-SIGNED'
SOLUTION_USER_REPLACE='VMCA-SIGNED'
VMDIR_REPLACE='VMCA-SIGNED'
AUTH_PROXY_REPLACE='VMCA-SIGNED'
AUTO_DEPLOY_CA_REPLACE='SELF-SIGNED'

# CSR defaults and variables
VMCA_CN_DEFAULT='CA'
CSR_COUNTRY_DEFAULT='US'
CSR_ORG_DEFAULT='VMware'
CSR_ORG_UNIT_DEFAULT='VMware Engineering'
CSR_STATE_DEFAULT='California'
CSR_LOCALITY_DEFAULT='Palo Alto'

# script workflow
preStartOperations $@

operationMenu

processOperationMenu