我正在编写一个通过HTTPS连接到一堆URL的脚本,下载他们的SSL证书并提取CN。一切都有效,除非我偶然发现一个SSL证书无效的网站。我绝对不在乎证书是否有效。我只想要CN,但如果证书未经过验证,Python就会顽固地拒绝提取证书信息。有没有办法绕过这种极其愚蠢的行为?哦,我只使用内置套接字和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()`
答案 0 :(得分:6)
ssl.get_server_certificate可以这样做:
import ssl
ssl.get_server_certificate(("www.sefaz.ce.gov.br",443))
我认为函数doc字符串比python doc site更清晰:
"""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
答案 1 :(得分: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