使用基于主机名而非 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 地址。
我正在做一个简单的 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 地址。