Requests: SSLError“错误握手:错误([('SSL例程','tls_process_server_certificate','证书验证失败')],)与自签名证书

创建于 2017-11-10  ·  12评论  ·  资料来源: psf/requests

你好。 我正在尝试使用 https 连接到服务器。 我的证书是一个自签名证书,它包含在验证参数中,但结果是错误“证书验证失败”错误。 我怀疑这与自签名的证书(由 Microsoft IIS)有关,但使用 curl 可以。
提前致谢!

这是 openssl 输出:

openssl s_client -showcerts -connect server:44300
CONNECTED(00000003)
depth=0 CN = server
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = server
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:/CN=server
   i:/CN=server
-----BEGIN CERTIFICATE-----
<certificate data here>
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=server
issuer=/CN=server
---
No client certificate CA names sent
Peer signing digest: SHA1
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 1477 bytes and written 431 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: CC4A000083B1E03B446416C9C0B16CBEAB79949E3CF5C936A309A6F92FA01364
    Session-ID-ctx:
    Master-Key: 798A570B0EC2A0CBB7C4C4DE6167E7579A92239942D869CD794B8BEBEA6EB5E492394634AD32665A8BB829DE1F3858D2
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1510329948
    Timeout   : 300 (sec)
    Verify return code: 21 (unable to verify the first certificate)
---

预期结果

我期待,因为证书在验证参数中,连接不会失败。 如果我在 python 之外尝试使用 curl ,它可以工作:

curl https://server:44300 --cacert /usr/share/ca-certificates/server.crt
 HTTP/1.1 403 Forbidden
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 1158
Content-Type: text/html
Server: Microsoft-IIS/10.0
X-Frame-Options: SAMEORIGIN
P3P: CP=None
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Headers: X-Requested-With,Content-Type
Access-Control-Allow-Credentials: true
Date: Fri, 10 Nov 2017 16:02:26 GMT

(它失败但不是因为证书问题)

实际结果

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/api.py", line 72, in get
    return request('get', url, params=params, **kwargs)
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/api.py", line 58, in request
    return session.request(method=method, url=url, **kwargs)
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/sessions.py", line 508, in request
    resp = self.send(prep, **send_kwargs)
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/sessions.py", line 618, in send
    r = adapter.send(request, **kwargs)
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/adapters.py", line 506, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='nlybstqvp4nb75n.code1.emi.philips.com', port=44300): Max retries exceeded with url: / (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",),))

复制步骤

import requests
requests.get('https://server:44300', verify='/usr/share/ca-certificates/server.crt')

系统信息

$ python -m requests.help
{
  "chardet": {
    "version": "3.0.4"
  },
  "cryptography": {
    "version": "2.1.3"
  },
  "idna": {
    "version": "2.6"
  },
  "implementation": {
    "name": "CPython",
    "version": "2.7.12"
  },
  "platform": {
    "release": "4.10.0-38-generic",
    "system": "Linux"
  },
  "pyOpenSSL": {
    "openssl_version": "1010007f",
    "version": "17.3.0"
  },
  "requests": {
    "version": "2.18.4"
  },
  "system_ssl": {
    "version": "1000207f"
  },
  "urllib3": {
    "version": "1.22"
  },
  "using_pyopenssl": true
}

此命令仅适用于 Requests v2.16.4 及更高版本。 否则,
请提供有关您的系统的一些基本信息(Python 版本,
操作系统等)。

最有用的评论

如果您是 python requests模块的初学者并且想要做一些需要访问安全站点的事情,那么您很有可能会被这个错误注定 - Certificate verify failed并且像所有初学者程序员一样会很想使用auth = session.post( mysecureurl, verify=false)

但这是非常糟糕的做法,并且在很多 SO 帖子中都被劝阻,但初学者仍然会滥用它,因为这个错误很难修复。
让我尝试对这个问题进行一些说明。
Python(pip) 和 Conda 以及任何基于 python 的软件都使用单独的证书存储,就像所有浏览器一样。 Python Requests 库默认使用它自己的 CA 文件,或者如果安装了 certifi 包的证书包,将使用它。 此外,与curl不同,pip 不使用系统证书。
因此,对于requests ,您必须通过 conda 或 pip 手动指定证书存储。

特长;

  1. 根据此处显示的这个惊人的博客,使用浏览器导出所有.cer编码的证书链。 请注意,该博客不是关于 conda certstore 而是 git certstore ,它只说导出根目录,但是我将所有证书链导出到单独的文件中。
  2. 接下来,使用命令pip install certifi安装certifi #$
  3. 检查 conda 或 python 的证书存储的默认路径:

