如何检查 SSL/TLS 证书的主题备用名称?
Posted
技术标签:
【中文标题】如何检查 SSL/TLS 证书的主题备用名称?【英文标题】:How to Check Subject Alternative Names for a SSL/TLS Certificate? 【发布时间】:2012-10-19 02:31:43 【问题描述】:有没有办法以编程方式检查 SAN SSL 证书的主题备用名称?
例如,使用以下命令,我可以获得很多信息,但不是所有的 SAN:
openssl s_client -connect www.website.com:443
非常感谢!
【问题讨论】:
当您以编程方式说,特别是任何语言,或者您可能在使用一些 shell 脚本时? 嗨 Bruno,我可能滥用了这个词 :) 我只是一个脚本或命令行或 .. 允许获取所有 AN。 【参考方案1】:要获取证书的主题备用名称 (SAN),请使用以下命令:
openssl s_client -connect website.com:443 </dev/null 2>/dev/null | openssl x509 -noout -text | grep DNS:
首先,此命令连接到我们想要的站点(website.com,用于 SSL 的端口 443):
openssl s_client -connect website.com:443
然后将 (|
) 输入此命令:
openssl x509 -noout -text
这将获取证书文件并输出其所有详细信息。 -noout
标志阻止它输出我们不需要的(base64 编码的)证书文件本身。 -text
标志告诉它以文本形式输出证书详细信息。
通常有很多我们不关心的输出(签名、颁发者、扩展等),所以我们将 输入到一个简单的 grep 中:
grep DNS:
由于 SAN 条目以 DNS:
开头,因此只返回包含该条目的行,去除所有其他信息,并为我们留下所需的信息。
您可能会注意到该命令没有干净地退出; openssl s_client
实际上充当客户端并保持连接打开,等待输入。如果您希望它立即退出(例如,在 shell 脚本中解析输出),只需将 echo
输入它:
echo | openssl s_client -connect website.com:443 | openssl x509 -noout -text | grep DNS:
如何直接从文件中获取 SAN?
为此,您不需要openssl s_client
命令。只需在 openssl x509
命令上添加 -in MyCertificate.crt
并再次通过 grep 管道,例如:
openssl x509 -noout -text -in MyCertificate.crt | grep DNS:
【讨论】:
我认为您也可以接受自己的答案。这样它就不会对其他人显示为“开放式问题”:) 哦,好的。我从来没有接受自己的答案,不知道有时间限制:) 这是 -text 选项,它提供了包括 SAN 在内的证书的完整列表 @JoeSlav 我知道这已经晚了五年,但我已经提交了一个编辑,分解了您的命令的工作原理,并解释了如何修改它以直接从证书文件中读取数据。希望它有所帮助:) 这行得通,但注意:如果这些是虚拟名称,您可能需要参数-servername www.website.com
来获取正确的 SSL 证书。 echo | openssl s_client -servername www.website.com -connect sameserver.website.com:443 | openssl x509 -noout -text | grep DNS
【参考方案2】:
如果您只想查看 SAN,grep DNS:
是显而易见的解决方案。
如果你想有一个更清晰的列表来进一步处理,你可以使用这个 Perl 正则表达式来提取名称:@names=/\sDNS:([^\s,]+)/g
例如:
true | openssl s_client -connect example.com:443 2>/dev/null \
| openssl x509 -noout -text \
| perl -l -0777 -ne '@names=/\bDNS:([^\s,]+)/g; print join("\n", sort @names);'
哪个会输出这个:
example.com
example.edu
example.net
example.org
www.example.com
www.example.edu
www.example.net
www.example.org
所以你可以通过管道将其发送到 while read name; do echo "do stuff with $name"; done
等。
或者对于一行以逗号分隔的列表,将join("\n",
替换为join(",",
(perl 的-0777
开关使其一次读取整个输入,而不是逐行读取)
【讨论】:
【参考方案3】:也可以使用捆绑的 openssl 功能:
openssl s_client -connect website.com:443 </dev/null | openssl x509 -noout -ext subjectAltName
【讨论】:
【参考方案4】:有没有办法以编程方式检查 SAN SSL 证书的备用名称?
X509 证书中可能有多个 SAN。以下内容来自 OpenSSL wiki SSL/TLS Client。它循环遍历名称并打印它们。
您可以从函数中获取X509*
,例如来自 TLS 连接的 SSL_get_peer_certificate
、来自内存的 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);
【讨论】:
以上是关于如何检查 SSL/TLS 证书的主题备用名称?的主要内容,如果未能解决你的问题,请参考以下文章
通用名称 (CN) 和主题备用名称 (SAN) 如何协同工作?
如何使用 Apache mod_ssl 变量验证 URI 格式的主题备用名称的内容?