when are client certificates verified?

Asked by Jessica McKellar on 2009-08-04

I have a server that requires client authentication and a client that has certs to authenticate itself. They appear to work - the server doesn't accept the client connection when the client doesn't have certs and does accept the client connection when the client does have certs. They also don't cause errors when paired with s_client and s_server. I'm passing set_verify SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT.

However, the server context's set_verify callback only gets called when verification is successful, and I'm wondering why. If I pass set_verify only SSL.VERIFY, the callback is never called. Does verification also happen at some earlier time? If that's the case, what is the purpose of the callback? Is my code just wrong?

The code uses Twisted, but the context is set up using pyOpenSSL calls. I've included the complete code in case it helps, and hopefully the surrounding Twisted code isn't a deterrent (also Jean-Paul I see you're the maintainer here :) ).

The server and client code are also here if that's easier:
http://mit.edu/jesstess/www/pyopenssl/

Thank you,
Jessica

==== Server ====

from twisted.internet import ssl, reactor
from twisted.internet.protocol import Factory, Protocol
from OpenSSL import SSL

class Echo(Protocol):
    def dataReceived(self, data):
        self.transport.write(data)

def verify_callback(connection, x509, errnum, errdepth, ok):
    if not ok:
        print "Bad Certs"
        return False
    else:
        print "Certs are fine"
    return ok

if __name__ == '__main__':
    factory = Factory()
    factory.protocol = Echo

    myContextFactory = ssl.DefaultOpenSSLContextFactory(
        'keys/server.key', 'keys/server.crt'
        )

    ctx = myContextFactory.getContext()

    ctx.set_verify(
        SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
        verify_callback
        )

    ctx.load_verify_locations("keys/ca.pem")

    reactor.listenSSL(8000, factory, myContextFactory)
    reactor.run()

==== Client ====

from twisted.internet.protocol import ClientFactory, Protocol
from twisted.protocols.basic import LineReceiver
from twisted.internet import ssl, reactor
from OpenSSL import SSL

class EchoClient(Protocol):
    def connectionMade(self):
        print "hello, world"
        self.transport.write("hello, world!")

    def dataReceived(self, data):
        print "Server said:", data
        self.transport.loseConnection()

class EchoClientFactory(ClientFactory):
    protocol = EchoClient

    def clientConnectionFailed(self, connector, reason):
        print "Connection failed - goodbye!"
        reactor.stop()

    def clientConnectionLost(self, connector, reason):
        print "Connection lost - goodbye!"
        reactor.stop()

class CtxFactory(ssl.ClientContextFactory):
    def getContext(self):
        self.method = SSL.SSLv23_METHOD
        ctx = ssl.ClientContextFactory.getContext(self)
        ctx.use_certificate_file('keys/client.crt')
        ctx.use_privatekey_file('keys/client.key')

        return ctx

if __name__ == '__main__':
    factory = EchoClientFactory()
    reactor.connectSSL('localhost', 8000, factory, CtxFactory())
    reactor.run()

Question information

Language:
English Edit question
Status:
Expired
For:
pyOpenSSL Edit question
Assignee:
No assignee Edit question
Last query:
2009-08-04
Last reply:
2009-08-20
Launchpad Janitor (janitor) said : #1

This question was expired because it remained in the 'Open' state without activity for the last 15 days.