如何显示证书的主题备用名称?
Posted
技术标签:
【中文标题】如何显示证书的主题备用名称?【英文标题】:How to display the Subject Alternative Name of a certificate? 【发布时间】:2014-01-25 19:40:15 【问题描述】:我找到的最接近的答案是使用“grep”。
> openssl x509 -text -noout -in cert.pem | grep DNS
有没有更好的方法来做到这一点?我只喜欢命令行。
谢谢。
【问题讨论】:
grep
有什么问题?
Stack Overflow 是一个编程和开发问题的网站。这个问题似乎离题了,因为它与编程或开发无关。请参阅帮助中心的What topics can I ask about here。也许Super User 或Unix & Linux Stack Exchange 会是一个更好的提问地方。另见Where do I post questions about Dev Ops?
参见this answer 仅列出名称,不包括“DNS:”等。
【参考方案1】:
请注意,您可以通过添加以下选项将-text
的输出限制为仅扩展名:
-certopt no_subject,no_header,no_version,no_serial,no_signame,no_validity,no_issuer,no_pubkey,no_sigdump,no_aux
即:
openssl x509 -text -noout -in cert.pem \
-certopt no_subject,no_header,no_version,no_serial,no_signame,no_validity,no_issuer,no_pubkey,no_sigdump,no_aux
但是,您仍然需要应用一些文本解析逻辑来获取 Subject Alternative Name
。
如果这还不够,我认为您需要编写一个小程序,使用 openssl 库来提取您要查找的特定字段。以下是一些示例程序,展示了如何解析证书,包括提取扩展字段,例如 Subject Alternative Name
:
https://zakird.com/2013/10/13/certificate-parsing-with-openssl
请注意,如果您走编程路线,则不必使用 openssl 和 C……您可以选择自己喜欢的语言和 ASN.1
解析器库,然后使用它。例如,在 Java 中,您可以使用 http://jac-asn1.sourceforge.net/ 等。
【讨论】:
谢谢。我会试试看的。【参考方案2】:较新版本的 openssl 有一个“-ext”选项,允许您仅打印 subjectAltName 记录。我在 Debian 9.9 上使用“OpenSSL 1.1.1b”
openssl x509 -noout -ext subjectAltName -in cert.pem
虽然您仍然需要解析输出。
更改是在https://github.com/openssl/openssl/issues/3932中进行的
【讨论】:
这个答案应该被提升以反映大多数最新设置中的现实,包括面向稳定性的发行版中的版本,比如 Debian【参考方案3】:获取证书数据
使用gnutls
和certtool
$ gnutls-cli example.com -p 443 --print-cert < /dev/null | certtool -i | grep -C3 -i dns
与openssl
取自https://***.com/a/13128918/1695680
$ openssl s_client -connect example.com:443 < /dev/null | openssl x509 -noout -text | grep -C3 -i dns
提取证书数据
| grep -C3 -i dns
适用于一个简单的案例,如果您手动查看这些数据确实足够好的话。但是证书数据是分层的,而不是面向行的(所以 greping 会很混乱,尤其是对于 ca 链)。
我不知道任何可以进行键值提取的 x509 命令行工具,我使用的大多数系统都有 python on-box 或附近所以这里是一个使用 python 的方法,x509 接口由cryptography
on pypi 提供。使用cryptography
有点冗长,我不习惯将其压缩成一个单行器,但使用此脚本,您可以从传递给标准输入的证书中提取 dns 名称
#!/usr/bin/env python3
import sys
import cryptography.x509
import cryptography.hazmat.backends
import cryptography.hazmat.primitives
DEFAULT_FINGERPRINT_HASH = cryptography.hazmat.primitives.hashes.SHA256
def _x509_san_dns_names(certificate):
""" Return a list of strings containing san dns names
"""
crt_san_data = certificate.extensions.get_extension_for_oid(
cryptography.x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME
)
dns_names = crt_san_data.value.get_values_for_type(
cryptography.x509.DNSName
)
return dns_names
def _find_certificate_pem(stream):
""" Yield hunks of pem certificates
"""
certificate_pem = []
begin_certificate = False
for line in stream:
if line == b'-----END CERTIFICATE-----\n':
begin_certificate = False
certificate_pem.append(line)
yield b''.join(certificate_pem)
certificate_pem = []
if line == b'-----BEGIN CERTIFICATE-----\n':
begin_certificate = True
if begin_certificate:
certificate_pem.append(line)
def _dump_stdincert_san_dnsnames():
""" Print line-oriented certificate fingerprint and san dns name
"""
for certificate_pem in _find_certificate_pem(sys.stdin.buffer):
certificate = cryptography.x509.load_pem_x509_certificate(
certificate_pem,
cryptography.hazmat.backends.default_backend()
)
certificate_fingerprint = certificate.fingerprint(
DEFAULT_FINGERPRINT_HASH(),
)
certificate_fingerprint_str = ':'.join(
':02x'.format(i) for i in certificate_fingerprint
)
try:
for dns_name in _x509_san_dns_names(certificate):
sys.stdout.write(' \n'.format(certificate_fingerprint_str, dns_name))
except cryptography.x509.extensions.ExtensionNotFound:
sys.stderr.write(' Certificate has no extension SubjectAlternativeName\n'.format(certificate_fingerprint_str))
def main():
_dump_stdincert_san_dnsnames()
if __name__ == '__main__':
main()
$ true | openssl s_client -connect localhost:8443 | openssl x509 -noout -text | grep DNS:
depth=2 C = US, ST = NC, L = SomeCity, O = SomeCompany Security, OU = SomeOU, CN = SomeCN
verify error:num=19:self signed certificate in certificate chain
DONE
DNS:localhost, DNS:127.0.0.1, DNS:servername1.somedom.com, DNS:servername2.somedom.local
【讨论】:
我想知道与 gnutls-cli 相关的等价物 这应该是公认的答案。工作很轻松。 避免无用的分叉!!而不是true | tool ...
,你可以写tool <<<'' ...
!【参考方案4】:
有我的解决方案(使用openssl 和sed):
bash第一
sed -ne '
s/^\( *\)Subject:/\1/p;
/X509v3 Subject Alternative Name/
N;
s/^.*\n//;
:a;
s/^\( *\)\(.*\), /\1\2\n\1/;
ta;
p;
q;
' < <(openssl x509 -in cert.pem -noout -text)
可以写成:
sed -ne 's/^\( *\)Subject:/\1/p;/X509v3 Subject Alternative Name/
N;s/^.*\n//;:a;s/^\( *\)\(.*\), /\1\2\n\1/;ta;p;q; ' < <(
openssl x509 -in cert.pem -noout -text )
并且可以呈现如下内容:
CN=www.example.com
DNS:il0001.sample.com
DNS:example.com
DNS:demodomain.com
DNS:testsite.com
DNS:www.il0001.sample.com
DNS:www.il0001.sample.com.vsite.il0001.sample.com
DNS:www.example.com
DNS:www.example.com.vsite.il0001.sample.com
DNS:www.demodomain.com
DNS:www.demodomain.com.vsite.il0001.sample.com
DNS:www.testsite.com
DNS:www.testsite.com.vsite.il0001.sample.com
直播服务器也一样
sed -ne 's/^\( *\)Subject:/\1/p;/X509v3 Subject Alternative Name/
N;s/^.*\n//;:a;s/^\( *\)\(.*\), /\1\2\n\1/;ta;p;q; ' < <(
openssl x509 -noout -text -in <(
openssl s_client -ign_eof 2>/dev/null <<<$'HEAD / HTTP/1.0\r\n\r' \
-connect google.com:443 ) )
可能输出:
C=US, ST=California, L=Mountain View, O=Google Inc, CN=*.google.com
DNS:*.google.com
DNS:*.android.com
DNS:*.appengine.google.com
DNS:*.cloud.google.com
DNS:*.gcp.gvt2.com
DNS:*.google-analytics.com
DNS:*.google.ca
DNS:*.google.cl
DNS:*.google.co.in
DNS:*.google.co.jp
DNS:*.google.co.uk
DNS:*.google.com.ar
DNS:*.google.com.au
DNS:*.google.com.br
DNS:*.google.com.co
DNS:*.google.com.mx
DNS:*.google.com.tr
DNS:*.google.com.vn
DNS:*.google.de
DNS:*.google.es
DNS:*.google.fr
DNS:*.google.hu
DNS:*.google.it
DNS:*.google.nl
DNS:*.google.pl
DNS:*.google.pt
DNS:*.googleadapis.com
DNS:*.googleapis.cn
DNS:*.googlecommerce.com
DNS:*.googlevideo.com
DNS:*.gstatic.cn
DNS:*.gstatic.com
DNS:*.gvt1.com
DNS:*.gvt2.com
DNS:*.metric.gstatic.com
DNS:*.urchin.com
DNS:*.url.google.com
DNS:*.youtube-nocookie.com
DNS:*.youtube.com
DNS:*.youtubeeducation.com
DNS:*.ytimg.com
DNS:android.clients.google.com
DNS:android.com
DNS:developer.android.google.cn
DNS:g.co
DNS:goo.gl
DNS:google-analytics.com
DNS:google.com
DNS:googlecommerce.com
DNS:urchin.com
DNS:www.goo.gl
DNS:youtu.be
DNS:youtube.com
DNS:youtubeeducation.com
POSIX shell 现在
由于< <(...)
是一个bashism,所以必须编写相同的命令:
openssl x509 -in cert.pem -noout -text | sed -ne '
s/^\( *\)Subject:/\1/p;
/X509v3 Subject Alternative Name/
N;
s/^.*\n//;
:a;
s/^\( *\)\(.*\), /\1\2\n\1/;
ta;
p;
q;
'
和
printf 'HEAD / HTTP/1.0\r\n\r\n' |
openssl s_client -ign_eof 2>/dev/null -connect google.com:443 |
openssl x509 -noout -text |
sed -ne 's/^\( *\)Subject:/\1/p;/X509v3 Subject Alternative Name/
N;s/^.*\n//;:a;s/^\( *\)\(.*\), /\1\2\n\1/;ta;p;q; '
【讨论】:
【参考方案5】:使用 grep 的非常简单的解决方案
openssl x509 -in /path/to/x509/cert -noout -text|grep -oP '(?<=DNS:|IP Address:)[^,]+'|sort -uV
对于谷歌证书,输出:
android.clients.google.com
android.com
developer.android.google.cn
g.co
goo.gl
google.com
googlecommerce.com
google-analytics.com
hin.com
urchin.com
www.goo.gl
youtu.be
youtube.com
youtubeeducation.com
*.android.com
*.appengine.google.com
*.cloud.google.com
*.gcp.gvt2.com
*.googleadapis.com
*.googleapis.cn
*.googlecommerce.com
*.googlevideo.com
*.google.ca
*.google.cl
*.google.com
*.google.com.ar
*.google.com.au
*.google.com.br
*.google.com.co
*.google.com.mx
*.google.com.tr
*.google.com.vn
*.google.co.in
*.google.co.jp
*.google.co.uk
*.google.de
*.google.es
*.google.fr
*.google.hu
*.google.it
*.google.nl
*.google.pl
*.google.pt
*.gstatic.cn
*.gstatic.com
*.gvt1.com
*.gvt2.com
*.metric.gstatic.com
*.urchin.com
*.url.google.com
*.youtubeeducation.com
*.youtube.com
*.ytimg.com
*.google-analytics.com
*.youtube-nocookie.com
【讨论】:
欢迎来到 Stack Overflow!感谢您提供此代码 sn-p,它可能会提供一些有限的短期帮助。一个正确的解释would greatly improve 它的长期价值通过展示为什么这是一个很好的解决问题的方法,并将使它对未来有其他类似问题的读者更有用。请edit您的回答添加一些解释,包括您所做的假设。【参考方案6】:如何显示证书的主题备用名称?
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);
【讨论】:
【参考方案7】:您可以使用awk
更接近SAN,将上述选项通过管道传递到awk
语句中:
openssl x509 -in mycertfile.crt -text -noout \
-certopt no_subject,no_header,no_version,no_serial,no_signame,no_validity,no_subject,no_issuer,no_pubkey,no_sigdump,no_aux \
| awk '/X509v3 Subject Alternative Name/','/X509v3 Basic Constraints/'
【讨论】:
我不明白您所说的“上述选项”是什么意思。【参考方案8】:改进的基于 awk 的解决方案(帽子提示:@RandomW):
openssl x509 -in certfile -text -noout \
-certopt no_header,no_version,no_serial,no_signame,no_validity,no_issuer,no_pubkey,no_sigdump,no_aux \
| awk '/X509v3 Subject Alternative Name:/ san=1;next
san && /^ *X509v3/ exit
san sub(/DNS:/,"",$1);print $1'
这会打印出一个列表,grep
和 sed
解决方案也可以在这里找到。不同之处在于对信息的查找位置进行了更严格的控制。如果输出格式发生变化,这个版本会更加健壮,并且会更好地处理这些变化。只有“Subject Alternative Name”和下一个“X509v3”部分之间的文本被打印出来,所有可选的“DNS:”前面的文本都被删除。
android.clients.google.com
android.com
developer.android.google.cn
g.co
goo.gl
...
【讨论】:
【参考方案9】:添加一个 python 替代品。 先决条件是您有一个带有“DNS:”记录的字符串。
获取证书详细信息(子进程、OpenSSL 模块等) dnsstring 包含“openssl”输出的“DNS:”行。 如何从文本输出中获取 DNS 名称字符串的示例 证书。
for idx, line in enumerate(certoutput.split()):
if ' X509v3 Authority Key Identifier:' in line:
dnsstring = certoutput.split()[idx + 1]
# Get a list
[x.replace('DNS:', '').replace(',', '') for x in dnsstring]
# Format to a comma separated string
', '.join([x.replace('DNS:', '').replace(',', '') for x in dnsstring])
命令行示例:
true | \
openssl s_client -showcerts -connect google.com:443 2>/dev/null | \
openssl x509 -noout -text 2>/dev/null | grep " DNS:" | \
python -c"import sys; print ', '.join([x.replace('DNS:', '').replace(',', '') for x in sys.stdin.readlines()[0].split()])"
输出:
*.google.com, *.android.com, <etc>
【讨论】:
【参考方案10】:也许这就够了:
openssl x509 -in cert.pem -noout -text -certopt ca_default,no_sigdump
【讨论】:
【参考方案11】:以下是我们如何通过awk
执行此操作。
'/Subject: C=/printf $NF"\n"
匹配任何具有/Subject: C=
模式的行,printf $NF"\n"
只是用换行符打印最后一个字段。
/DNS:/x=gsub(/ *DNS:/, ""); printf "SANS=" $0"\n"
匹配模式 DNS:
的行。 gsub
用于在每个 fqdn 之前替换不需要的 DNS:
。 printf "SANS="$0"\n"
用换行符打印整行。
➤ echo | openssl s_client -connect google.com:443 2>&1 | openssl x509 -noout -text | awk '/Subject: C=/printf $NF"\n" /DNS:/x=gsub(/ *DNS:/, ""); printf "SANS=" $0"\n"'
CN=*.google.com
SANS=*.google.com,*.android.com,*.appengine.google.com,*.cloud.google.com,*.crowdsource.google.com,*.g.co,*.gcp.gvt2.com,*.gcpcdn.gvt1.com,*.ggpht.cn,*.gkecnapps.cn,*.google-analytics.com,*.google.ca,*.google.cl,*.google.co.in,*.google.co.jp,*.google.co.uk,*.google.com.ar,*.google.com.au,*.google.com.br,*.google.com.co,*.google.com.mx,*.google.com.tr,*.google.com.vn,*.google.de,*.google.es,*.google.fr,*.google.hu,*.google.it,*.google.nl,*.google.pl,*.google.pt,*.googleadapis.com,*.googleapis.cn,*.googlecnapps.cn,*.googlecommerce.com,*.googlevideo.com,*.gstatic.cn,*.gstatic.com,*.gstaticcnapps.cn,*.gvt1.com,*.gvt2.com,*.metric.gstatic.com,*.urchin.com,*.url.google.com,*.wear.gkecnapps.cn,*.youtube-nocookie.com,*.youtube.com,*.youtubeeducation.com,*.youtubekids.com,*.yt.be,*.ytimg.com,android.clients.google.com,android.com,developer.android.google.cn,developers.android.google.cn,g.co,ggpht.cn,gkecnapps.cn,goo.gl,google-analytics.com,google.com,googlecnapps.cn,googlecommerce.com,source.android.google.cn,urchin.com,www.goo.gl,youtu.be,youtube.com,youtubeeducation.com,youtubekids.com,yt.be
➤
【讨论】:
以上是关于如何显示证书的主题备用名称?的主要内容,如果未能解决你的问题,请参考以下文章
CA签名X509证书包含X509v3扩展名“主题备用名称”两次
无法使用主题备用名称证书为 SSL/TLS 安全通道建立信任关系