无法使用 python 3 websocket 库连接到远程安全 web socket服务器
Posted
技术标签:
【中文标题】无法使用 python 3 websocket 库连接到远程安全 web socket服务器【英文标题】:Cannot connect to remote secure web socket server using python 3 websocket library 【发布时间】:2021-09-17 06:53:24 【问题描述】:我有一个在 python 3 中实现的安全 Web 套接字服务器,该服务器在地址为 RASPI_ADDRESS 的 RaspberryPI 设备上运行,暴露在端口 8000 上。 在 RaspberryPI 设备上,这是 ssl 版本显示的内容:
>>> import ssl
>>> print(ssl.OPENSSL_VERSION)
OpenSSL 1.1.1d 10 Sep 2019
出于测试目的,我使用的是自签名证书,由 openssl: 证书文件 cert.pem 生成,伴随的私钥在 key.pem 中。 p>
在客户端,我在一台 Windows 机器上,我按如下方式实现了客户端(上面的相同 cert.pem 文件可在此处作为本地副本获得):
import ssl
import websocket
ws = websocket.WebSocket(sslopt="ssl_version": ssl.PROTOCOL_TLSv1, "certfile": "cert.pem")
try:
ws.connect("wss://RASPI_ADDRESS:8000")
ws.send("Hello, Server")
print(ws.recv())
ws.close()
except Exception as e:
print("Exception: ", e)
我在 ws.connect(...) 上遇到了这个异常:
Exception: [SSL] PEM lib (_ssl.c:4065)
(如果我使用“ws://...”以非安全方式连接,它可以工作)
很遗憾,在搜索此错误时,我没有得到很多相关结果。我也尝试在 sslopt 中提供私钥(“keyfile”:“key.pem”),但随后脚本似乎陷入了一些同步阻塞 - 无一例外,屏幕上没有列出任何内容,但也没有收到任何内容在服务器端。
关于我做错了什么的任何指针?
【问题讨论】:
certfile
选项用于客户端证书的证书 - 在这种情况下,它还需要私钥。您可能正在寻找 ca_certs
选项。
@SteffenUllrich 谢谢,这个主意听起来不错!不幸的是,它似乎并没有改变任何东西。
如果您仍然收到 exact 相同的错误,则该文件格式错误,即它不是 PEM 文件或行尾错误或类似。如果没有关于文件的更多信息而不仅仅是它的名称,很难分辨。如果您现在遇到不同的错误,请调整您的问题以反映您在做什么以及您遇到的错误类型。
对不起,我的错,这是星期一早上的情况:-/我不再遇到同样的错误;事实上,我根本没有收到任何错误,但我也没有看到服务器端收到任何内容。在发送密钥文件时,该脚本似乎也处于我在帖子中提到的相同同步阻塞状态。
您确定 wss 服务器是否正常工作,即它是 a) 真正的 websocket 而不是普通的 socket 和 b) 真正的 wss 而不是 ws?您是如何对此进行测试的?
【参考方案1】:
最后,我通过使用 websockets 库重写服务器和客户端解决了这个问题:https://pypi.org/project/websockets/
也许它也可以与我之前使用的 websocket-client 库 https://pypi.org/project/websocket-client/ 一起运行,但文档部分不一致且令人困惑。在这里编写一个简化的工作解决方案以供将来参考,以虚拟回显服务器的形式。
在 RasPI 上运行的服务器(在 IP 地址为 RASPI_IP 的 LAN 中可见)
import asyncio
import pathlib
import ssl
import websockets
async def hello(websocket, path):
name = await websocket.recv()
print(f"<<< name")
greeting = f"Hello name!"
await websocket.send(greeting)
print(f">>> greeting")
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
localhost_pem = pathlib.Path(__file__).with_name("key_cert.pem")
ssl_context.load_cert_chain(localhost_pem)
async def main():
async with websockets.serve(hello, "0.0.0.0", 8765, ssl=ssl_context):
await asyncio.Future() # run forever
asyncio.run(main())
注意 websockets.serve() 中的“0.0.0.0”主机 IP!如果我们将它设置为“localhost”,客户端将看到一个堆栈跟踪以这个错误结束:
ConnectionRefusedError: [WinError 1225] The remote computer refused the network connection
在 Windows 机器上运行的客户端:
import asyncio
import pathlib
import ssl
import websockets
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
localhost_pem = pathlib.Path(__file__).with_name("key_cert.pem")
ssl_context.load_verify_locations(localhost_pem)
uri_linux = "wss://RASPI_IP:8765"
async def hello():
uri = uri_linux
async with websockets.connect(uri, ssl=ssl_context) as websocket:
name = input("What's your name? ")
await websocket.send(name)
print(f">>> name")
greeting = await websocket.recv()
print(f"<<< greeting")
asyncio.run(hello())
与原始实现相比,这至少让我有所反应,然后我遇到了这个错误:
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: IP address mismatch, certificate is not valid for *RASPI_IP*. (_ssl.c:1129)
这可以通过使用 SAN 而不是仅 CN 生成证书来解决:https://serverfault.com/a/880809
此外,我将证书和密钥合并到一个文件中:cat key.pem cert.pem > key_cert.pem
【讨论】:
以上是关于无法使用 python 3 websocket 库连接到远程安全 web socket服务器的主要内容,如果未能解决你的问题,请参考以下文章
使用Python Websockets库建立WebSocket客户端链接
如何使用 Python websockets 库获取“ping”调用的“pong”响应?
Python websockets 服务器和 websockets 客户端在运行这两个任务时使用 asyncio 断言错误