Reputation: 123
Hi I'm using the python library Websockets. In developement everything was working, but on the server it crashes because it needs to use WSS. The link above gives an example how to do this:
#!/usr/bin/env python
# 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")
ssl_context.load_cert_chain(localhost_pem)
start_server = websockets.serve(
hello, "localhost", 8765, ssl=ssl_context
)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
This code is straightforward enough, but I am totally lost on how to generate the file it wants (both for server and client). I've researched "creating a pem file" to no avail and have recieved all sorts of ssl errors. Can someone please explain how to create the pem file for this application? Thanks
EDIT: Per the answer I used
sudo openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem
this created two files.
My server now successfully listens by doing:
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
path_cert = pathlib.Path(__file__).with_name("cert.pem")
path_key = pathlib.Path(__file__).with_name("key.pem")
ssl_context.load_cert_chain(path_cert, keyfile=path_key)
print("Listening for connection...")
start_server = websockets.serve(handler, HOSTNAME, PORT, ssl=ssl_context)
The only part left I'm having issues is getting the client to connect, I try:
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
path_cert = pathlib.Path(__file__).with_name("cert.pem")
ssl_context.load_cert_chain(path_cert)
async with websockets.connect(uri, ssl=ssl_context) as websocket:
But I get the error: ssl.SSLError: [SSL] PEM lib (_ssl.c:3854)
I also tried:
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
path_cert = pathlib.Path(__file__).with_name("cert.pem")
path_key = pathlib.Path(__file__).with_name("key.pem")
ssl_context.load_cert_chain(path_cert, keyfile=path_key)
async with websockets.connect(uri, ssl=ssl_context) as websocket:
and get the error ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1076)
EDIT2: Per answer I tried this for the client:
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ssl_context.load_verify_locations()
async with websockets.connect(uri, ssl=ssl_context) as websocket:
This generates a new error: TypeError: cafile, capath and cadata cannot be all omitted
Trying the second suggestion:
ssl_context = ssl.create_default_context()
ssl_context.load_verify_locations(certifi.where())
async with websockets.connect(uri, ssl=ssl_context) as websocket:
Generates error: ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1076)
EDIT3: The final working client:
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
path_cert = pathlib.Path(__file__).with_name("cert.pem")
ssl_context.load_verify_locations(path_cert)
async with websockets.connect(uri, ssl=ssl_context) as websocket:
Upvotes: 3
Views: 3836
Reputation: 2137
PEM file contains somethings about public key or|and private key or certificates, and it has base64-encoded bit of data. PEM means Privacy-Enhanced Mail for mail security standard. It includes header and footer lines of the form
How to create a self-signed PEM file
openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem
How to create a PEM file from existing certificate files that form a chain (optional) Remove the password from the Private Key by following the steps listed below:
openssl rsa -in server.key -out nopassword.key
How to create a PEM file with the help of an automated script:
Download NetIQ Cool Tool OpenSSL-Toolkit.
Select Create Certificates | PEM with key and entire trust chain
Provide the full path to the directory containing the certificate files.
Provide the filenames of the following: private key public key (server crt) (conditional) password for private key (conditional) any intermediate certificate chain file(s)
You will have something like this:
-----BEGIN RSA PRIVATE KEY-----
(Private Key: domain_name.key contents)
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
(Primary SSL certificate: domain_name.crt contents)
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
(Intermediate certificate: certChainCA.crt contents)
-----END CERTIFICATE----
You can decode it:
openssl x509 -in cert.pem -text -noout
You will have something like this:
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 0 (0x0)
Signature Algorithm: ecdsa-with-SHA256
Issuer: C = BE, O = GnuTLS, OU = GnuTLS certificate authority, ST = Leuven, CN = GnuTLS certificate authority
Validity
Not Before: May 23 20:38:21 2011 GMT
Not After : Dec 22 07:41:51 2012 GMT
Subject: C = BE, O = GnuTLS, OU = GnuTLS certificate authority, ST = Leuven, CN = GnuTLS certificate authority
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:52:d8:8d:23:8a:e3:67:d7:86:36:b1:20:0b:09:
7d:c8:c9:ba:a2:20:95:2f:c5:4a:63:fa:83:5f:ce:
78:2f:8f:f3:62:ca:fd:b7:f7:80:56:9d:6e:17:b9:
0e:11:4c:48:b2:c0:af:3b:59:17:16:30:68:09:07:
99:17:fe:dd:a7
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Key Usage: critical
Certificate Sign, CRL Sign
X509v3 Subject Key Identifier:
F0:B4:81:FE:98:12:BF:B5:28:B9:64:40:03:CB:CC:1F:66:4E:28:03
Signature Algorithm: ecdsa-with-SHA256
30:45:02:20:31:ae:c0:3d:4a:3f:21:be:85:17:fc:f0:c7:b2:
31:07:2a:38:56:43:d1:36:d5:95:e1:7e:52:c0:06:43:87:a7:
02:21:00:97:8c:0e:b8:3c:0a:41:af:ae:a5:cf:06:7e:d5:c4:
d8:2f:ff:e2:62:80:34:10:ba:22:dd:35:81:46:93:22:9a
For client section you wrote:
ssl_context.load_cert_chain(path_cert, keyfile=path_key)
Replace it:
import json
import asyncio
import websockets
import ssl
import certifi
ssl_context = ssl.create_default_context()
ssl_context.load_verify_locations(certifi.where())
query = {
"jsonrpc": "2.0",
"method": "queryHeadsets",
"params": {},
"id": 1
}
json = json.dumps(query)
async def query(json):
async with websockets.connect("wss://yourserver.com:54321", ssl=ssl_context) as ws:
await ws.send(json)
response = await ws.recv()
print(response)
asyncio.get_event_loop().run_until_complete(query(json))
Upvotes: 3