jmanteau

Mon coin de toile - A piece of Web

Shell Utilities for Checking Certificates

Posted: Oct 25, 2023
Shell Utilities for Checking Certificates

For those frequently examining SSL/TLS certificates, the openssl command often becomes a recurring presence in the shell history. However, while openssl is powerful, it often requires users to juggle between various parameters like host, servername, and port, not to mention the subtle annoyance of needing to provide just the host, rather than a more intuitive URL.

Recognizing this, I’ve crafted a shell function/alias to streamline the process. This tool is designed to accept both hosts and URLs as parameters, providing flexibility to the user. Moreover, it effortlessly handles ports, whether they’re explicitly defined in the host or embedded within the URL.

Functions:

Here is examples of the usage (output abbreviated here):

image-20231121162626824

❯ showcert  https://example.com:443/main.html -v
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            0c:1f:cb:18:45:18:c7:e3:86:67:41:23:6d:6b:73:f1
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1
        Validity
            Not Before: Jan 13 00:00:00 2023 GMT
            Not After : Feb 13 23:59:59 2024 GMT
        Subject: C = US, ST = California, L = Los Angeles, O = Internet\C2\A0Corporation\C2\A0for\C2\A0Assigned\C2\A0Names\C2\A0and\C2\A0Numbers, CN = www.example.org
[...]
            X509v3 Authority Key Identifier:
                B7:6B:A2:EA:A8:AA:84:8C:79:EA:B4:DA:0F:98:B2:C5:95:76:B9:F4
            X509v3 Subject Key Identifier:
                B0:93:3F:E8:17:82:FD:6C:B2:B6:17:87:CB:E3:80:FE:82:9B:01:9E
            X509v3 Subject Alternative Name:
                DNS:www.example.org, DNS:example.net, DNS:example.edu, DNS:example.com, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net
[...]
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        59:e4:4a:d8:a9:82:ba:9a:4a:f1:63:0c:6d:76:26:75:b3:3c:
        74:be:c5:f7:3d:a7:91:92:f8:cf:06:2d:58:10:ed:f3:b8:d6:
        fc:6c:ff:13:96:32:cd:4f:e9:87:24:85:0b:74:a2:c2:f6:0f:
        f5:a7:d8:7d:76:8a:ae:e9:c9:58:2b:6e:00:6f:b9:cd:24:ee:
        c4:42:c5:4c:16:85:9d:34:61:39:23:bf:c6:8e:95:c9:84:a9:
        b2:e5:41:0f:44:78:d7:95:b9:cf:d9:74:bf:58:4f:e7:16:ff:
        7c:40:30:c4:6c:4e:22:4d:cb:83:67:3a:93:bf:2b:c5:c5:9c:
        1a:f2:43:a1:25:3b:84:f6:f7:53:6e:a8:85:ae:de:14:74:91:
        30:06:0d:f2:07:d4:c4:08:ba:43:64:c5:e2:3f:da:ac:c5:41:
        af:a4:37:e8:42:76:74:f7:13:bb:4a:7d:36:59:81:9b:c7:44:
        df:89:73:b9:33:42:e8:60:c2:4d:61:5d:12:5a:10:f6:ef:ff:
        33:89:14:50:e8:d6:9f:c6:b9:5c:2b:35:db:ad:ed:dd:36:b6:
        25:f2:95:8a:ac:69:3f:9a:fe:1a:f8:15:28:6d:ea:18:5a:c2:
        d2:62:18:af:40:78:b5:fa:5e:09:8f:53:f9:cc:f8:23:a1:83:
        31:23:f4:c6
        
❯ showchain https://example.com:443/main.html
subject=C = US, ST = California, L = Los Angeles, O = Internet\C2\A0Corporation\C2\A0for\C2\A0Assigned\C2\A0Names\C2\A0and\C2\A0Numbers, CN = www.example.org
issuer=C = US, O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1
subject=C = US, O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1
issuer=C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA        

You can find below the snippets to add to your respective shell configuration :

.zshrc for ZSH

