我正在尝试在以"https"开头的网站上使用urllib.request.urlopen。错误输出为:ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed
有许多很棒的线程可以覆盖此错误。包括这个提到SSL实验室评级的。 我可以在我测试过的所有其他"https"网站上使用 urllib.request.urlopen。
SSL 实验室显示以下输出:
Key RSA 2048 bits (e 65537)
Issuer Let's Encrypt Authority X3
AIA: http://cert.int-x3.letsencrypt.org/
Signature algorithm SHA256withRSA
Extended Validation No
Certificate Transparency No
OCSP Must Staple No
Revocation information OCSP
Revocation status Good (not revoked)
DNS CAA No (more info)
Trusted Yes
澄清一下,我的问题是:是否有完成握手的解决方案,不包括绕过证书验证?如果有解决方案,是否可以完全在linux,macOS和Windows上的python脚本中解决?
我无法回答urllib的这个问题,但我能够使用python请求来克服这个问题。请注意,仅当相关网站存在受信任的证书链,但服务器可能缺少根证书或中间证书时,这才有效。
使用 SSL 实验室服务器测试(此处链接(,运行测试并向下滚动到认证路径。如果存在受信任的证书路径,但服务器由于某种原因未提供完整的链,则可以在此处以文本形式下载完整的受信任路径。复制完整的证书链并将其另存为 .pem 文件,并将此文件的路径传递给请求函数:
r = requests.get(url, verify = "path/to/chain.pem")
请求模块可能会抛出各种与SSL相关的认证失败,其中许多将是服务器端问题,您确实希望避免禁用SSL验证。此解决方案仅适用于存在完整证书但有问题的颁发者或服务器草率省略根证书或中间证书的罕见情况。
> 可以通过将缺少的中间证书添加到活动的 X509Store 来解决此问题:
cert_text = '''
-----BEGIN CERTIFICATE-----
...put your actual certificate text here...
-----END CERTIFICATE-----
'''
# fill this out depending on which specific intermediate cert you're missing
missing_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_text)
context = ssl.create_default_context() # load default trusted certificates
store = context.get_cert_store() # get the X509Store for that context
store.add_cert(missing_cert) # add your missing cert to it
urllib.request.urlopen(site, context=context)
请注意,如果您只需要与要执行此操作的一台服务器通信,则可以将适当的cafile
或capath
参数传递给create_default_context()
。
我能够解决这个问题(对于基于 debian 的系统,我正在运行 Debian 9(。我仍然需要在macOS和Windows上测试解决方案。
在 SSL 实验室报告中的"证书路径"标题下,它显示:
1 Sent by server www.exampleSITE.com
BLAH BLAH BLAH
2 Extra download Let's Encrypt Authority X3
BLAH BLAH BLAH
3 In trust store DST Root CA X3 Self-signed
BLAH BLAH BLAH
我导航到/etc/ssl/certs/
,注意到没有Let's Encrypt
证书。然后我下载了.pem并重新散列。
cd /etc/ssl/certs
sudo wget https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem
sudo c_rehash
然后我测试了之前给我一个错误的 python 行
page = urllib.request.urlopen('https://www.exampleSITE.com').read()
它成功检索了该页面。