Python使用ssl.getpeercert()从URL获取通用名称

时间:2017-08-03 08:15:18

标签: python ssl https

我正在尝试获取证书颁发者信息(公用名),但链接中的代码不适用于某些网址。

How can i get Certificate issuer information in python?

import ssl, socket

hostname = 'google.com'
ctx = ssl.create_default_context()
s = ctx.wrap_socket(socket.socket(), server_hostname=hostname)
s.connect((hostname, 443))
cert = s.getpeercert()

subject = dict(x[0] for x in cert['subject'])
issued_to = subject['commonName']

>>> issued_to
u'*.google.com'

例如,我尝试了主机名" cds.ca",它说

ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:661)

但我仍然可以使用Internet Explorer(* .cds.ca)获取公用名称

所以我认为我应该使用自己的证书(.cer)而不是使用getpeercert(),那么我该如何更改该行呢?

或者,有没有其他方法可以使用我自己的证书文件来实现CN?

1 个答案:

答案 0 :(得分:2)

如果您只想获取CN或其他证书详细信息,无论证书验证成功与否,您都必须禁用验证。不幸的是,如果禁用证书验证,简单的sock.getpeercert()将仅返回空字典。这就是为什么必须使用sock.getpeercert(True)获取证书的二进制表示并使用OpenSSL.crypto从中提取CN的原因:

import socket
import ssl
import OpenSSL.crypto as crypto

dst = ('cds.ca',443)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(dst)

# upgrade the socket to SSL without checking the certificate
# !!!! don't transfer any sensitive data over this socket !!!!
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
s = ctx.wrap_socket(s, server_hostname=dst[0])

# get certificate
cert_bin = s.getpeercert(True)
x509 = crypto.load_certificate(crypto.FILETYPE_ASN1,cert_bin)
print("CN=" + x509.get_subject().CN)