在Python中禁用SSL证书验证

8
我正在编写一个脚本,它通过HTTPS连接到多个URL,下载其SSL证书并提取CN。一切正常,除了当我遇到一个无效的SSL证书时。我绝对不关心证书是否有效,我只想要CN,但是Python固执地拒绝提取未经验证的证书信息。有没有办法绕过这种非常愚蠢的行为?哦,我只使用内置的socket和ssl库,不想使用M2Crypto或pyOpenSSL等第三方库,因为我想让脚本尽可能地便携。

以下是相关代码:
    file = open("list.txt", "r")
    for x in file:
    server = socket.getaddrinfo(x.rstrip(), "443")[0][4][0]
    sslsocket = socket.socket()
    sslsocket.connect((server, 443))
    sslsocket = ssl.wrap_socket(sslsocket, cert_reqs=ssl.CERT_REQUIRED, ca_certs="cacerts.txt")
    certificate = sslsocket.getpeercert()`
2个回答

6
ssl.get_server_certificate可以实现此功能。
import ssl
ssl.get_server_certificate(("www.sefaz.ce.gov.br",443)) 

我认为函数文档字符串比Python官方文档更清晰:

"""Retrieve the certificate from the server at the specified address,
   and return it as a PEM-encoded string.
   If 'ca_certs' is specified, validate the server cert against it.
   If 'ssl_version' is specified, use it in the connection attempt."""

因此,您可以从二进制DER证书中提取通用名称,搜索通用名称对象标识符:

def get_commonname(host,port=443):
    oid='\x06\x03U\x04\x03' # Object Identifier 2.5.4.3 (COMMON NAME)
    pem=ssl.get_server_certificate((host,port))
    der=ssl.PEM_cert_to_DER_cert(pem)
    i=der.find(oid) # find first common name (certificate authority)
    if i!=-1:
        i=der.find(oid,i+1) # skip and find second common name
        if i!=-1:
            begin=i+len(oid)+2
            end=begin+ord(der[begin-1])
            return der[begin:end]
    return None

这个可以工作。谢谢你。你的代码中有一个错别字,len(id) 应该是 len(oid),但这很容易就能弄清楚。\x03U 是什么意思?特别是 U。 - user1608235
好的,我回答了自己的问题。它是一个字面上的U。为什么你不只是用\x55来保持一致呢? - user1608235
你说的错别字问题是对的,现在已经修复了,谢谢。我知道 DER 格式使用签名来识别对象,我只是简单地复制粘贴,所以出现 U 而不是 0x55 的原因就在这里。 - olivecoder
FYI,ssl.get_server_certificate()在幕后使用了SSLSocket.getpeercert(True)。https://docs.python.org/3/library/ssl.html#ssl.SSLSocket.getpeercert如果binary_form参数为True,并且提供了证书,则此方法将以字节序列的形式返回整个证书的DER编码形式,如果对等方未提供证书,则返回None。 - Jeremy Fishman

2

好的。我整理了olivecoder的代码,以解决它假设证书链中总是有三个CN(根、中间和服务器)的问题,并对其进行了简化。这是我将要使用的最终代码。

cert = ssl.get_server_certificate(("www.google.com", 443)) #Retrieve SSL server certificate
cert = ssl.PEM_cert_to_DER_cert(cert) #Convert certificate to DER format
begin = cert.rfind('\x06\x03\x55\x04\x03') + 7 #Find the last occurence of this byte string indicating the CN, add 7 bytes to startpoint to account for length of byte string and padding
end = begin + ord(cert[begin - 1]) #Set endpoint to startpoint + the length of the CN
print cert[begin:end] #Retrieve the CN from the DER encoded certificate

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接