Get a self-signed client certificate on the server side

I have a socketserver.TCPServer that uses the ssl module to enforce TLS connections.

I want to use certificates to identify clients, and I want to enable those clients to use self-signed certificates for that purpose.

However, it appears that this isn’t possible with Python’s standard library. To get the client to send a certificate, I must use an ssl.VerifyMode other than ssl.CERT_NONE:

In server mode, no certificate is requested from the client, so the client does not send any for client cert authentication.

But both ssl.CERT_OPTIONAL and ssl.CERT_REQUIRED enforce mandatory verification of any received certificate:

In server mode, a client certificate request is sent to the client. The client may either ignore the request or send a certificate in order perform TLS client cert authentication. If the client chooses to send a certificate, it is verified. Any verification error immediately aborts the TLS handshake.

With server socket, this mode provides mandatory TLS client cert authentication. A client certificate request is sent to the client and the client must provide a valid and trusted certificate.

As the self-signed client certificate can’t be verified by my server (because it’s not one of my server’s loaded CA certificates) the TLS handshake fails.

How can I configure my server to both request a certificate from the client and to not verify that certificate (so that I can get it using ssl.SSLSocket.getpeercert()) either with the standard library or an external package?

I think you may be correct there. To my knowledge, there’s no way to tell Python to accept any self-signed certificate. After all, that doesn’t provide any measure of security, so asking for a client certificate without being able to verify anything is quite useless!

However, if you are in full control of the situation, you should be able to get copies of all the clients’ certificates. You can then identify those certificates as authorities (not as signing authorities, so they can’t sign other keys, but as stand-alone ones), and then they will be accepted as client certificates, since they will then be able to be traced back to a valid authority (via the shortest possible chain - cert is itself recognized).

Assuming that you have multiple such clients, it would probably make the most sense to create a directory in which you deposit all of the certs, and then call load_verify_locations(capath=...) to authorize them all.

Self-signed client certs are a quite unusual situation. I tried doing some things with that a little while back (not in Python but the concepts will be the same), and eventually gave up; it ended up easier to use LetsEncrypt to get actual legit certificates for the clients than to make self-signed certs work properly.

It’s possible to tell Python to accept self-signed certificates on the client side (i.e. from a server); in fact, that’s what I’m doing in my client code, as I plan to use a TOFU authentication scheme on the client side to verify my server’s self-signed certificate, rather than the ssl module’s built-in CA-based verification. This works because the server will always send its certificate, even when ssl.CERT_NONE is used on the client side (which is required in my case to stop the TLS handshake from failing when the client attempts to verify the server’s self-signed certificate). This differs from the server side behaviour of ssl.CERT_NONE, which informs the client that a certificate isn’t required, so it doesn’t send one. The fact that it’s possible to get a self-signed certificate on the client side is why I think that it might also be possible on the server side, but I may be wrong.

I’ve been able to load the client’s certificate as a CA on the server side to enable its verification as you described, but I want to avoid this intermediary step, as there isn’t a nice way of doing it with the network protocol that I’m using (Gemini). I want to enable a client to simply generate a self-signed certificate that my server accepts, without having to first upload it to the server to add it to my server’s CAs.