使用基于主机名而非 IP 地址的自签名证书保护 Python WebSocket

Secure Python WebSocket with a self-signed Certificate based on a Hostname instead of an IP Address

我正在做一个简单的 Python (3.8) 安全 WebSocket 实现,使用 python docs for WebSocket 中的示例。这是在 Windows 10 计算机和连接到 switch/hub 的 Ubuntu 18.04.2 Linux 计算机之间。粗略的服务端和客户端代码如下:

服务器:

# WSS (WS over TLS) server example, with a self-signed certificate
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("localhost.pem")
private_key = pathlib.Path(__file__).with_name("private.key")
ssl_context.load_cert_chain(localhost_pem,private_key)

start_server = websockets.serve(
    hello, "164.123.456.2", 1234, ssl=ssl_context)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

客户:

# WSS (WS over TLS) client example, with a self-signed certificate
import asyncio
import pathlib
import ssl
import websockets

ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
localhost_pem = pathlib.Path(__file__).with_name("localhost.pem")
private_key = pathlib.Path(__file__).with_name("private.key")
ssl_context.load_verify_locations(localhost_pem, private_key )

async def hello():
    uri = "wss://164.123.456.2:1234"
    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.get_event_loop().run_until_complete(hello())

我能够使此实现与自签名证书(使用 openssl 创建)一起使用,该证书具有 IP 地址的主题备用名称 (SAN) 字段。我想使用基于主机名而不是 IP 地址的证书。我已将“DNS 名称 = 主机名”添加到 SAN 证书字段,但这没有用。它导致了以下错误:

ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: IP address mismatch, certificate is not valid for “164.123.456.2”  (_ssl.c:1123)

这可能吗?应该将哪些字段添加到证书中才能使其正常工作?是否应该对 python 代码进行任何更改?

uri = "wss://164.123.456.2:1234"

证书必须与 URL 中使用的 domain/IP 匹配,在本例中为 164.123.456.2。如果你想使用一个域,你必须在证书中有域名,并在 URL 中使用它。当然,域名必须解析为客户端系统上的正确 IP 地址。