opexxx

certchainresolver.sh

Jan 18th, 2016
211
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 4.25 KB | None | 0 0
  1. #!/bin/sh
  2.  
  3. # SSL certificate chain resolver
  4. #
  5. # https://github.com/zakjan/cert-chain-resolver
  6. #
  7. # Copyright (c) 2015 Jan Žák (http://zakjan.cz)
  8. # The MIT License (MIT).
  9.  
  10. set -eu
  11.  
  12.  
  13. alias command_exists="type >/dev/null 2>&1"
  14. alias echoerr="echo >&2"
  15.  
  16.  
  17. INPUT_FILENAME="/dev/stdin"
  18. OUTPUT_FILENAME="/dev/stdout"
  19. OUTPUT_DER_FORMAT=""
  20. OUTPUT_INTERMEDIATE_ONLY=""
  21.  
  22.  
  23. cert_normalize_to_pem() {
  24.   # bash variables can't contain binary data with null-bytes, so it needs to be stored encoded, and decoded before use
  25.   local INPUT="$(openssl base64)"
  26.  
  27.   if CERT="$(echo "$INPUT" | openssl base64 -d | openssl x509 -inform pem -outform pem 2>/dev/null)"; then
  28.     echo "$CERT"
  29.     return
  30.   fi
  31.  
  32.   if CERT="$(echo "$INPUT" | openssl base64 -d | openssl x509 -inform der -outform pem 2>/dev/null)"; then
  33.     echo "$CERT"
  34.     return
  35.   fi
  36.  
  37.   echoerr "Invalid certificate"
  38.   return 1
  39. }
  40.  
  41. cert_pem_to_text() {
  42.   # certificate is parsed from OpenSSL text output. dirty solution, but it works
  43.   openssl x509 -inform pem -noout -text
  44. }
  45.  
  46. cert_get_subject() {
  47.   cert_pem_to_text | awk 'BEGIN {FS="Subject: "} NF==2 {print $2; exit}'
  48. }
  49.  
  50. cert_get_issuer_url() {
  51.   cert_pem_to_text | awk 'BEGIN {FS="CA Issuers - URI:"} NF==2 {print $2; exit}'
  52. }
  53.  
  54.  
  55.  
  56. usage() {
  57.   echoerr "SSL certificate chain resolver"
  58.   echoerr
  59.   echoerr "Usage: ./cert-chain-resolver.sh [OPTION]... [INPUT_FILE]"
  60.   echoerr
  61.   echoerr "Read certificate from stdin, or INPUT_FILE if specified. The input certificate can be in either DER or PEM format."
  62.   echoerr "Write certificate bundle to stdout in PEM format, with both leaf and intermediate certificates."
  63.   echoerr
  64.   echoerr "    -d|--der"
  65.   echoerr
  66.   echoerr "        output DER format"
  67.   echoerr
  68.   echoerr "    -i|--intermediate-only"
  69.   echoerr
  70.   echoerr "        output intermediate certificates only, without leaf certificate"
  71.   echoerr
  72.   echoerr "    -o|--output OUTPUT_FILE"
  73.   echoerr
  74.   echoerr "        write output to OUTPUT_FILE"
  75. }
  76.  
  77. check_dependencies() {
  78.   if ! command_exists wget; then
  79.     echoerr "Error: wget is required"
  80.     return 1
  81.   fi
  82.  
  83.   if ! command_exists openssl; then
  84.     echoerr "Error: openssl is required"
  85.     return 1
  86.   fi
  87. }
  88.  
  89. parse_opts() {
  90.   while [ "$#" -gt "0" ]; do
  91.     case "$1" in
  92.       -d|--der) OUTPUT_DER_FORMAT=1; shift;;
  93.       -i|--intermediate-only) OUTPUT_INTERMEDIATE_ONLY=1; shift;;
  94.       -o|--output) OUTPUT_FILENAME="$2"; shift 2;;
  95.       -h|--help) usage; return 1;;
  96.       -*) echoerr "Unknown option $1"; echoerr "See --help for accepted options"; return 1;;
  97.       *) break;;
  98.     esac
  99.   done
  100.  
  101.   if [ "$#" -gt "0" ] && [ -n "$1" ]; then
  102.     INPUT_FILENAME="$1"
  103.     shift
  104.   fi
  105.   if [ "$#" -gt "0" ] && [ -n "$1" ]; then
  106.     # deprecated, pass output filename in --output argument instead
  107.     OUTPUT_FILENAME="$1"
  108.     shift
  109.   fi
  110. }
  111.  
  112. main() {
  113.   if ! check_dependencies; then
  114.     return 1
  115.   fi
  116.  
  117.   if ! parse_opts "$@"; then
  118.     return 1
  119.   fi
  120.  
  121.   local CURRENT_CERT
  122.   local CURRENT_SUBJECT
  123.   local ISSUER_CERT_URL
  124.  
  125.   > "$OUTPUT_FILENAME" # clear output file
  126.  
  127.   # extract the first certificate from input file, to make this script idempotent; normalize to PEM
  128.   if ! CURRENT_CERT="$(cert_normalize_to_pem < "$INPUT_FILENAME")"; then
  129.     return 1
  130.   fi
  131.  
  132.   # loop over certificate chain using AIA extension, CA Issuers field
  133.   I="0"
  134.   while true; do
  135.     # get certificate subject
  136.     CURRENT_SUBJECT="$(echo "$CURRENT_CERT" | cert_get_subject)"
  137.     echoerr "$((I+1)): $CURRENT_SUBJECT"
  138.  
  139.     # append certificate to result
  140.     if [ "$I" -gt 0 ] || [ -z "$OUTPUT_INTERMEDIATE_ONLY" ]; then
  141.       if [ -n "$OUTPUT_DER_FORMAT" ]; then
  142.         echo "$CURRENT_CERT" | openssl x509 -inform pem -outform der >> "$OUTPUT_FILENAME"
  143.       else
  144.         echo "$CURRENT_CERT" >> "$OUTPUT_FILENAME"
  145.       fi
  146.     fi
  147.  
  148.     # get issuer's certificate URL
  149.     ISSUER_CERT_URL="$(echo "$CURRENT_CERT" | cert_get_issuer_url)"
  150.     if [ -z "$ISSUER_CERT_URL" ]; then
  151.       break
  152.     fi
  153.  
  154.     # download issuer's certificate, normalize to PEM
  155.     if ! CURRENT_CERT="$(wget -q -O - "$ISSUER_CERT_URL" | cert_normalize_to_pem)"; then
  156.       return 1
  157.     fi
  158.  
  159.     I="$((I+1))"
  160.   done
  161.  
  162.   echoerr "Certificate chain complete."
  163.   echoerr "Total $((I+1)) certificate(s) found."
  164. }
  165.  
  166. main "$@"
Add Comment
Please, Sign In to add comment