【问题标题】:Get or build PEM certificate chain in Python在 Python 中获取或构建 PEM 证书链
【发布时间】:2018-06-26 09:27:09
【问题描述】:

是否可以使用 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-----

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

(我已经检查过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 是无。我认为这是因为我在使用 OpenSSL 时遗漏了一些东西。

【问题讨论】:

  • “但 cert_str 为无。”请参阅我更新的答案中的备注。根据观察,尝试仅在 在 TLS 流中交换了一些数据之后才调用它,因为在此之前它没有正确填充。 PS:请不要在问题/文档中使用1.2.3.4作为占位符,请参阅RFC5737中的指导以混淆IP地址,因此您可以使用192.0.2.x
  • 感谢您的回答和建议,我会记住的!

标签: python ssl pem


【解决方案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

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

【讨论】:

  • 感谢您的回答,但我无法让 OpenSSL 工作。你能帮我吗?我更新了我的帖子,所以你可以看到我尝试了什么。
  • @TimothéeChabat 我已经用完整的例子更新了上面的回复,我希望它可以激励你。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-25
  • 1970-01-01
  • 1970-01-01
  • 2015-07-08
  • 1970-01-01
  • 1970-01-01
  • 2020-09-30
相关资源
最近更新 更多