import ssl
ssl.get_default_verify_paths()
import certifi
certifi.where()

  1. 找到默认的cacert.pem文件后,打开它(最好在 Notepad++ 中)并在文件末尾附加所有证书。 (注意证书划分-----BEGIN CERTIFICATE----------END CERTIFICATE----- )。 保存文件,就是这样。
    或者,如果您使用 conda,请使用 conda 命令:
    conda config --set ssl_verify <pathToYourFile>.crt
    (我注意到这个命令会更新C:\Users\johndoe\.condarc中的内容)

  2. 使用以下代码进行验证:
    import certifi
    auth = session.post('https://mysecuresite.com/', cert=());

此外,如果您使用的是 linux,则可以使用此链接将自定义 cacert 导出到系统范围或用户配置文件( .bashrc.bash_profile )。

所有12条评论

顺便说一句,这是 Ubuntu 16.04

这个错误几乎可以肯定是因为证书本身在某种程度上是无效的。 你能提供 PEM 编码的证书本身吗?

你好。 感谢你及时的答复。 这是证书:
-----开始证书-----
MIID3TCCAsWgAwIBAgIIU/nMdlbWojMwDQYJKoZIhvcNAQELBQAwMDEuMCwGA1UE
AwwlTkxZQlNUUVZQNE5CNzVOLmNvZGUxLmVtaS5waGlsaXBzLmNvbTAeFw0xNzEx
MTAwMDAwMDBaFw0yNzExMTAwMDAwMDBaMDAxLjAsBgNVBAMMJU5MWUJTVFFWUDRO
Qjc1Ti5jb2RlMS5lbWkucGhpbGlwcy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQC3mzWep6k1/FbkzzoyZ8QBy/tE8adfwKvw80zaLL+car1bBZ9U
VIXs4es3babtjD3QJWP5/mwoBdIp8gvQkjA1X7RBNJZXbPz6hGR4eqaeRQLrFV9Y
TtB92MA9CDpCXfalCvzzO1jw3zvP1BHUdnTQEwSnnwtf/Ryaud+e7TDxGq8LThmc
glZgO8d2zaYpIjwWx92bXDF/qlqWBkH5mtKIkWOw6Y71xz0Di62cFrMAPEGBjK3c
szpBa5Ttb9+SFtl16t2xDyCbiPFkoMW/4u3Husy/i18hLhEuQwZMHnWsocm+etZ4
8fDt5Bqhab8zC+LKS+Ll7qZdqMHzobeB6j5JAgMBAAGjgfowgfcwXwYDVR0jBFgw
VoAUGdJ3os9nPtTubuwcy1ugtDMdSMChNKQyMDAxLjAsBgNVBAMMJU5MWUJTVFFW
UDROQjc1Ti5jb2RlMS5lbWkucGhpbGlwcy5jb22CCFP5zHZW1qIzMB0GA1UdDgQW
BBQZ0neiz2c+1O5u7BzLW6C0Mx1IwDAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIE
sDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwOwYDVR0RBDQwMoIlTkxZ
QlNUUVZQNE5CNzVOLmNvZGUxLmVtaS5waGlsaXBzLmNvbYIJbG9jYWxob3N0MA0G
CSqGSIb3DQEBCwUAA4IBAQC0MtflowNB4LTLKD1MW3w0QIY5ale3/sEhNCQgHGN5
iNZJptFuFt5jgPGmFjy0Pb5vLMS/Ag1RF3UgTeZzFnaSgyB4mTnwj1gLdwQidVcr
2SlL7TffCj0m/bYjtNbwExRqXE4pQKb5RKwYwpruZaX/G3oHWOG9+2X9Pw5C42zB
OFE4KvYUwOV+noabXvil8LERIdKYxR/2B6qBiwm47IcioqM07zTYLHJ+WDTEMO2k
Qy51yXwFmeOer5MIBElYCQ0j2AfI4RCXr+2cyUym7tjEr3/I8EsZ5Crvdf++Gwaz
2A05ScPMr+5yfVXygZCenMTwNAyUY1yN9zVj8/n94Psa
-----结束证书-----
我知道并不完全有效。 但不知何故 curl 能够接受它。 我想知道为什么 requests 不这样做。 我假设两者都使用 openssl 来验证证书。

对此有何更新? 我还面临 python 请求库中自签名认证站点的问题。

低于错误。

requests.get('https://10.10.24.20', verify='/etc/ssl/certs/certSIGN_ROOT_CA.pem')
回溯(最近一次通话最后):
文件 ”",第 1 行,在
文件“/usr/local/lib/python2.7/dist-packages/requests/api.py”,第 72 行,在 get
返回请求('get',url,params=params, *kwargs)文件“/usr/local/lib/python2.7/dist-packages/requests/api.py”,第 58 行,在请求中返回 session.request(method=method, url=url, * kwargs)
文件“/usr/local/lib/python2.7/dist-packages/requests/sessions.py”,第 508 行,在请求中
resp = self.send(prep, *send_kwargs)文件“/usr/local/lib/python2.7/dist-packages/requests/sessions.py”,第 618 行,在发送中r = adapter.send(request, * kwargs)
文件“/usr/local/lib/python2.7/dist-packages/requests/adapters.py”,第 506 行,在发送中
引发 SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='10.10.24.20', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate ', '证书验证失败')],)",),))

