web-dev-qa-db-ja.com

SSL / TLS証明書のサブジェクトの別名を確認する方法は?

SAN SSL証明書のサブジェクトの別名をプログラムで確認する方法はありますか?

たとえば、次のコマンドを使用すると、すべてのSANではなく多くの情報を取得できます。

openssl s_client -connect www.website.com:443 

どうもありがとうございました!

51
JoeSlav

証明書のサブジェクトの別名(SAN)を取得するには、次のコマンドを使用します。

openssl s_client -connect website.com:443 | openssl x509 -noout -text | grep DNS:

最初に、このコマンドは必要なサイト(website.com、SSLのポート443)に接続します。

openssl s_client -connect website.com:443

次に、このコマンドにパイプ(|)します。

openssl x509 -noout -text

これにより、証明書ファイルが取得され、すべてのジューシーな詳細が出力されます。 -nooutフラグは、(base64エンコードされた)証明書ファイル自体を出力しないようにしますが、これは必要ありません。 -textフラグは、証明書の詳細をテキスト形式で出力するように指示します。

通常、気にする必要のない出力(署名、発行者、拡張機能など)が大量にあるため、thatを単純なgrepにパイプします。

grep DNS:

SANエントリはDNS:で始まるため、これはそれを含む行のみを返し、他のすべての情報を取り除き、目的の情報を残します。

コマンドが正常に終了しないことに注意してください。 openssl s_clientは実際にはクライアントとして機能し、接続を開いたままにして、入力を待機します。すぐに終了したい場合(たとえば、シェルスクリプトで出力を解析する場合)、単にechoをパイプしてください:

echo | openssl s_client -connect website.com:443 | openssl x509 -noout -text | grep DNS:

ファイルからSANを直接取得するにはどうすればよいですか?

このためには、openssl s_clientコマンドは必要ありません。 -in MyCertificate.crtコマンドにopenssl x509を追加し、もう一度grepを介してパイプします。例:

openssl x509 -noout -text -in MyCertificate.crt | grep DNS:
85
JoeSlav

SAN SSL証明書の代替名をプログラムで確認する方法はありますか?

X509証明書には複数のSANがある場合があります。以下は SSL/TLSクライアント のOpenSSL wikiからのものです。名前をループして出力します。

X509*のような関数からSSL_get_peer_certificateをTLS接続から、d2i_X509をメモリから、またはPEM_read_bio_X509をファイルシステムから取得します。

void print_san_name(const char* label, X509* const cert)
{
    int success = 0;
    GENERAL_NAMES* names = NULL;
    unsigned char* utf8 = NULL;

    do
    {
        if(!cert) break; /* failed */

        names = X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0 );
        if(!names) break;

        int i = 0, count = sk_GENERAL_NAME_num(names);
        if(!count) break; /* failed */

        for( i = 0; i < count; ++i )
        {
            GENERAL_NAME* entry = sk_GENERAL_NAME_value(names, i);
            if(!entry) continue;

            if(GEN_DNS == entry->type)
            {
                int len1 = 0, len2 = -1;

                len1 = ASN1_STRING_to_UTF8(&utf8, entry->d.dNSName);
                if(utf8) {
                    len2 = (int)strlen((const char*)utf8);
                }

                if(len1 != len2) {
                    fprintf(stderr, "  Strlen and ASN1_STRING size do not match (embedded null?): %d vs %d\n", len2, len1);
                }

                /* If there's a problem with string lengths, then     */
                /* we skip the candidate and move on to the next.     */
                /* Another policy would be to fails since it probably */
                /* indicates the client is under attack.              */
                if(utf8 && len1 && len2 && (len1 == len2)) {
                    fprintf(stdout, "  %s: %s\n", label, utf8);
                    success = 1;
                }

                if(utf8) {
                    OPENSSL_free(utf8), utf8 = NULL;
                }
            }
            else
            {
                fprintf(stderr, "  Unknown GENERAL_NAME type: %d\n", entry->type);
            }
        }

    } while (0);

    if(names)
        GENERAL_NAMES_free(names);

    if(utf8)
        OPENSSL_free(utf8);

    if(!success)
        fprintf(stdout, "  %s: <not available>\n", label);

}
2
jww