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

Sea*_*ean 3 python ssl https

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

如何在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'
Run Code Online (Sandbox Code Playgroud)

例如,我试过主机名“cds.ca”,它说

ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:661)
Run Code Online (Sandbox Code Playgroud)

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

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

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

Ste*_*ich 7

如果无论证书验证成功与否,您只想获取 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)
Run Code Online (Sandbox Code Playgroud)