如果我保持 verify False 它可以工作,但我希望它带有 verify=True

之前没有看到这个,所以我开了一个新问题,对此感到抱歉。 但我面临同样的问题。 对我来说,即使验证 = False,“请求”也会失败。

$蟒蛇
Python 2.7.13(默认,2017 年 1 月 19 日,14:48:08)
[GCC 6.3.0 20170118] 在 linux2 上
输入“帮助”、“版权”、“信用”或“许可”以获取更多信息。

导入请求
requests.get("https://localhost:9000/getcpuinfo", verify=False)
回溯(最近一次通话最后):
文件 ”",第 1 行,在
文件“/usr/lib/python2.7/dist-packages/requests/api.py”,第 70 行,在 get
返回请求('get',url,params=params, *kwargs)文件“/usr/lib/python2.7/dist-packages/requests/api.py”,第 56 行,在请求中返回 session.request(method=method, url=url, * kwargs)
请求中的文件“/usr/lib/python2.7/dist-packages/requests/sessions.py”,第 488 行
resp = self.send(prep, *send_kwargs)文件“/usr/lib/python2.7/dist-packages/requests/sessions.py”,第 609 行,在发送中r = adapter.send(request, * kwargs)
发送中的文件“/usr/lib/python2.7/dist-packages/requests/adapters.py”,第 497 行
引发 SSLError(e, request=request)
requests.exceptions.SSLError: ("糟糕的握手: SysCallError(-1, 'Unexpected EOF')",)

我的本地证书信息是,

$ openssl s_client -showcerts -connect localhost:9000
CONNECTED(00000003)
depth=0 CN = localhost
verify error:num=18:self signed certificate
verify return:1
depth=0 CN = localhost
verify return:1
write:errno=0
---
Certificate chain
 0 s:/CN=localhost
   i:/CN=localhost
-----BEGIN CERTIFICATE-----
MIIC/jCCAeagAwIBAgIJAKATu2AY/QT4MA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0xNzExMDkyMTQ1NTBaFw0xNzEyMDkyMTQ1NTBaMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAJTPk5nao0wG/EDFGnq7BvXMkEZ5oUVq7PAUxWi+E/byJk924l7Z5kACgWBa
zQL0lLXLpdMk97EFGWMblz5Ehtqh7U8HaE9OZ6x/pesDTka+REnpXecklRrdZHX7
lfFnNIU58grPpB2GyUXrRdOtcPlaKXUo+VTd7PgwMtYVtt8pyTWxSB2MMYrqJGT8
78KX6trRzQLm7tas3U0jD59+R8j7gxU6FyFaNJBrkJ5T9kHGKOsAzSqZdCgRBjl5
i7xcXJfOAAnZ3jhGlY5DQht+HZDHhjkLG9kcZZhFDYteFk8drzbd3lBw96nLq+8A
Sy92FtQL4GiYSwZ0WVAmwmTCGjUCAwEAAaNTMFEwHQYDVR0OBBYEFLYjwGKbcV9h
sYHxe8l9UvXVivByMB8GA1UdIwQYMBaAFLYjwGKbcV9hsYHxe8l9UvXVivByMA8G
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAHl7okBCJlms+cwfzLhs
sbyyuX2wgngxyvjy497zBmeh1TiueGPhOx9u/sfJSZmoUaeRd/zPGkp2DcPQ+Lo2
EHYrXMPE1Ecgpu/15JZ8jNuE+FwZb9lllULLwzq8pDkdbdsSRltdV/rFlZ2YkscB
c+xvVaCltw5KpKnY6AWHoqwoDcd8TZKzyKXLSuluKbHNC1lvg8cMzs6hFA9P92Ae
9P08AKLAIOGJ7QzRrXQIsAO4p9rHheeZeYQZyNiRrXPQUoWos4+OjynaNs+FabhN
XBtSl/GGPRRRfU/D9v4iKfQx15CEvs1AKn1Z6mIPF05pSqbgIoz2mJBV6UM7e+hz
TRs=
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=localhost
issuer=/CN=localhost
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 1198 bytes and written 302 bytes
Verification error: self signed certificate
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: 
    Session-ID-ctx: 
    Master-Key: 5311B8500C8AF327083E1465FE1E1A6A98E0996B4791150A01D6B130C7F0549909A4BDCDED388E9EDE124BB6C50E150A
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1510599077
    Timeout   : 7200 (sec)
    Verify return code: 18 (self signed certificate)
    Extended master secret: no
---

