SAN SSL証明書のサブジェクトの別名をプログラムで確認する方法はありますか?
たとえば、次のコマンドを使用すると、すべてのSANではなく多くの情報を取得できます。
openssl s_client -connect www.website.com:443
どうもありがとうございました!
証明書のサブジェクトの別名(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:
このためには、openssl s_client
コマンドは必要ありません。 -in MyCertificate.crt
コマンドにopenssl x509
を追加し、もう一度grepを介してパイプします。例:
openssl x509 -noout -text -in MyCertificate.crt | grep DNS:
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);
}