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.
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.
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.
Regards,
Dirk
[1] http://archives.free.net.ph/message/20070511.203607.36001e38.en.html
[2] http://archives.free.net.ph/message/20070607.211438.9354342f.en.html