请不要劫持这个问题。 一般来说,你应该使用 Stack Overflow 来问这些问题:如果你用请求来超载问题,你只会让我倾向于关闭它。

@sg77您的证书被标记CA=FALSE ,这使得它没有资格成为发行证书。 我怀疑 curl 正在自定义他们的代码以将此证书用作 pin,而不是作为根 CA。 Requests 不这样做:此证书不能是根 CA,因此它不会验证。

我建议使用CA=TRUE创建一个新证书,或者完全省略 BasicConstraints。

@ashwini-kaklij 我不知道为什么您的验证失败,因为我看不到证书。 请不要在此处发布:将您的问题直接发送到 StackOverflow。

@uttampawar您的错误是由于服务器出于某种原因不喜欢我们的 TLS 握手引起的。 在没有更多细节的情况下,我无法确定原因。 请再次将您的问题提交给 Stack Overflow。

@Lukasa我并不是要劫持这个问题只是提升。 可能这是一个错误的地方。 我添加了我的评论和观察,因为我看到它具有相似的性质。 感谢您的反馈。 我会在stackoverflow上问。 谢谢。

请不要劫持这个问题。 一般来说,你应该使用 Stack Overflow 来问这些问题:如果你用请求来超载问题,你只会让我倾向于关闭它。

@sg77您的证书被标记CA=FALSE ,这使得它没有资格成为发行证书。 我怀疑 curl 正在自定义他们的代码以将此证书用作 pin,而不是作为根 CA。 Requests 不这样做:此证书不能是根 CA,因此它不会验证。

我建议使用CA=TRUE创建一个新证书,或者完全省略 BasicConstraints。

@ashwini-kaklij 我不知道为什么您的验证失败,因为我看不到证书。 请_不要在此处发布_:将您的问题直接发送到 StackOverflow。

@uttampawar您的错误是由于服务器出于某种原因不喜欢我们的 TLS 握手引起的。 在没有更多细节的情况下,我无法确定原因。 请再次将您的问题提交给 Stack Overflow。

嗨 Lukasa - 刚刚看到你的回复。 您已回复@sg77说“您的证书标记CA=FALSE ”。

你如何判断这是错误的,我在哪里/如何将其设置回 TRUE?

谢谢。

在线发布证书密钥就像发布您的密码

@辛尼达

在线发布证书密钥就像发布您的密码

嗯,没有。 发布证书的私钥等同于您的密码。

每个业务中的 CA 在每个主流操作系统和浏览器中都有自己的证书。 他们是否将密码泄露给所有人?

https://en.wikipedia.org/wiki/Public-key_cryptography

这个错误几乎可以肯定是因为证书本身在某种程度上是无效的。 你能提供 PEM 编码的证书本身吗?

我怎样才能获得这个证书?

如果您是 python requests模块的初学者并且想要做一些需要访问安全站点的事情,那么您很有可能会被这个错误注定 - Certificate verify failed并且像所有初学者程序员一样会很想使用auth = session.post( mysecureurl, verify=false)

但这是非常糟糕的做法,并且在很多 SO 帖子中都被劝阻,但初学者仍然会滥用它,因为这个错误很难修复。
让我尝试对这个问题进行一些说明。
Python(pip) 和 Conda 以及任何基于 python 的软件都使用单独的证书存储,就像所有浏览器一样。 Python Requests 库默认使用它自己的 CA 文件,或者如果安装了 certifi 包的证书包,将使用它。 此外,与curl不同,pip 不使用系统证书。
因此,对于requests ,您必须通过 conda 或 pip 手动指定证书存储。

特长;

  1. 根据此处显示的这个惊人的博客,使用浏览器导出所有.cer编码的证书链。 请注意,该博客不是关于 conda certstore 而是 git certstore ,它只说导出根目录,但是我将所有证书链导出到单独的文件中。
  2. 接下来,使用命令pip install certifi安装certifi #$
  3. 检查 conda 或 python 的证书存储的默认路径:

import ssl
ssl.get_default_verify_paths()
import certifi
certifi.where()

  1. 找到默认的cacert.pem文件后,打开它(最好在 Notepad++ 中)并在文件末尾附加所有证书。 (注意证书划分-----BEGIN CERTIFICATE----------END CERTIFICATE----- )。 保存文件,就是这样。
    或者,如果您使用 conda,请使用 conda 命令:
    conda config --set ssl_verify <pathToYourFile>.crt
    (我注意到这个命令会更新C:\Users\johndoe\.condarc中的内容)

  2. 使用以下代码进行验证:
    import certifi
    auth = session.post('https://mysecuresite.com/', cert=());

此外,如果您使用的是 linux,则可以使用此链接将自定义 cacert 导出到系统范围或用户配置文件( .bashrc.bash_profile )。

此页面是否有帮助?
0 / 5 - 0 等级