philaeagles
philaeagles

Reputation: 3

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

I’m doing a simple Python (3.8) implementation of Secure WebSocket, using the examples in the python docs for WebSocket. This is between a Windows 10 computer and an Ubuntu 18.04.2 Linux Machine connected to a switch/hub. The rough server and client code are below:

Server:

# 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()

Client:

# 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())

I was able to get this implementation to work with a self-signed certificate (created using openssl) with a Subject Alternate name (SAN) field for the IP address. I want to use a certificate that is based on a hostname instead of an IP address. I’ve added ‘DNS Name =Hostname’ to the SAN certificate field, but this hasn’t worked. It resulted in the following error:

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

Is this possible? What fields should be added to the certificate to make this work? Should any changes be made to the python Code?

Upvotes: 0

Views: 3237

Answers (1)

Steffen Ullrich
Steffen Ullrich

Reputation: 123380

uri = "wss://164.123.456.2:1234"

The certificate must match the domain/IP used in the URL, in this case 164.123.456.2. If you want to use a domain you have to have the domain name both in the certificate and also use it in the URL. Of course the domain name must resolve to the correct IP address on the client systems.

Upvotes: 1

Related Questions