- #! /bin/bash
- progname=$(basename $0)
- OPT_SUMMARY=0
- OPT_SOME=1
- OPT_MOST=2
- OPT_ALL=3
- EXIT_SUCCESS=0
- EXIT_FILE_OPEN=1
- EXIT_CHECK=2
- EXIT_HELP=3
- declare -a options
- summaryopts="-subject -dates -issuer -noout"
- someopts="-text -fingerprint -noout -certopt no_pubkey,no_sigdump,no_version,no_signame,no_serial"
- mostopts="-text -fingerprint -noout -certopt no_pubkey,no_sigdump"
- allopts="-text -fingerprint -noout"
- showDetails=$OPT_SUMMARY
- doCheck=0
- exitStatus=$EXIT_SUCCESS
- options[$OPT_SUMMARY]=$summaryopts
- options[$OPT_SOME]=$someopts
- options[$OPT_MOST]=$mostopts
- options[$OPT_ALL]=$allopts
- # Set the exit status to indicate a consistency problem.
- # This is a function in case we decide that we don't want
- # to overwrite a more serious error.
- SetCheckError ()
- {
- exitStatus=$EXIT_CHECK
- }
- x509_show_cert ()
- {
- local cert="$1"
- shift
- local opts=$(eval echo $*)
- eval openssl x509 $opts <<< "$cert"
- }
- x509_show_chain ()
- {
- local filename="$1"
- local showDetails=$2
- local line=
- local cert=
- local newline=$'\n'
- local firstInFile=1
- shift
- shift
- local opts=$*
- local line=
- local line2=
- local numCertsInFile=0
- local lastIdx=0
- local numSelfSigned=0
- local seenSelfSignedIdx=0
- local seenExtraCert=0
- local seenCA=0
- local extraCert=0
- local curIdx=0
- local curSubj=""
- local curIssuer=""
- local lastIdx=0
- local lastSubj=""
- local lastIssuer=""
- local nl=$'\n' # set to "" when not wanted
- local NL=$'\n' # never changed
- IFS=$'\n'
- while read line
- do
- if [[ "$line" =~ BEGIN.*CERTIFICATE ]]
- then
- cert="$line"
- while read line
- do
- cert+="$newline$line"
- if [[ "$line" =~ END.*CERTIFICATE ]]
- then
- if [ $firstInFile -eq 1 ]
- then
- firstInFile=0
- else
- echo "***"
- fi
- certInfo=$(x509_show_cert "$cert" $opts)
- echo "Cert #${curIdx}:"
- echo "${certInfo}"
- ((++numCertsInFile))
- # get and check Subject and Issuer from this cert
- while read line2
- do
- case "${line2}" in
- subject=*)
- curSubj=$(echo "${line2}" | sed -e 's/^subject=//')
- ;;
- issuer=*)
- curIssuer=$(echo "${line2}" | sed -e 's/^issuer=//')
- ;;
- esac
- done <<< "${certInfo}"
- #
- # check for errors or anomalies
- #
- if [ $doCheck -ne 0 ]
- then
- nl=$'\n'
- if [ ${numSelfSigned} -ne 0 ]
- then
- seenExtraCert=1
- SetCheckError
- echo "${nl}-- Cert #${curIdx} is after self-signed cert #${seenSelfSignedIdx}, so OpenSSL will ignore it."
- nl=""
- fi
- if [ ${curIdx} -gt 0 ]
- then
- if [ "${lastIssuer}" != "${curSubj}" ]
- then
- SetCheckError
- echo "${nl}-- Cert #${curIdx} did not sign the previous cert #${lastIdx}."
- echo " #${lastIdx}: Previous Issuer=\"${lastIssuer}\""
- echo " #${curIdx}: Current Subject=\"${curSubj}\""
- nl=""
- fi
- fi
- if [ -n "${curIssuer}" -a "${curIssuer}" = "${curSubj}" ]
- then
- ((++numSelfSigned))
- seenSelfSignedIdx=${curIdx}
- echo "${nl}-- Cert #${curIdx} is self-signed."
- nl=""
- fi
- lastSubj="${curSubj}"
- lastIssuer="${curIssuer}"
- curSubj=""
- curIssuer=""
- fi # doCheck
- lastIdx=${curIdx}
- ((++curIdx))
- cert=
- fi
- done
- fi
- done < "$filename"
- exitStatus=$?
- if [ $doCheck -ne 0 ]
- then
- nl=$'\n'
- if [ $numSelfSigned -ne 0 ]
- then
- local lastIdx=$((numCertsInFile - 1))
- [ $lastIdx -lt 0 ] && lastIdx=0
- echo "${nl}>> File \"${filename}\":"
- echo " -- contains ${numSelfSigned} self-signed certificate(s);"
- echo " -- the last self-signed certificate is #${seenSelfSignedIdx};"
- echo " -- the last certificate in the file is #${lastIdx}."
- fi
- if [ ${numSelfSigned} -ne 0 ]
- then
- [ ${seenSelfSignedIdx} -ne ${lastIdx} ] && SetCheckError
- fi
- fi
- }
- CurDetail ()
- {
- local val=$(($1 == $showDetails))
- echo $(boolof $val)
- }
- stringof ()
- {
- echo "\"$1\""
- }
- boolof ()
- {
- if [ -z "$1" ]
- then
- echo "false"
- elif [ $1 -ne 0 ]
- then
- echo "true"
- else
- echo "false"
- fi
- }
- notboolof ()
- {
- if [ -z "$1" ]
- then
- echo "true"
- elif [ $1 -eq 0 ]
- then
- echo "true"
- else
- echo "false"
- fi
- }
- Help ()
- {
- [ -n "$1" ] && echo "${progname}: unknown option \"$arg\""
- cat <<- _EOF_
- ${progname} [opts] files...
- opts:
- -h | --help Show this help, then exit.
- -c | --check Check cert files for problems [current=$(boolof $doCheck)].
- -a | --all Show all info about each cert [current=$(CurDetail $OPT_ALL)].
- -m | --most Show most info about each cert [current=$(CurDetail $OPT_MOST)].
- -s | --some Show some info about each cert [current=$(CurDetail $OPT_SOME)].
- -S | --summary Show summary info about each cert [current=$(CurDetail $OPT_SUMMARY)].
- The "--all", "--most", "--some", and "--summary" options are ordered
- by the amount of detailed info they provide, from most to least.
- Options and files are processed left-to-right, so files are processed
- using only options to their left; thus later files may be processed
- with different options, if desired.
- If no file args are provided then certs are read from stdin.
- Similarly stdin is read when a file arg of "-" is processed.
- Any non-certificate lines in an input file are ignored,
- so ${progname} can be used as a filter in a pipe, e.g.:
- openssl s_client -connect ibm.com:443 < /dev/null | x509-show-chain
- or even:
- openssl s_client -connect ibm.com:443 < /dev/null \\
- | x509-show-chain cert1.crt -c - -S cert2.crt
- ${progname} exit status values:
- 0 No problems were detected.
- 1 Some files were not readable.
- 2 Problems were found within at least one certificate file.
- 3 Invalid parameters were given, or help was requested.
- NOTE: If multiple problems were encountered then
- the exit status will reflect only the last error.
- _EOF_
- exit $EXIT_HELP
- }
- process_args ()
- {
- local opts="$summaryopts"
- local first=1
- for arg in "$@"
- do
- case "$arg" in
- -h | --help)
- Help
- ;;
- -c | --check)
- doCheck=1
- ;;
- -a | --all)
- showDetails=$OPT_ALL
- opts=${options[$OPT_ALL]}
- ;;
- -m | --most)
- showDetails=$OPT_MOST
- opts=${options[$OPT_MOST]}
- ;;
- -s | --some)
- showDetails=$OPT_SOME
- opts=${options[$OPT_SOME]}
- ;;
- -S | --summary)
- showDetails=$OPT_SUMMARY
- opts=${options[$OPT_SUMMARY]}
- ;;
- -?*)
- Help "$arg"
- ;;
- *)
- if [ $first -eq 0 ]
- then
- echo
- else
- first=0
- fi
- [ "$arg" = "-" ] && arg="/dev/stdin"
- echo "=== $arg ==="
- x509_show_chain "$arg" $showDetails $opts
- ;;
- esac
- done
- # read stdin if no file args
- if [ $first -ne 0 ]
- then
- arg="/dev/stdin"
- echo "=== $arg ==="
- x509_show_chain "$arg" $showDetails $opts
- fi
- }
- process_args "$@"
- exit $exitStatus
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 31204 | Will Kreitzmann | Released SDP 2024.2.31193 (2025/01/17). Copy Up using 'p4 copy -r -b perforce_software-sd...p-dev'. « |
2 months ago | |
//guest/perforce_software/sdp/dev/Unsupported/setup/x509-show-chain | |||||
#2 | 31160 | Mark Wittenberg | I squeezed in some time to improve my script: * Added a --check option to check cert file...s for problems and report them: - certificates following a self-signed cert - certificates not signed by the subsequent cert - identify self-signed certs * It's now usable in a pipeline: - If no file arguments are given then it reads from stdin - Any file argument of - read stdin when that argument is processed - All non-certificate lines in the input are ignored so you can pipe the output of openssl s_client to it without needing to pre-process the data * To make it easier to use it from another script, it now exits with a meaningful exit status: - 0 No problems were detected. - 1 Some files were not readable. - 2 Problems were found within at least one certificate file. - 3 Invalid parameters were given, or help was requested. * I expanded and improved the help text * I renamed the -d | --details option to -s | --some to make the order of increasing detail more obvious - the --all, --most, --some, and --summary options are ordered by the amount of detailed info they provide, from most to least. - this conflicted with the -s | --summary option so I renamed that to -S | --summary « |
2 months ago | |
#1 | 31107 | Mark Wittenberg | Added x509-show-chain cert script. | 2 months ago |