dig*_*emy 2 python ssl ssl-certificate websocket
我正在开发一个 Python 应用程序,通过安全的 websocket 协议与在本地主机上运行的服务进行通信。这是一个示例代码:
import json
import asyncio
import websockets
import ssl
import certifi
ssl_context = ssl.create_default_context()
ssl_context.load_verify_locations(certifi.where())
ssl_context.load_default_certs()
query = {
"jsonrpc": "2.0",
"method": "queryHeadsets",
"params": {},
"id": 1
}
json = json.dumps(query)
async def query(json):
async with websockets.connect("wss://emotivcortex.com:54321") as ws:
await ws.send(json)
response = await ws.recv()
print(response)
asyncio.get_event_loop().run_until_complete(query(json))
Run Code Online (Sandbox Code Playgroud)
问题是 ssl 握手不断失败并出现以下错误:
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)
Run Code Online (Sandbox Code Playgroud)
我运行的是 Windows 10,Python 3.7.3 64 位
$pip list
Package Version
---------- --------
certifi 2019.3.9
pip 19.0.3
setuptools 40.8.0
websockets 7.0
Run Code Online (Sandbox Code Playgroud)
我已经检查了服务提供的证书。它似乎有效并由 COMODO 签名。我试过了:
ssl_context = ssl.create_default_context()
ssl_context.load_verify_locations(certifi.where())
print(ssl.get_default_verify_paths())
print(ssl_context.cert_store_stats())
ssl_context.load_default_certs()
print(ssl_context.get_ca_certs())
Run Code Online (Sandbox Code Playgroud)
并发现有几个可用于 python 的 COMODO CA 证书。但是我仍然收到错误。
如果有帮助,这里是完整的错误消息:
SSL handshake failed on verifying the certificate
protocol: <asyncio.sslproto.SSLProtocol object at 0x0000020C11283048>
transport: <_SelectorSocketTransport fd=508 read=polling write=<idle, bufsize=0>>
Traceback (most recent call last):
File "C:\Users\Matyas2\Python\lib\asyncio\sslproto.py", line 625, in _on_handshake_complete
raise handshake_exc
File "C:\Users\Matyas2\Python\lib\asyncio\sslproto.py", line 189, in feed_ssldata
self._sslobj.do_handshake()
File "C:\Users\Matyas2\Python\lib\ssl.py", line 763, in do_handshake
self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)
SSL error in data received
protocol: <asyncio.sslproto.SSLProtocol object at 0x0000020C11283048>
transport: <_SelectorSocketTransport closing fd=508 read=idle write=<idle, bufsize=0>>
Traceback (most recent call last):
File "C:\Users\Matyas2\Python\lib\asyncio\sslproto.py", line 526, in data_received
ssldata, appdata = self._sslpipe.feed_ssldata(data)
File "C:\Users\Matyas2\Python\lib\asyncio\sslproto.py", line 189, in feed_ssldata
self._sslobj.do_handshake()
File "C:\Users\Matyas2\Python\lib\ssl.py", line 763, in do_handshake
self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)
Traceback (most recent call last):
File "test.py", line 37, in <module>
asyncio.get_event_loop().run_until_complete(query(json))
File "C:\Users\Matyas2\Python\lib\asyncio\base_events.py", line 584, in run_until_complete
return future.result()
File "test.py", line 32, in query
async with websockets.connect("wss://emotivcortex.com:54321") as ws:
File "C:\Users\Matyas2\Python\lib\site-packages\websockets\py35\client.py", line 2, in __aenter__
return await self
File "C:\Users\Matyas2\Python\lib\site-packages\websockets\py35\client.py", line 12, in __await_impl__
transport, protocol = await self._creating_connection
File "C:\Users\Matyas2\Python\lib\asyncio\base_events.py", line 986, in create_connection
ssl_handshake_timeout=ssl_handshake_timeout)
File "C:\Users\Matyas2\Python\lib\asyncio\base_events.py", line 1014, in _create_connection_transport
await waiter
File "C:\Users\Matyas2\Python\lib\asyncio\sslproto.py", line 526, in data_received
ssldata, appdata = self._sslpipe.feed_ssldata(data)
File "C:\Users\Matyas2\Python\lib\asyncio\sslproto.py", line 189, in feed_ssldata
self._sslobj.do_handshake()
File "C:\Users\Matyas2\Python\lib\ssl.py", line 763, in do_handshake
self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)
Run Code Online (Sandbox Code Playgroud)
与 Internet 中服务器的 SSL 连接工作正常。我错过了什么?我究竟做错了什么?
如果需要,我很乐意提供其他信息。
编辑: 该证书适用于 emotivcortex.com,由 COMODO RSA 域验证安全服务器 CA 颁发,因此我认为它不是自签名证书。开放式SSL:
$python -c "import ssl; print(ssl.OPENSSL_VERSION)"
OpenSSL 1.1.0j 20 Nov 2018
Run Code Online (Sandbox Code Playgroud)
通过检查 OpenSSL 中服务提供的证书,我发现该证书是由“COMODO RSA 域验证安全服务器 CA”颁发的。这个特定机构的 CA 证书实际上并不存在于 python 包的 CA 包中certifi(有不同的 COMODO... 证书)。
从 CA 的网页手动下载缺少的 PEM 格式的证书,并将其添加到您的代码中使用的 CA 包中。
此外,应用程序代码中存在一个错误:调用函数时websockets.connect(),传递关键字参数,ssl=ssl_context以便实际使用之前指定的 CA 包。正确的代码如下所示:
import json
import asyncio
import websockets
import ssl
import certifi
ssl_context = ssl.create_default_context()
ssl_context.load_verify_locations(certifi.where())
query = {
"jsonrpc": "2.0",
"method": "queryHeadsets",
"params": {},
"id": 1
}
json = json.dumps(query)
async def query(json):
async with websockets.connect("wss://emotivcortex.com:54321", ssl=ssl_context) as ws:
await ws.send(json)
response = await ws.recv()
print(response)
asyncio.get_event_loop().run_until_complete(query(json))
Run Code Online (Sandbox Code Playgroud)
非常感谢larsks和Steffen Ullrich为我指明了正确的方向。