Search code examples
pythontwisted

How to use startTLS in Twisted?


I want to establish a non-encrypted connection to a server and then, upon sending an agreed-upon command, to switch to a TLS-encrpted connection. It seems that Twisted's startTLS is meant exactly for this purpose - except I can't make it to work, not even the very examples given in Twisted's official documentation.

Obviously, these examples depend on an external file, called server.pem. Higher on that page of the documentation, it says that this file is "private key and self-signed certificate together". So, I used OpenSSL to create it:

openssl req -x509 -newkey rsa:4096 -keyout serverkey.pem -out servercert.pem -sha256 -days 3650 -nodes -subj "/CN=localhost"
copy serverkey.pem+servercert.pem server.pem

If I now run the server example, starttls_server.py, it works fine - but if I run the client example, starttls_client.py, it crashes with a stack trace and the error

service_identity.exceptions.CertificateError: Certificate does not contain any `subjectAltName`s.

I have no idea what this means (and googling didn't help) but it seems strange to me that both the client and the server are using the same server.pem file. Although I can't see anything theoretically wrong with the idea, in practice it would virtually impossible for both the client and the server to have the same private key, so maybe pyOpenSSL contains some kind of check to report such an oddity as an error.

So, I created yet another private key and self-signed certificate, stored them in a file named client.pem

openssl req -x509 -newkey rsa:4096 -keyout clientkey.pem -out clientcert.pem -sha256 -days 3650 -nodes -subj "/CN=localhost"
copy clientkey.pem+clientcert.pem client.pem

and modified the file starttls_client.py to use client.pem instead of server.pem.

The result sort of works, in the sense that the client doesn't crash any more - but it doesn't really work, because it doesn't do what it is supposed to do.

Looking at the code, the server is supposed to print every line sent by the client and, if this line is STARTTLS, to print -- Switching to TLS, send the line READY to the client, and call startTLS.

The client, upon establishing a connection to the server, is supposed to send the line plain text, then STARTTLS. If it receives the line READY from the server, it is supposed to call startTLS, then send the line secure text to the server.

There is what actually happens:

The server prints

received:  b'plain text'
received:  b'STARTTLS'
-- Switching to TLS

meaning that it did receive the plantext line from the client, then the command to switch to TLS-encrypted connection, and has called startTLS.

The client prints only

received:  b'READY'

and exits. That is, at this point it has established a non-encrypted communication with the server, has send the command to switch to TLS-encrypted communication, and has received the (plaintext) acknowledgement from the server. At this point, the communication is supposed to be TLS-encrypted, so the client has, pesumably, sent the line secure text to the server and exits.

Except the server never prints secure text, so clearly it has not received this line.

I am stumped at this point. I was expecting at least the examples from the Twisted documentation to work "as is" but, apparently, this is not the case. Any ideas what might be wrong? Is there some problem with my generation of the private keys and self-signed certificates? The PEM files are the only thing I have created myself instead of using literally what is in Twisted's documentation.


Solution

  • service_identity.exceptions.CertificateError: Certificate does not contain any subjectAltNames.

    subjectAltName is an X509v3 extension that's used with TLS. It supersedes the original "Common Name" field that was used in X509. "Common name" checking was deprecated by RFC 2818 (in 2000) and, eg, browsers stopped supporting "common name" 5 to 10 years ago.

    Unfortunately the OpenSSL command line will still happily create a certificate using a Common Name instead of a subjectAltName and that's what the command you used to make a certificate did.

    How can I generate a self-signed certificate with SubjectAltName using OpenSSL? covers this topic and has an answer which contains instructions for generating a certificate with a subjectAltName field.

    Note that the problem is only related to the certificate. It's not related to startTLS in particular.