openssl,python 请求错误:“证书验证失败”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22027418/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 00:06:35  来源:igfitidea点击:

openssl, python requests error: "certificate verify failed"

pythonopensslpython-requests

提问by chadgh

If I run the following command from my development box:

如果我从我的开发框中运行以下命令:

$ openssl s_client -connect github.com:443

I get the following last line of output:

我得到以下最后一行输出:

Verify return code: 20 (unable to get local issuer certificate)

If I try to do this with requests I get another failed request:

如果我尝试对请求执行此操作,则会收到另一个失败的请求:

>>> import requests
>>> r = requests.get('https://github.com/', verify=True)

With an exception raised:

有一个例外:

SSLError: [Errno 1] _ssl.c:507: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

I can also run the first command with the verify flag and get similar output:

我还可以使用验证标志运行第一个命令并获得类似的输出:

$ openssl s_client -connect github.com:443 -verify 9
...
Verify return code: 27 (certificate not trusted)

Basically this is telling me that there is a problem with the certificates. I can specify a specific certificate with both methods and it will work:

基本上这告诉我证书有问题。我可以用这两种方法指定一个特定的证书,它会起作用:

$ openssl s_client -connect github.com:443 -CAfile /etc/ssl/certs/DigiCert_High_Assurance_EV_Root_CA.pem -verify 9
...
Verify return code: 0 (ok)

and:

和:

>>> r = requests.get('https://github.com/', verify='/etc/ssl/certs/DigiCert...pem')
<Response [200]>

So, to my question, what exactly is wrong here? Shouldn't requests/openssl already know where to find valid certs?

所以,对于我的问题,这里到底有什么问题?请求/openssl 不应该知道在哪里可以找到有效的证书吗?

Other Info:

其他信息:

  • Python==2.7.6
  • requests==2.2.1
  • openssl 0.9.8h
  • Python==2.7.6
  • 请求==2.2.1
  • openssl 0.9.8h

Also, I know passing verify=Falseto the requests.getmethod will work too, but I do want to verify.

另外,我知道传递verify=False给该requests.get方法也可以,但我确实想验证一下。

EDIT

编辑

I've confirmed that, as @Heikki Toivonen indicated in an answer, specifying the -CAfile flag for the version of openssl that I'm running works.

我已经确认,正如@Heikki Toivonen 在回答中指出的那样,为我正在运行的 openssl 版本指定了 -CAfile 标志。

$ openssl s_client -connect github.com:443 -CAfile `python -c 'import requests; print(requests.certs.where())'`
...
Verify return code: 0 (ok)

So there is nothing wrong with the version of openssl that I'm running, and there is nothing wrong with the default cacert.pem file that requests provides.

所以我运行的 openssl 版本没有问题,请求提供的默认 cacert.pem 文件也没有问题。

Now that I know openssl is meant to work that way, that the CAfile or the place to find certs has to be specified, I'm more concerned about getting requests to work.

既然我知道 openssl 就是这样工作的,必须指定 CAfile 或查找证书的地方,我更关心让请求工作。

If I run:

如果我运行:

>>> r = requests.get('https://github.com/', verify='path to cacert.pem file')

I'm still getting the same error as before. I even tried downloading the cacert.pem file from http://curl.haxx.se/caand it still didn't work. requests only seems to work (on this specific machine) if I specify a specific vendor cert file.

我仍然遇到与以前相同的错误。我什至尝试从http://curl.haxx.se/ca下载 cacert.pem 文件,但仍然无法正常工作。如果我指定一个特定的供应商证书文件,请求似乎只工作(在这台特定的机器上)。

A side note: On my local machine everything is working as expected. There are several difference between the two machines though. I so far haven't been able to determine what the specific difference is that causes this issue.

旁注:在我的本地机器上,一切都按预期工作。两台机器之间有几个区别。到目前为止,我还无法确定导致此问题的具体差异是什么。

回答by Heikki Toivonen

openssl s_client by default will not use the CA certificates file it ships with, but it does try to verify the connection. This is the reason why your test fails without any parameters and works with -CAfile.

openssl s_client 默认情况下不会使用它附带的 CA 证书文件,但它会尝试验证连接。这就是您的测试在没有任何参数的情况下失败并使用 -CAfile 的原因。

Similarly, Requests tries to verify the connection by default, but it seems it doesn't know where the CA certificates are. This might be a configuration issue in your environment when building/installing OpenSSL, Python or Requests. I say this because the Requests website shows your example working against https://github.comwithout needing to set the CA path.

同样,Requests 默认尝试验证连接,但它似乎不知道 CA 证书在哪里。在构建/安装 OpenSSL、Python 或 Requests 时,这可能是您环境中的配置问题。我这么说是因为 Requests 网站显示了您的示例,无需设置 CA 路径即可针对https://github.com工作。

