#!/usr/bin/bash

# SSL info

show_help() {
cat << EOF
Usage: $0 [info|infoall|subject|modulus|check|match] openssl_crt_key_files
Examples:
ssli filename
	show info for filename (with pager)
ssli [info|infoall] filename
	show certificate information
ssli https://url...
	show online info from https web
ssli [imaps|pop3s|smtps|submission]://hostname
	show online info from mail or other tcp based service
ssli expire URL_as_above
ssli [check|match] file1 file2
	compare 2 modulus files:
ssli modulus file
	show modulus
ssli ciphers URL
	show cipher list
ssli convert filename.[crt|der] [filename.pem]
	convert der to pem
ssli csr domain.name [altdomains ...]
	generate private key and csr:
ssli refresh_csr|refresh filename.crt [altdomains ...]
	refresh CSR file from CRT and add altdomains
ssli download hostname Self_Enrollment_Certificate_ID
	download already issued certificate from CA
EOF
exit 0
}

CERT_OPTS="-certopt no_signame,no_pubkey,no_sigdump,no_extensions -ext subjectAltName"

ssl_read() {
  if [ ! -f "$1" ]; then
    echo "ERROR: File not found: $1!"
    exit 1
  fi
  if grep -q '^-----BEGIN\( .* \| \)PRIVATE KEY-----' "$1"; then
    openssl rsa -noout -in "$@"
  elif grep -q '^-----BEGIN CERTIFICATE REQUEST-----' "$1"; then
    openssl req -noout -in "$@"
  elif grep -q '^-----BEGIN X509 CRL-----' "$1"; then
    openssl crl -noout -in "$@"
  elif grep -q '^-----BEGIN' "$1"; then
    openssl x509 -noout -in "$@"
  else
    # der format
    openssl x509 -noout -inform der -in "$@"
    echo "DER format detected!"
  fi
}

ssl_read_all() {
  txt=""
  while read line; do
    if [ "${line//-----END}" != "$line" ]; then
        txt="$txt$line\n"
        printf -- "$txt" \
          | openssl x509 -noout -text $CERT_OPTS
        txt=""
    else
        txt="$txt$line\n"
    fi
  done < "$1"
}

ssl_read_tcp() {
  if [ "$2" = "submission" -o "$2" = "587" ]; then
    tls_args="-starttls smtp"
  else
    tls_args=""
  fi
  if [[ "$1" =~ ^[0-9.:]*$ ]]; then
    servername=""
  else
    servername="-servername $1"
  fi
  openssl s_client -showcerts $servername -connect "$1:$2" $tls_args \
    </dev/null 2>/dev/null \
    | ssl_read_all /dev/stdin
}

check_modulus() {
  m1="`ssl_read \"$1\" -modulus | cut -d= -f2`"
  m2="`ssl_read \"$2\" -modulus | cut -d= -f2`"
  if [ "$m1" = "$m2" ]; then
    echo "MATCH OK"
    if [ "$3" = "-d" ]; then
      echo "$m1"
      echo "$m2"
    fi
    exit 0
  else
    echo "$m1"
    echo "$m2"
    echo "NO MATCH"
    exit 1
  fi
}

ssl_convert() {
  SRC="$1"
  if [ "$2" ]; then
    TGT="$2"
  else
    TGT="${SRC%.*}.pem"
  fi
  echo "Converting $SRC -> $TGT ..."
  openssl x509 -inform der -in "$SRC" -out "$TGT"
}

openssl_config_alt_names() {
  # openssl_config_alt_names dns_name1 dns_name2 ...
  echo ".include /etc/pki/tls/openssl.cnf"
  echo "[ req ]"
  echo "req_extensions = v3_req"
  echo "[ v3_req ]"
  echo "subjectAltName = @alt_names"
  echo "[ alt_names ]"
  N=0
  for domain in "$@"; do
    N=$((N+1))
    echo "DNS.$N = $domain"
  done
}

ssl_csr() {
  DOMAIN="${1##\*.}"
  echo "CSR request for: $1 [$DOMAIN]"
  if [ -f "$DOMAIN.key" ]; then
    echo "Private key $DOMAIN.key already exists!"
    exit 1
  fi
  SUBJ="/CN=$1"
  shift
  if [ "$1" ]; then
    openssl req -new -nodes -newkey rsa:2048 \
      -subj "$SUBJ" -keyout "$DOMAIN.key" -out "$DOMAIN.csr" \
      -config <(openssl_config_alt_names $@)
  else
    openssl req -new -nodes -newkey rsa:2048 \
      -subj "$SUBJ" -keyout "$DOMAIN.key" -out "$DOMAIN.csr" $CONFIG
  fi
  echo
  openssl req -text -noout -sha256 -in "$DOMAIN.csr" \
    | grep -e 'CN *=' -e "DNS:" | sed 's/[ \t]*//g'
  echo
  cat "$DOMAIN.csr"
}

