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

Posted

技术标签:

【中文标题】使用基于主机名而不是 IP 地址的自签名证书保护 Python WebSocket【英文标题】:Secure Python WebSocket with a self-signed Certificate based on a Hostname instead of an IP Address 【发布时间】:2020-09-23 17:39:28 【问题描述】:

我正在使用python docs for WebSocket 中的示例进行安全 WebSocket 的简单 Python (3.8) 实现。这是在 Windows 10 计算机和连接到交换机/集线器的 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())

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

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 代码进行任何更改?

【问题讨论】:

【参考方案1】:
uri = "wss://164.123.456.2:1234"

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

【讨论】:

在 URL 中尝试主机名时,出现 python 错误,指出主机名无法解析为 IP 地址。如何确保在客户端系统上将域名解析为 IP 地址? @philaeagles:正常的方法是为此使用 DNS。如果这仅适用于单台机器,您可以在这台机器上edit the hosts file。

以上是关于使用基于主机名而不是 IP 地址的自签名证书保护 Python WebSocket的主要内容,如果未能解决你的问题,请参考以下文章

在android应用程序中连接到主机名而不是IP地址

PowerShell代码签名:如何使用带密码保护的自签名证书?

使用 HttpClient 信任自签名证书

wss 连接中忽略的自签名证书 SAN

App Store 是不是接受自签名证书应用程序?

WCF 中的自签名证书问题 - 必须具有私钥