[Twisted-Python] SSL: Getting the client certificate

Jean-Paul Calderone exarkun at divmod.com
Sun Aug 26 13:05:40 MDT 2007


On Sun, 26 Aug 2007 20:26:44 +0200, Dirk Loss <lists at dirk-loss.de> wrote:
>Hi,
>
>following Eli Criffields nice example [1] I implemented a small SSL
>server with Twisted. My server should not only verify the client
>certificate, but also check the Common Name (CN) against a whitelist.
>All this should happen before any user data is exchanged.
>
>Verifying the client certificate worked nicely, but I couldn't access
>its contents: transport.getPeerCertificate() always returned 'None'.
>Apparently Eli had the same problem [2].
>
>After some testing with PyOpenSSL now I think I have found a solution:
>
>Before we can get the client certificate, we have to make sure that the
>SSL handshake has taken place. (If it hasn't, there simply is no client
>certificate to deal with yet.) This can be done by calling the
>do_handshake() method of the underlying socket. The SSL handshake takes
>some time so we will have to try several times.

This is basically a bug in Twisted's SSL support.  I forget if there's a
way to fix it using PyOpenSSL or if it's a limitation of the bindings.

>
>Here's an (incomplete) example showing the interesting part:
>- - cut ---
>import OpenSSL
>
>class MyProtocol(Protocol):
>
>     def connectionMade(self):
>
>         # Make sure that SSL handshake has taken place
>         while True:
>             try:
>                 self.transport.socket.do_handshake()
>                 break
>             except OpenSSL.SSL.WantReadError:
>                 pass
>
>         clientCert = self.transport.getPeerCertificate()
>         if clientCert is None:
>             log.msg("No client cert available.")
>         else:
>             subject = clientCert.get_subject()
>             log.msg("Subject: %s" % subject)
>             log.msg("Common Name: %s" % subject.CN)
>- - cut ---
>
>If you see a nicer way to wait for the SSL handshake please let me
>know. Using time.sleep() didn't work for me.

This solution has at least two related problems:

  * it will block the reactor until the handshake for that client completes,
    which means no other I/O will occur and none other application code will
    be able to run.  This might be fine for your application, but in general
    it's not a very good thing.

  * if a malicious client connects, they can just never complete the
    handshake and your server will hang in that loop indefinitely.

>
>Side note:
>Getting the certificate in a dataReceived() instead of connectionMade()
>works without manually doing the handshake. I think this is because the
>underlying PyOpenSSL recv() method handles the handshake for us. But at
>least for my purpose it makes more sense to verify the client cert
>right upon connection, before any user data is exchanged.

The ideal solution would be to fix the bug in Twisted's SSL support so that
connectionMade is called at the right time.  Another possible solution might
be to do your verification using the SSL context object.  CertificateOptions
might give you some ideas about how to do this:

http://twistedmatrix.com/documents/current/api/twisted.internet.ssl.CertificateOptions.html

Jean-Paul




More information about the Twisted-Python mailing list