“SSLError: [SSL] PEM lib (_ssl.c:2532)”是啥意思使用 Python ssl 库?

Posted

技术标签:

【中文标题】“SSLError: [SSL] PEM lib (_ssl.c:2532)”是啥意思使用 Python ssl 库?【英文标题】:What does "SSLError: [SSL] PEM lib (_ssl.c:2532)" mean using the Python ssl library?“SSLError: [SSL] PEM lib (_ssl.c:2532)”是什么意思使用 Python ssl 库? 【发布时间】:2015-05-07 18:55:31 【问题描述】:

我正在尝试使用 Python 3 asyncio 模块连接到另一方并收到此错误:

     36     sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
---> 37     sslcontext.load_cert_chain(cert, keyfile=ca_cert)
     38

SSLError: [SSL] PEM lib (_ssl.c:2532)

问题就是错误的含义。我的证书是正确的,密钥文件(CA 证书)可能不正确

【问题讨论】:

您确实提到密钥文件可能不正确。如果证书(公钥)没有与正确的私钥文件一起使用,证书加载将失败。 你有keyfile=ca_cert,这似乎是不正确的(或者你选择了可怕的变量名)。你的ca_cert 真的包含私钥吗? @larsks 选择可怕的变量名如何导致代码错误?关于如何命名它们的任何建议(或约定)? ca_cert 文件以-----BEGIN CERTIFICATE----- 开头,这似乎是一个有效的加密密钥,并以-----END CERTIFICATE----- 结尾。页眉和页脚应该说不同的东西吗? 对某个问题投反对票的人至少应该对原因发表评论,甚至可能对如何改进问题发表评论。 我已在此处回滚了上一次修订,因为不应将答案编辑到问题中。 【参考方案1】:

假设使用的是 3.6 版:

见:https://github.com/python/cpython/blob/3.6/Modules/_ssl.c#L3523-L3534

 PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state);
 r = SSL_CTX_check_private_key(self->ctx);
 PySSL_END_ALLOW_THREADS_S(pw_info.thread_state);
 if (r != 1)  
    _setSSLError(NULL, 0, __FILE__, __LINE__);
    goto error;
 

它的意思是SSL_CTX_check_private_key失败了;因此,私钥不正确。

参考可能的版本:

https://github.com/python/cpython/blob/3.4/Modules/_ssl.c#L2529-L2535

【讨论】:

同意。你去了源头,找到了正确的答案。 我以前读过它,但不知道那是什么意思。我认为它正在使用 openssl (在 Unix 系统上)并报告它在系统级别遇到的错误。这就解释了为什么错误会从源代码中的那一行产生,对吧?【参考方案2】:

在您的代码中,您正在调用:

sslcontext.load_cert_chain(cert, keyfile=ca_cert)

来自documentation:

加载一个私钥和相应的证书。证书文件 string 必须是 PEM 格式的单个文件的路径,其中包含 证书以及所需的任意数量的 CA 证书 确定证书的真实性。密钥文件字符串,如果 存在,必须指向包含私钥的文件。否则 私钥也将从 certfile 中获取。见 讨论证书以获取有关证书如何使用的更多信息 存储在证书文件中。

根据您示例中的参数名称,您似乎正在将 CA 证书传递给 keyfile 参数。这是不正确的,您需要传入用于生成本地证书的私钥(否则客户端无法使用您的证书)。私钥文件如下所示:

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,9BA4973008F0A0B36FBE1426C198DD1B

...data...
-----END RSA PRIVATE KEY-----

仅当您尝试验证已由该证书签署的 SSL 证书的有效性时,您才需要 CA 证书。在这种情况下,您可能会使用SSLContext.load_verify_locations() 来加载 CA 证书(尽管我最近没有使用过 SSL 模块,所以请不要相信我的话)。

【讨论】:

我收到了同样的错误,我只需要验证证书链。私钥不相关,所以这个答案很有帮助,因为它解释了我的 ansible 脚本正在寻找一个私钥,但应该设置为只寻找证书链。【参考方案3】:

该错误表示缺少私钥文件。 在 openssl shell 中生成密钥对: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365

启动 Python SSL 服务器:

from http.server import HTTPServer, 
             SimpleHTTPRequestHandler

import ssl

httpd = HTTPServer(('localhost', 4443), 
                           SimpleHTTPRequestHandler)

httpd.socket = ssl.wrap_socket(httpd.socket, 
                 certfile='/tmp/cert.pem',keyfile='
                           /tmp/key.pem', server_side=True)

httpd.serve_forever()

(我们使用端口 4443 以便我可以以普通用户身份运行测试;通常的端口 443 需要 root 权限)。

【讨论】:

【参考方案4】:

就我而言,此错误意味着我的证书具有错误的文件扩展名。我必须使用以下命令将我的 cert.dir 文件转换为 cert.pem 文件:

openssl x509 -inform der -in cert.der -out cert.pem 

【讨论】:

不是扩展名错误,文件格式错误,您必须将文件转换为 pem 格式。【参考方案5】:

在使用 openssl 生成受密码保护的自签名证书时,我遇到了类似的问题,我得到以下输出:

ontext.load_cert_chain(certfile= certificate_private, keyfile= certificate)
ssl.SSLError: [SSL] PEM lib (_ssl.c:4012)

在阅读了两次以了​​解load_cert_chain 上的文档后,我想出了以下解决方案:

import ssl
    
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.load_cert_chain(certfile= certificate_private, keyfile= certificate_key, password= certificate_password)

connection = http.client.HTTPSConnection(host, port=443, context=context)
connection.request(method="POST", url=request_url, headers=request_headers, body=json.dumps(request_body_dict))
response = connection.getresponse()

证书文件是cert.pem 密钥文件是key.pem 并且密码是我在生成类似于on this answer 所述的自签名证书时使用的密码。

【讨论】:

以上是关于“SSLError: [SSL] PEM lib (_ssl.c:2532)”是啥意思使用 Python ssl 库?的主要内容,如果未能解决你的问题,请参考以下文章