##############################################################
# Function: showcert
# 
# Description:
#   Retrieves the SSL/TLS certificate for a given host or URL and 
#   displays detailed information about the certificate. This function 
#   simplifies the usage of the `openssl` tool by allowing users to input 
#   either a host, a full URL, or a host with a specified port.
#   
# Usage:
#   showcert <host|URL> (-v)
#   
# Parameters:
#   host|URL: The host or full URL of the server to retrieve the certificate 
#             from. A port can be specified using the format: host:port 
#             or https://url:port/path
#   v       : verbose output for the certificate with details
#   
# Examples:
#   showcert example.com
#   showcert https://example.com/main.html
#   showchain https://example.com:443/main.html
#   showchain example.com:443
#   showcert example.com -v
#   
# Note:
#   This function uses the `openssl` tool. Ensure that `openssl` is installed 
#   and available in your PATH.
##############################################################

certfunc() {
    verbose=false

    if [[ "$2" == "-v" ]]; then
        verbose=true
    fi

    host_and_port="${${${1#https://}#http://}%%/*}"
    host="${host_and_port%%:*}"
    port="${host_and_port#*:}"
    [ "$port" = "$host" ] && port=443

    cert=$(echo | openssl s_client -showcerts -servername $host -connect $host:$port 2>/dev/null | openssl x509 -outform pem)

    if $verbose; then
        echo "$cert" | openssl x509 -noout -text
    else
        name=$(echo "$cert" | openssl x509 -noout -subject | cut -d'/' -f 6- | sed 's/subject=//')
        all_san=$(echo "$cert" | openssl x509 -noout -text | grep -A1 'Subject Alternative Name:' | tail -n1 | sed 's/DNS:/\n  DNS:/g')
        start_validity=$(echo "$cert" | openssl x509 -noout -startdate | cut -d'=' -f2)
        end_validity=$(echo "$cert" | openssl x509 -noout -enddate | cut -d'=' -f2)

        if [[ "$(uname)" == "Darwin" ]]; then
            start_date=$(date -j -f "%b %d %T %Y %Z" "$start_validity" +"%Y-%m-%d")
            end_date=$(date -j -f "%b %d %T %Y %Z" "$end_validity" +"%Y-%m-%d")
            days_left=$(( ( $(date -j -f "%Y-%m-%d" "$end_date" +%s) - $(date +%s) ) / 86400 ))
        else
            start_date=$(date -d "$start_validity" +"%Y-%m-%d")
            end_date=$(date -d "$end_validity" +"%Y-%m-%d")
            days_left=$(( ( $(date -d "$end_date" +%s) - $(date +%s) ) / 86400 ))
        fi

        hash_algo=$(echo "$cert" | openssl x509 -noout -text | grep "Signature Algorithm" | head -n 1 | cut -d: -f2 | xargs)
        pubkey_info=$(echo "$cert" | openssl x509 -noout -text | grep "Public Key Algorithm" | cut -d: -f2 | xargs)
        pubkey_size=$(echo "$cert" | openssl x509 -noout -text | grep "Public-Key" | awk '{print $2}' | tr -d '(')

        echo -e "\e[1;34mCertificate Information:\e[0m"

        {
            echo -e "\e[32mName\e[0m;$name"
            echo -e "\e[32mSAN\e[0m;$all_san"
            echo -e "\e[32mValidity Period\e[0m;$start_date to $end_date (expires in $days_left days)"
            echo -e "\e[32mHash Algorithm\e[0m;$hash_algo"
            echo -e "\e[32mPublic Key Info\e[0m;$pubkey_info ($pubkey_size bits)"
        } | column -t -s ";"

        echo -e "\n\e[1;34mIssuer Chain:\e[0m"
        chainfunc $1
    fi
}
alias showcert=certfunc


##############################################################
# Function: showchain
# 
# Description:
#   Retrieves the certificate chain for a given host or URL and 
#   displays the subject and issuer of each certificate in the chain.
#   This function is designed to simplify the `openssl` command usage,
#   allowing users to input either a host, a full URL, or a host with 
#   a specified port.
#   
# Usage:
#   showchain <host|URL>
#   
# Parameters:
#   host|URL: The host or full URL of the server to retrieve the certificate 
#             chain from. A port can be specified using the format: host:port 
#             or https://url:port/path
#   
# Examples:
#   showchain example.com
#   showchain https://example.com/main.html
#   showchain https://example.com:443/main.html
#   showchain example.com:443
#   
# Note:
#   This function uses the `openssl` tool. Ensure that `openssl` is installed 
#   and available in your PATH.
##############################################################