ssl_refresh_csr_basic_only() { # unused
  # Refresh CSR file from CRT contents.
  # This works only for basic information, doesn't copy alternative names.
  DOMAIN="${1##\*.}"
  if [ -f "$DOMAIN.key" ]; then
    echo "Using private key $DOMAIN.key ..."
  else
    openssl genrsa -out "$DOMAIN.key" 2048
  fi
  openssl x509 -in "$2" -x509toreq \
    -signkey "$DOMAIN.key" \
    -out "$DOMAIN.csr"
}

ssl_refresh_csr() {
  # Refresh CSR file from CRT contents.
  #DOMAIN="${1##\*.}"
  CRT="$1"
  shift
  SUBJ="`openssl x509 -in $CRT -noout -subject | sed 's/^subject=//'`"
  DOMAIN="`echo \"$SUBJ\" | sed 's/^.* CN = \([a-z0-9.-]*\)/\1/'`"
  SUBJ="/CN=$DOMAIN"
  if [ -f "$DOMAIN.key" ]; then
    echo "Private key $DOMAIN.key already exists!"
    return
  fi
  ALT_NAMES="`openssl x509 -in $CRT -noout -text | grep -oP '(?<=DNS:)[^,]+'`"
  if [ "$1" -o "$ALT_NAMES" ]; then
    openssl req -new -nodes -newkey rsa:2048 \
      -subj "$SUBJ" -keyout "$DOMAIN.key" -out "$DOMAIN.csr" \
      -config <(openssl_config_alt_names $ALT_NAMES $@)
  else
    openssl req -new -nodes -newkey rsa:2048 \
      -subj "$SUBJ" -keyout "$DOMAIN.key" -out "$DOMAIN.csr" $CONFIG
  fi
  cat "$DOMAIN.csr"
}

download_sanet() {
  if [ -z "$2" ]; then
    echo "Usage: ssli download hostname Self_Enrollment_Certificate_ID"
    exit
  fi
  URL="https://cert-manager.com/customer/SANET/ssl?action=download&"
  curl "$URL&sslId=$2&format=pemia" > "$1.pem"
  curl "$URL&sslId=$2&format=x509CO" > "$1.crt"
  curl "$URL&sslId=$2&format=x509IOR" > "$1.int"
  ssl_read_all "$1.crt"
  check_modulus "$1.key" "$1.crt"
}

list_ciphers() {
  nmap --script ssl-enum-ciphers -p "$2" "$1"
}

get_hostname() {
  echo "`echo $1 | awk -F[:/] '{ print $4 }'`"
}

get_proto() {
  echo "`echo $1 | cut -d: -f1`"
}

expire_filter() {
  d0=$(date +%s)
  grep "Not After : " | sort -k7n -k4M -k5n -k6 \
    | while read not after x date; do
        d1=$(date -d "$date" +%s)
        echo $date, $(( (d1 - d0) / 86400 )) days
      done
}

url_command="ssl_read_tcp"
filter="cat"

while true; do
  case "$1" in
    ""|-h|--help|help)
          show_help
          break
          ;;
    i|info)
          shift
          for i in "$@"; do
                  echo "$i:"
                  ssl_read "$i" -text
          done
          break
          ;;
    ia|infoall)
          shift
          for i in "$@"; do
                  echo "$i:"
                  ssl_read_all "$i"
          done
          break
          ;;
    web|info_web)
          shift
          for i in "$@"; do
                  echo "$i:"
                  ssl_read_tcp "$i" 443
          done
          break
          ;;
    "https://"*|"http://"*)
          $url_command "`get_hostname $1`" 443 2>&1 | $filter
          shift
          break
          ;;
    mail|info_mail)
          shift
          ssl_read_tcp "$@"
          break
          ;;
    "pop3s://"*|"imaps://"*|"smtps://"*|"submission://"*)
          $url_command "`get_hostname $1`" "`get_proto $1`" 2>&1 | $filter
          shift
          break
          ;;
    m|mod*)
          shift
          for i in "$@"; do
                  echo "$i:"
                  ssl_read "$i" -modulus
          done
          break
          ;;
    check|match)
          shift
          check_modulus "$@"
          break
          ;;
    s|sub*)
          shift
          for i in "$@"; do
                  echo "$i:"
                  ssl_read "$i" -subject | sed 's/^subject *= *//i'
          done
          break
          ;;
    expire*)
          shift
          filter="expire_filter"
          ;;
    ciphers|cip*)
          shift
          url_command="list_ciphers"
          ;;
    convert)
          shift
          ssl_convert "$@"
          break
          ;;
    csr)
          shift
          ssl_csr "$@"
          break
          ;;
    refresh_csr|refresh)
          shift
          ssl_refresh_csr "$@"
          break
          ;;
    download)
          shift
          download_sanet "$@"
          break
          ;;
    *)
          for i in "$@"; do
                  ssl_read "$i" -text | less -FX
          done
          break
          ;;
  esac
done
