Script/wrapper 对于将下载整个证书链并将每个证书保存到单独文件的 openssl?

Script/wrapper for openssl which will download an entire certificate chain, and save each cert to a serparate file?

我见过如下示例,它使用 openssl 下载一个证书,但我正在寻找一个将下载整个证书链并将每个证书链保存为单独文件的示例。理想情况下,最好保存每个与 CN 名称匹配的证书文件。

echo -n | openssl s_client -connect HOST:PORTNUMBER | \
  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > /tmp/$SERVERNAME.cert

来自OpenSSL docs

-showcerts: display the whole server certificate chain: normally only the server certificate itself is displayed.

如果您真的需要多个文件,您只需要使用 csplit(或 awk)之类的东西通过匹配每个证书的开头(类似于正则表达式一个在你的命令中)。

通常,我不喜欢 "write a program for me" 在这里输入请求,但在这种情况下,我意识到我自己也想拥有这样的东西,所以我继续写了它。这不是特别完善,它只是将证书转储到当前目录中。我添加的一件好事是它会自动为 openssl s_client 支持它的非 ssl 服务启用适当的 starttls 模式(即 smtp、imap、pop3、ftp 和 xmpp)。 .它还包括证书文件中每个证书的主题和颁发者的 openssl 打印摘要(在 -----BEGIN CERTIFICATE----- 行之前)。

#!/bin/bash

# Connect to an SSL service and extract its certificates to files in the
# current directory.

usage() {
    echo "Usage:" >&2
    echo "  $(basename "[=10=]") server[:port] [other s_client flags]" >&2
    echo "  $(basename "[=10=]") protocol://server [other s_client flags]" >&2
    exit 1
}


# Parse command-line arguments
openssl_options=()
if (( $# < 1 )); then    # No server address specified
    usage

elif [[ "" = *://* ]]; then   # proto://domain format
    port="${1%%://*}" # Just use the protocol name as the port; let openssl look it up
    server="${1#*://}"
    server="${server%%/*}"

elif [[ "" = *:* ]]; then    # Explicit port number supplied
    port="${1#*:}"
    server="${1%:*}"

else # No port number specified; default to 443 (https)
    server=""
    port=443
fi

# If the protocol/port specified is a non-SSL service that s_client supports starttls for, enable that
if [[ "$port" = "smtp" || "$port" = "pop3" || "$port" = "imap" || "$port" = "ftp" || "$port" = "xmpp" ]]; then
    openssl_options+=(-starttls "$port")
elif [[ "$port" = "imap3" ]]; then
    openssl_options+=(-starttls imap)
elif [[ "$port" = "pop" ]]; then
    port=pop3
    openssl_options+=(-starttls pop3)
fi


# Any leftover command-line arguments get passed to openssl s_client
shift
openssl_options+=("$@")

# Try to connect and collect certs
connect_output=$(openssl s_client -showcerts -connect "$server:$port" "${openssl_options[@]}" </dev/null) || {
    status=$?
    echo "Connection failed; exiting" >&2
    exit $status
}
echo

nl=$'\n'

state=begin
while IFS= read -r line <&3; do
    case "$state;$line" in
      "begin;Certificate chain" )
        # First certificate is about to begin!
        state=reading
        current_cert=""
        certname=""
        ;;

      "reading;-----END CERTIFICATE-----" )
        # Last line of a cert; save it and get ready for the next
        current_cert+="${current_cert:+$nl}$line"

        # Pick a name to save the cert under
        if [[ "$certname" = */CN=* ]]; then
            certfile="${certname#*/CN=}"
            certfile="${certfile%%/*}"
            certfile="${certfile// /_}.crt"
        elif [[ -n "$certname" && "$certname" != "/" ]]; then
            certfile="${certname#/}"
            certfile="${certfile//\//:}"
            certfile="${certfile// /_}.crt"
        else
            echo "???No name found for certificate" >&2
            certfile="Unknown_certificate.crt"
        fi

        # ...and try to save it
        if [[ -e "$certfile" ]]; then
            echo "Already exists: $certfile" >&2
        else
            echo "Saving cert: $certfile"
            echo "$current_cert" >"$certfile"
        fi

        state=reading
        current_cert=""
        certname=""
        ;;

      "reading; "*" s:"* )
        # This is the cert subject summary from openssl
        certname="${line#*:}"
        current_cert+="${current_cert:+$nl}Subject: ${line#*:}"
        ;;

       "reading; "*" i:"* )
        # This is the cert issuer summary from openssl
        current_cert+="${current_cert:+$nl}Issuer:  ${line#*:}"
        ;;

      "reading;---" )
        # That's the end of the certs...
        break
        ;;

      "reading;"* )
        # Otherwise, it's a normal part of a cert; accumulate it to be
        # written out when we see the end
        current_cert+="$nl$line"
        ;;
    esac
done 3<<< "$connect_output"