请求无法使用提供的.pem文件连接到服务器,但urrlib3即使没有它也能工作



我正试图通过请求库向服务器发送HTTP请求(只能从特定网络访问,因此所有名称都是虚构的(,但我收到了SSL错误。

我的代码:

site_auth = HTTPBasicAuth("user", "password")
response = requests.get("https://my.hidden.site.com", auth=site_auth , verify="location/src/mycertfile.pem")

我也尝试过这个:

site_auth = HTTPBasicAuth("user", "password")
response = requests.get("https://my.hidden.site.com", auth=site_auth , verify=True, cert="location/src/mycertfile.pem")

但结果还是一样的。

Stacktrace:

Traceback (most recent call last):
File "location.venvlibsite-packagesurllib3connectionpool.py", line 670, in urlopen
httplib_response = self._make_request(
File "location.venvlibsite-packagesurllib3connectionpool.py", line 381, in _make_request
self._validate_conn(conn)
File "location.venvlibsite-packagesurllib3connectionpool.py", line 976, in _validate_conn
conn.connect()
File "location.venvlibsite-packagesurllib3connection.py", line 361, in connect
self.sock = ssl_wrap_socket(
File "location.venvlibsite-packagesurllib3utilssl_.py", line 377, in ssl_wrap_socket
return context.wrap_socket(sock, server_hostname=server_hostname)
File "C:UsersAdamAppDataLocalProgramsPythonPython38-32libssl.py", line 500, in wrap_socket
return self.sslsocket_class._create(
File "C:UsersAdamAppDataLocalProgramsPythonPython38-32libssl.py", line 1040, in _create
self.do_handshake()
File "C:UsersAdamAppDataLocalProgramsPythonPython38-32libssl.py", line 1309, in do_handshake
self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1108)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "location.venvlibsite-packagesrequestsadapters.py", line 439, in send
resp = conn.urlopen(
File "location.venvlibsite-packagesurllib3connectionpool.py", line 724, in urlopen
retries = retries.increment(
File "location.venvlibsite-packagesurllib3utilretry.py", line 439, in increment
raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='my.hidden.site.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1108)')))
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "location/main_playground.py", line 15, in <module>
response = requests.get("https://my.hidden.site.com", auth=rt_auth, verify="location/mycertfile.pem")
File "location.venvlibsite-packagesrequestsapi.py", line 76, in get
return request('get', url, params=params, **kwargs)
File "location.venvlibsite-packagesrequestsapi.py", line 61, in request
return session.request(method=method, url=url, **kwargs)
File "location.venvlibsite-packagesrequestssessions.py", line 530, in request
resp = self.send(prep, **send_kwargs)
File "location.venvlibsite-packagesrequestssessions.py", line 643, in send
r = adapter.send(request, **kwargs)
File "location.venvlibsite-packagesrequestsadapters.py", line 514, in send
raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='my.hidden.site.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1108)')))

我正在使用以下版本的libs:

requests==2.23.0
urllib3==1.25.9

我在谷歌上搜索了很多,但都不适用。我手动下载了证书链,并创建了我的.pem文件,我用openssl验证了该文件。我有3个证书在里面,我有他们从根到我的目标网站排序,从上到下的方式如下:

-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

奇怪的是,即使没有.pem文件和显式启用的证书,urrlib3也能正常工作,代码:

http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED')
r = http.request('GET', "https://my.hidden.site.com", fields={'user': "user", 'pass': "password"})
print(r)
print(r.data.decode('utf-8'))

另一件奇怪的事情是,curl默认情况下不起作用,但当我提供.pem文件时,它的工作方式应该是这样的,所以请求也应该是这样,对吧?

curl https://my.hidden.site.com --cacert mycertfile.pem

我也试过Postman,但他甚至不能在设置中使用选定的.pem文件。

Could not get any response
There was an error connecting to https://my.hidden.site.com.

我也试过在我的机器(Windows 10(上安装根证书和中间证书,但没有效果。有人知道可能出了什么问题吗?或者我下一步应该检查什么?

感谢的帮助

//编辑1

我使用这个命令创建了证书链,并从中提取了3个密钥。我已经像前面提到的那样更改了.pem文件中的顺序,因此根CA的密钥在.pem文件中将是第一个,中间密钥是第二个,my.hidden.site.com的密钥是第三个。

我可以看到有行无法获得本地颁发者证书验证错误:num=21:无法验证第一个证书,但我不知道是否有问题,或者可能缺少什么。

$ openssl s_client -showcerts -connect my.hidden.site.com:443
CONNECTED(0000017C)
---
Certificate chain
0 s:SECRET LINE my.hidden.site.com
i:SECRET LINE intermediate
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
1 s:SECRET LINE intermediate
i:SECRET LINE root ca
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
2 s:SECRET LINE root ca
i:SECRET LINE root ca
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
---
Server certificate
subject=SECRET LINE my.hidden.site.com
issuer=SECRET LINE intermediate
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 4618 bytes and written 444 bytes
Verification error: unable to verify the first certificate
---
New, TLSv1.2, 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: <HIDDEN_SESSION_ID>
Session-ID-ctx:
Master-Key: <HIDDEN_MASTER_KEY>
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 300 (seconds)
TLS session ticket:
<HIDDEN_SESSION_TICKET>
Start Time: 1588693969
Timeout   : 7200 (sec)
Verify return code: 21 (unable to verify the first certificate)
Extended master secret: no
---
closed
depth=0 HIDDEN DETAILS, CN = my.hidden.site.com
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 HIDDEN DETAILS CN = my.hidden.site.com
verify error:num=21:unable to verify the first certificate
verify return:1
requests.get("https://my.hidden.site.com", auth=site_auth , verify=True, cert="location/src/mycertfile.pem")

根据文档,cert是客户端证书的路径,用于双向TLS的情况。这是服务器同时验证证书的时候,所以这里不是这样。

verify是到CA束的路径。

我已经手动下载了证书链并创建了我的.pem

这取决于您如何下载证书链,但服务器不会在TLS握手期间发送根CA证书。作为客户,建立信任链是你的工作。那该怎么办?这取决于my.hidden.site.com证书是如何生成的。您可以检查链,看看谁是最顶级证书的颁发者。如果是某个全局CA,你可以在网上找到证书,但要小心。

另一件奇怪的事情是,curl默认情况下不工作,但当我提供.pem文件时,它的工作方式应该是这样的,所以请求也应该是这样,对吧?

没错;/。

我测试过https://stackoverflow.com使用浏览器匹配的实际CA和握手期间发送的中间CA,中间CA不起作用。你可能在使用中间的那个,但旋度起作用真的很奇怪。

编辑:正如我前面所说,CA不是在握手中发送的,所以您不会在openssl s_client输出中看到它。您可以通过检查CA是否是自签名的来验证这一点。检查颁发者是否与主题相同。

关于为什么将中间体CA指定为--cacert对于低于7.68.0的旋度不起作用,请参阅以下答案。

最新更新