使用Python获取或构建PEM证书链

时间:2018-06-26 09:27:10

标签: python ssl pem

是否可以使用ssl和Python以PEM格式获取整个证书链?我可以用:

import ssl
addr = '192.0.2.1'
cert_str = ssl.get_server_certificate((addr, 443))

这给了我类似的东西

-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----

但是我想拥有:

-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----

我很确定这是可能的,因为我可以从Web浏览器下载它。有什么想法吗?

(我已经检查过Getting certificate chain with Python 3.3 SSL module,但不确定这就是我想要的...)

编辑:我在 Patrick Mevzek 回答后尝试了什么:

from OpenSSL import SSL
import socket
dst = ('192.0.2.1', 443)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ctx = SSL.Context(SSL.SSLv23_METHOD)
ctx.set_alpn_protos([b'http/1.1'])
if sock.connect_ex(dst) == 0:
    connection = SSL.Connection(ctx, sock)
    cert_str = connection.get_peer_cert_chain()

但是cert_str为None。我认为这是因为我缺少使用OpenSSL的功能。

1 个答案:

答案 0 :(得分:1)

如果在Python中使用OpenSSL库,则可以将get_peer_cert_chain应用于连接对象,它将为您提供服务器发送的证书列表,因此最终证书并在需要时使用所有中间体。

请参见https://pyopenssl.org/en/stable/api/ssl.html#connection-objects

  

get_peer_cert_chain()

     

获取对方的证书(如果有)

     

返回:给出对等方证书链的X509实例列表;如果没有,则为None。

这是一个简单的示例(不进行任何错误处理):

from OpenSSL import SSL
import socket

dst = ('www.google.com', 443)
ctx = SSL.Context(SSL.SSLv23_METHOD)
s = socket.create_connection(dst)
s = SSL.Connection(ctx, s)
s.set_connect_state()
s.set_tlsext_host_name(dst[0])

s.sendall('HEAD / HTTP/1.0\n\n')
s.recv(16)

certs = s.get_peer_cert_chain()
for pos, cert in enumerate(certs):
   print "Certificate #" + str(pos)
   for component in cert.get_subject().get_components():
       print "Subject %s: %s" % (component)
   print "notBefore:" + cert.get_notBefore()
   print "notAfter:" + cert.get_notAfter()
   print "version:" + str(cert.get_version())
   print "sigAlg:" + cert.get_signature_algorithm()
   print "digest:" + cert.digest('sha256')

给出:

Certificate #0
Subject C: US
Subject ST: California
Subject L: Mountain View
Subject O: Google LLC
Subject CN: www.google.com
notBefore:20180612133452Z
notAfter:20180821121300Z
version:2
sigAlg:sha256WithRSAEncryption
digest:06:C5:12:EB:3C:B1:7F:AB:18:E0:D5:22:E4:25:12:A7:30:AA:27:16:0B:3A:99:CB:3D:11:CF:12:EF:95:2E:41
Certificate #1
Subject C: US
Subject O: Google Trust Services
Subject CN: Google Internet Authority G3
notBefore:20170615000042Z
notAfter:20211215000042Z
version:2
sigAlg:sha256WithRSAEncryption
digest:BE:0C:CD:54:D4:CE:CD:A1:BD:5E:5D:9E:CC:85:A0:4C:2C:1F:93:A5:22:0D:77:FD:E8:8F:E9:AD:08:1F:64:1B

因此,您具有证书的完整详细内容,有关可用信息,请参见https://pyopenssl.org/en/stable/api/crypto.html#x509-objects。 然后,您将拥有to_cryptography()可以通过以下方式获取其PEM版本:cert.to_cryptography().public_bytes(serialization.Encoding.PEM)

但也要考虑到这一点

  • 最好使用回调,请参见set_info_callback()方法,以便在适当的时间运行诊断程序(甚至键入SSL.SSL_CB_HANDSHAKE_DONE
  • 我发现您需要先交换一些流量(发送/接收),然后才能呼叫get_peer_cert_chain()(如果在我的示例中将其移至sendall()之前,它将返回None

从您问题的链接中可以看到,仅使用getpeercertchain()而不是ssl时,您与OpenSSL等效;但是,这似乎仍然被记录为具有某些可用补丁的bug,并且可能不会发布。 实际上,https://docs.python.org/3.8/library/ssl.html上的最新文档并未列出getpeercertchain()