Python ssl 标准库 load_cert_chain - 加载 PEM 证书链失败

Kev*_*chs 3 python ssl openssl python-3.x tls1.2

运行 Python3.6。

我有一个pem格式的证书包,即服务器证书及其 CA 证书。ssl 上下文 load_cert_chain('aws-bundle.pem') 抛出 SSL 错误。其他库,如 urllib,在验证来自 HTTPS 事务的证书时遇到问题。

这是捆绑文件的样子(省略了几行):

-----BEGIN CERTIFICATE----- 
MIIESTCCAzGgAwIBAgITBn+UV4WH6Kx33rJTMlu8mYtWDTANBgkqhkiG9w0BAQsF
. . .
yLyKQXhw2W2Xs0qLeC1etA+jTGDK4UfLeC0SF7FSi8o5LL21L8IzApar2pR/
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE----- 
MIIEkjCCA3qgAwIBAgITBn+USionzfP6wq4rAfkI7rnExjANBgkqhkiG9w0BAQsF
. . . 
akcjMS9cmvqtmg5iUaQqqcT5NJ0hGA==
-----END CERTIFICATE-----
Run Code Online (Sandbox Code Playgroud)

以下是我的 ipython 成绩单的摘录:

In [33]: import ssl

In [34]: context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)

In [35]: context.load_cert_chain('aws-bundle.pem')
---------------------------------------------------------------------------
SSLError                                  Traceback (most recent call last)
<ipython-input-38-c955611be04f> in <module>
----> 1 context.load_cert_chain('aws-bundle.pem')

SSLError: [SSL] PEM lib (_ssl.c:3520)
Run Code Online (Sandbox Code Playgroud)

顺便说一句,openssl 命令行工具能够很好地处理该包 - 将元数据转储为文本。

顺便说一句,我也愿意打赌 requests 库可以满足我的需求,但我正在处理一个不需要安装额外软件包的上下文。

Pat*_*zek 8

您可能需要使用load_verify_locations代替load_cert_chain.

看文档:

 SSLContext.load_cert_chain(certfile, keyfile=None, password=None)
Run Code Online (Sandbox Code Playgroud)

加载私钥和相应的证书。certfile 字符串必须是 PEM 格式的单个文件的路径,该文件包含证书以及建立证书真实性所需的任意数量的 CA 证书。密钥文件字符串(如果存在)必须指向包含私钥的文件。否则,私钥也会从 certfile 中获取。有关证书如何存储在 certfile 中的更多信息,请参阅证书的讨论。

仔细注意:加载私钥和相应的证书。

您没有在您的通话中提供密钥,因此它会在您的“证书”文件中搜索它,但没有找到它,它会在意识到您没有使用适当的方法之前在下面写的更长的解释中呕吐。

顺便说一句,您实际上可能正在load_cert_chainload_verify_locations. load_cert_chain是加载您的证书(附带可选的 CA 证书)及其关联的私钥,而不是加载 CA/中间证书,这是通过load_verify_locations.

您的“捆绑包”要么不是您的证书,要么不包含私钥。从它的名字来看,我想它实际上是 CA/中间证书,而不是你的证书,这就是为什么我认为你混合了两种不同的方法。

以前诊断里面_ssl.c了解错误

查看 Python 3.6.8 的源代码,第 3520 行_ssl.chttps://github.com/python/cpython/blob/3c6b436a57893dd1fae4e072768f41a199076252/Modules/_ssl.c)完全匹配错误:

 _setSSLError(NULL, 0, __FILE__, __LINE__);
Run Code Online (Sandbox Code Playgroud)

(为什么在没有任何细节的情况下如此神秘地回避了我)。

如果您仔细检查,您很可能在有关您的调用的适当位置,因为它出现的函数是_ssl__SSLContext_load_cert_chain_impl.

现在,如果您研究上面导致此行的代码,您会发现:

r = SSL_CTX_use_PrivateKey_file(self->ctx,
    PyBytes_AS_STRING(keyfile ? keyfile_bytes : certfile_bytes), SSL_FILETYPE_PEM);
Run Code Online (Sandbox Code Playgroud)

这里有些事情失败了。因此,仅从它的名称 ( SSL_CTX_use_PrivateKey_file)来看,我认为问题出在您的私钥附加到证书上,因此您可以停止查看证书包的内容!

可悲的是,我不知道私钥可能有什么问题,但我想您可以开始明显的检查路径:

  • 通向它的路好吗
  • 文件权限是否正常
  • 内容好吗

为什么在那里这样做?可能是因为后面的代码做了:

 r = SSL_CTX_check_private_key(self->ctx);
Run Code Online (Sandbox Code Playgroud)

因此它确保私钥与您的证书匹配。

如果您确实对捆绑文件有问题,则可以在上面完成:

r = SSL_CTX_use_certificate_chain_file(self->ctx, PyBytes_AS_STRING(certfile_bytes));
Run Code Online (Sandbox Code Playgroud)

如果失败,它将在第 3499 行触发错误,因此您可能会在堆栈跟踪中使用:

SSLError: [SSL] PEM lib (_ssl.c:3499)
Run Code Online (Sandbox Code Playgroud)

再一次,我完全无法理解为什么这些库和库之上的包装器的开发人员只是决定创建如此神秘的错误消息,而不是强迫所有用户遭受痛苦。基本上不研究源代码,是不可能理解发生了什么......即使如此,源代码中绝对没有注释,但无论如何它可能在某些部分是自动生成的。

  • 感谢您的帮助。优秀的侦探作品!我喜欢你对错误信息的批评! (2认同)