回答by jww

If I run the following command from my development box:

$ openssl s_client -connect github.com:443

I get the following last line of output:

Verify return code: 20 (unable to get local issuer certificate)

如果我从我的开发框中运行以下命令:

$ openssl s_client -connect github.com:443

我得到以下最后一行输出:

Verify return code: 20 (unable to get local issuer certificate)

You are missing DigiCert High Assurance EV CA-1as a root of trust:

您缺少DigiCert High Assurance EV CA-1信任根:

$ openssl s_client -connect github.com:443
CONNECTED(00000003)
depth=1 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV CA-1
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
 0 s:/businessCategory=Private Organization/1.3.6.1.4.1.311.60.2.1.3=US/1.3.6.1.4.1.311.60.2.1.2=Delaware/serialNumber=5157550/street=548 4th Street/postalCode=94107/C=US/ST=California/L=San Francisco/O=GitHub, Inc./CN=github.com
   i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV CA-1
 1 s:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV CA-1
   i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA
---
Server certificate
...
Start Time: 1393392088
Timeout   : 300 (sec)
Verify return code: 20 (unable to get local issuer certificate)

Download DigiCert High Assurance EV CA-1from DigiCert Trusted Root Authority Certificates:

DigiCert High Assurance EV CA-1DigiCert Trusted Root Authority 证书下载:

$ wget https://www.digicert.com/CACerts/DigiCertHighAssuranceEVCA-1.crt
--2014-02-26 00:27:50--  https://www.digicert.com/CACerts/DigiCertHighAssuranceEVCA-1.crt
Resolving www.digicert.com (www.digicert.com)... 64.78.193.234
...

Convert the DER encoded certifcate to PEM:

将 DER 编码的证书转换为 PEM:

$ openssl x509 -in DigiCertHighAssuranceEVCA-1.crt -inform DER -out DigiCertHighAssuranceEVCA-1.pem -outform PEM

Then, use it with OpenSSL via the -CAfile:

然后,通过以下方式将其与 OpenSSL 一起使用-CAfile