chainfunc() {
    host_and_port="${${${1#https://}#http://}%%/*}"
    host="${host_and_port%%:*}"
    port="${host_and_port#*:}"
    [ "$port" = "$host" ] && port=443
    indent="\t"
    skip_first=true

    echo | openssl s_client -showcerts -servername $host -connect $host:$port 2>/dev/null | awk '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/' | while read -r line; do
        cert="${cert}${line}\n"
        if [[ $line == "-----END CERTIFICATE-----" ]]; then
            subj=$(echo -e "$cert" | openssl x509 -noout -subject 2>/dev/null | cut -d'=' -f2-)
            iss=$(echo -e "$cert" | openssl x509 -noout -issuer 2>/dev/null | cut -d'=' -f2-)
            if $skip_first; then
                skip_first=false
            else
                echo -e "${indent}$subj ->"
                indent="${indent}\t"
            fi
            cert=""
        fi
    done
    echo -e "${indent}$iss"
}
alias showchain=chainfunc

.bashrc for Bash

Info: Bash version is only with the full details

##############################################################
# Function: showcert
# 
# Description:
#   Retrieves the SSL/TLS certificate for a given host or URL and 
#   displays detailed information about the certificate. This function 
#   simplifies the usage of the `openssl` tool by allowing users to input 
#   either a host, a full URL, or a host with a specified port.
#   
# Usage:
#   showcert <host|URL>
#   
# Parameters:
#   host|URL: The host or full URL of the server to retrieve the certificate 
#             from. A port can be specified using the format: host:port 
#             or https://url:port/path
#   
# Examples:
#   showcert example.com
#   showcert https://example.com/main.html
#   showchain https://example.com:443/main.html
#   showchain example.com:443
#   
# Note:
#   This function uses the `openssl` tool. Ensure that `openssl` is installed 
#   and available in your PATH.
##############################################################

certfunc() {
    host_and_port="${1#https://}"
    host_and_port="${host_and_port#http://}"
    host_and_port="${host_and_port%%/*}"
    host="${host_and_port%%:*}"
    port="${host_and_port#*:}"
    [ "$port" = "$host" ] && port=443
    echo | openssl s_client -showcerts -servername "$host" -connect "$host:$port" 2>/dev/null | openssl x509 -inform pem -noout -text
}
alias showcert=certfunc

##############################################################
# Function: showchain
# 
# Description:
#   Retrieves the certificate chain for a given host or URL and 
#   displays the subject and issuer of each certificate in the chain.
#   This function is designed to simplify the `openssl` command usage,
#   allowing users to input either a host, a full URL, or a host with 
#   a specified port.
#   
# Usage:
#   showchain <host|URL>
#   
# Parameters:
#   host|URL: The host or full URL of the server to retrieve the certificate 
#             chain from. A port can be specified using the format: host:port 
#             or https://url:port/path
#   
# Examples:
#   showchain example.com
#   showchain https://example.com/main.html
#   showchain https://example.com:443/main.html
#   showchain example.com:443
#   
# Note:
#   This function uses the `openssl` tool. Ensure that `openssl` is installed 
#   and available in your PATH.
##############################################################

chainfunc() {
    local proto="${1%%://*}"
    local host_and_port="${1#$proto://}"
    host_and_port="${host_and_port%%/*}"
    local host="${host_and_port%%:*}"
    local port="${host_and_port#*:}"
    [ "$port" = "$host" ] && port=443

    local cert_data=""
    echo | openssl s_client -showcerts -servername "$host" -connect "$host:$port" 2>/dev/null | while IFS= read -r line; do
        cert_data+="$line"$'\n'
        if [[ "$line" == "-----END CERTIFICATE-----" ]]; then
            echo "$cert_data" | openssl x509 -noout -subject -issuer
            cert_data=""
        fi
    done
}
alias showchain=chainfunc