$ openssl s_client -CAfile DigiCertHighAssuranceEVCA-1.pem -connect github.com:443
CONNECTED(00000003)
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV Root CA
verify return:1
depth=1 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV CA-1
verify return:1
depth=0 businessCategory = Private Organization, 1.3.6.1.4.1.311.60.2.1.3 = US, 1.3.6.1.4.1.311.60.2.1.2 = Delaware, serialNumber = 5157550, street = 548 4th Street, postalCode = 94107, C = US, ST = California, L = San Francisco, O = "GitHub, Inc.", CN = github.com
verify return:1
---
Certificate chain
 0 s:/businessCategory=Private Organization/1.3.6.1.4.1.311.60.2.1.3=US/1.3.6.1.4.1.311.60.2.1.2=Delaware/serialNumber=5157550/street=548 4th Street/postalCode=94107/C=US/ST=California/L=San Francisco/O=GitHub, Inc./CN=github.com
   i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV CA-1
 1 s:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV CA-1
   i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIHOjCCBiKgAwIBAgIQBH++LkveAITSyvjj7P5wWDANBgkqhkiG9w0BAQUFADBp
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSgwJgYDVQQDEx9EaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBFViBDQS0xMB4XDTEzMDYxMDAwMDAwMFoXDTE1MDkwMjEyMDAwMFowgfAxHTAb
BgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRMwEQYLKwYBBAGCNzwCAQMTAlVT
MRkwFwYLKwYBBAGCNzwCAQITCERlbGF3YXJlMRAwDgYDVQQFEwc1MTU3NTUwMRcw
FQYDVQQJEw41NDggNHRoIFN0cmVldDEOMAwGA1UEERMFOTQxMDcxCzAJBgNVBAYT
AlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2Nv
MRUwEwYDVQQKEwxHaXRIdWIsIEluYy4xEzARBgNVBAMTCmdpdGh1Yi5jb20wggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDt04nDXXByCfMzTxpydNm2WpVQ
u2hhn/f7Hxnh2gQxrxV8Gn/5c68d5UMrVgkARWlK6MRb38J3UlEZW9Er2TllNqAy
GRxBc/sysj2fmOyCWws3ZDkstxCDcs3w6iRL+tmULsOFFTmpOvaI2vQniaaVT4Si
N058JXg6yYNtAheVeH1HqFWD7hPIGRqzPPFf/jsC4YX7EWarCV2fTEPwxyReKXIo
ztR1aE8kcimuOSj8341PTYNzdAxvEZun3WLe/+LrF+b/DL/ALTE71lmi8t2HSkh7
bTMRFE00nzI49sgZnfG2PcVG71ELisYz7UhhxB0XG718tmfpOc+lUoAK9OrNAgMB
AAGjggNUMIIDUDAfBgNVHSMEGDAWgBRMWMsl8EFPUvQoyIFDm6aooOaS5TAdBgNV
HQ4EFgQUh9GPGW7kh29TjHeRB1Dfo79VRyAwJQYDVR0RBB4wHIIKZ2l0aHViLmNv
bYIOd3d3LmdpdGh1Yi5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsG
AQUFBwMBBggrBgEFBQcDAjBjBgNVHR8EXDBaMCugKaAnhiVodHRwOi8vY3JsMy5k
aWdpY2VydC5jb20vZXZjYTEtZzIuY3JsMCugKaAnhiVodHRwOi8vY3JsNC5kaWdp
Y2VydC5jb20vZXZjYTEtZzIuY3JsMIIBxAYDVR0gBIIBuzCCAbcwggGzBglghkgB
hv1sAgEwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9z
c2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4A
eQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQA
ZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUA
IABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAA
YQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcA
cgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIA
aQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQA
ZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMH0G
CCsGAQUFBwEBBHEwbzAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu
Y29tMEcGCCsGAQUFBzAChjtodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln
aUNlcnRIaWdoQXNzdXJhbmNlRVZDQS0xLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqG
SIb3DQEBBQUAA4IBAQBfFW1nwzrVo94WnEUzJtU9yRZ0NMqHSBsUkG31q0eGufW4
4wFFZWjuqRJ1n3Ym7xF8fTjP3fdKGQnxIHKSsE0nuuh/XbQX5DpBJknHdGFoLwY8
xZ9JPI57vgvzLo8+fwHyZp3Vm/o5IYLEQViSo+nlOSUQ8YAVqu6KcsP/e612UiqS
+UMBmgdx9KPDDzZy4MJZC2hbfUoXj9A54mJN8cuEOPyw3c3yKOcq/h48KzVguQXi
SdJbwfqNIbQ9oJM+YzDjzS62+TCtNSNWzWbwABZCmuQxK0oEOSbTmbhxUF7rND3/
+mx9u8cY//7uAxLWYS5gIZlCbxcf0lkiKSHJB319
-----END CERTIFICATE-----
subject=/businessCategory=Private Organization/1.3.6.1.4.1.311.60.2.1.3=US/1.3.6.1.4.1.311.60.2.1.2=Delaware/serialNumber=5157550/street=548 4th Street/postalCode=94107/C=US/ST=California/L=San Francisco/O=GitHub, Inc./CN=github.com
issuer=/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV CA-1
---
No client certificate CA names sent
---
SSL handshake has read 4139 bytes and written 446 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: 59D2883BBCE8E81E63E5551FAE7D1ACC00C49A9473C1618237BBBB0DD9016B8D
    Session-ID-ctx: 
    Master-Key: B6D2763FF29E77C67AD83296946A4D44CDBA4F37ED6F20BC27602F1B1A2D137FACDEAC862C11279C01095594F9776F79
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1393392673
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)


Shouldn't requests/openssl already know where to find valid certs?

请求/openssl 不应该知道在哪里可以找到有效的证书吗?

No. OpenSSL trusts nothing by default. Its a polar opposite of a browser's model, where nearly everything is trusted by default.

不。默认情况下,OpenSSL 不信任任何内容。它与浏览器模型截然相反,默认情况下几乎所有内容都是可信的。



 $ openssl s_client -connect github.com:443 -CAfile `python -c 'import requests; print(requests.certs.where())'`
 ...
 >>> r = requests.get('https://github.com/', verify='path to cacert.pem file')

Why would you trust hundreds of CAs and subordinate CAs (re: cacert.pem) when you know the one CA that is certifying the public key for the site? Trust the one required root and nothing more: DigiCert High Assurance EV CA-1.

cacert.pem当您知道一个认证站点公钥的 CA 时,您为什么要信任数百个 CA 和从属 CA(re :)?信任所需的根目录,仅此而已:DigiCert High Assurance EV CA-1.

Trusting everything - as in the browser's model - is what allowed Comodo Hacker to spoof certificates for Gmail, Hotmail, Yahoo, etc when the Diginotar root was compromised.

信任一切——就像在浏览器的模型中一样——是什么让 Comodo Hacker 在 Diginotar 根被入侵时欺骗 Gmail、Hotmail、雅虎等的证书。

回答by ecstaticpeon

From Request 2.4.0 the author recommendsusing certifi, which is a collection of Root Certificates. There's a python package for it:

从 Request 2.4.0 开始,作者建议使用certifi,它是根证书的集合。有一个 python 包:

pip install certifi