[Twisted-Python] Re: SSL: Getting the client certificate

David Bolen db3l.net at gmail.com
Mon Aug 27 13:00:19 MDT 2007


Dirk Loss <lists at dirk-loss.de> writes:

> Jean-Paul Calderone wrote:
(...)
>> Another possible solution might be to do your verification using the
>> SSL context object.  
>
> Could you elaborate on this? I think I am already using the SSL
> context object to do the verification:

Not sure if it helps, but here's some old code of mine where I
experimented with the echo SSL examples to add symmetric certificate
checking.  Just checked and it seems ok with Python 2.5.1 and Twisted
2.5.0 (pyOpenSSL 0.6).

It uses direct SSL context objects rather than the Twisted
wrapper versions.  To be honest, at the time it was because I was
still feeling my way around the SSL support and found using the direct
context easier, but I believe you do have full access to the
certificate in the context's _verify method.

Returning 0/False from _verify rather than just propagating ok can
reject the handshake.  (Note that _verify can be called multiple times
during the sequence as well as in cases where ok is already 0 I
believe).

There are a bunch of debugging prints still in the code where I was seeing
what sort of stuff was available to the context factory/verification.

-- David


echoserv_ssl.py:
---------------

# Twisted, the Framework of Your Internet
# Copyright (C) 2001 Matthew W. Lefkowitz
# 
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
# 
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

from OpenSSL import SSL, crypto

class ServerContextFactory:
    
    def _verify(self, connection, x509, errnum, errdepth, ok):
        print '_verify (ok=%d):' % ok
        print '  subject:', x509.get_subject()
        print '  issuer:', x509.get_issuer()
        print '  errnum %s, errdepth %d' % (errnum, errdepth)
        return False # ok

    def getContext(self):
        """Create an SSL context.
        
        This is a sample implementation that loads a certificate from a file 
        called 'server.pem'."""
        ctx = SSL.Context(SSL.SSLv23_METHOD)
        ctx.use_certificate_file('server.pem')
        ctx.use_privatekey_file('server.pem')
        print 'Context additions'
        ctx.load_client_ca('ca/all-cas.cert')
        ctx.load_verify_locations('ca/ca.cert')
        ctx.set_verify(SSL.VERIFY_PEER|SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
                       self._verify)
        print 'verify depth:', ctx.get_verify_depth()
        ctx.set_verify_depth(10)
        print 'verify depth:', ctx.get_verify_depth()
        return ctx

import echoserv

class MyProtocol(echoserv.Echo):

    def connectionMade(self):
        print 'connectionMade', self.transport.getPeerCertificate()
        return echoserv.Echo.connectionMade(self)

    def dataReceived(self, data):
        print 'dataReceived', self.transport.getPeerCertificate()
        return echoserv.Echo.dataReceived(self, data)


if __name__ == '__main__':
    import echoserv, sys
    from twisted.internet.protocol import Factory
    from twisted.internet import ssl, reactor
    from twisted.python import log
    log.startLogging(sys.stdout)
    factory = Factory()
    factory.protocol = MyProtocol
    reactor.listenSSL(9000, factory, ServerContextFactory())
    reactor.run()


echoclient_ssl.py:
-----------------

# Twisted, the Framework of Your Internet
# Copyright (C) 2001 Matthew W. Lefkowitz
# 
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
# 
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

from OpenSSL import SSL
import sys

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

import inspect


class ClientContextFactory(ssl.ClientContextFactory):

    def _verify(self, connection, x509, errnum, errdepth, ok):
        print '_verify (ok=%d):' % ok
        print '  subject:', x509.get_subject()
        print '  issuer:', x509.get_issuer()
        print '  errnum %s, errdepth %d' % (errnum, errdepth)
        return ok

    def getContext(self):
        ctx = ssl.ClientContextFactory.getContext(self)
        ctx.use_certificate_file('client.pem')
        ctx.use_privatekey_file('client.pem')

        ctx.load_verify_locations('ca/ca.cert')
        ctx.set_verify(SSL.VERIFY_PEER|SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
                       self._verify)

        return ctx

class EchoClient(LineReceiver):
    end="Bye-bye!"

    def connectionMade(self):
        self.sendLine("Hello, world!")
        self.sendLine("What a fine day it is.")
        self.sendLine(self.end)

    def connectionLost(self, reason):
        print 'connection lost (protocol)'

    def lineReceived(self, line):
        x509 = self.transport.getPeerCertificate()
        methods = [x for x in dir(x509)
                   if callable(getattr(x509,x)) and
                   not (x.startswith('set_') or
                        x.startswith('add_') or
                        x.startswith('gmtime_') or
                        x in ('sign','digest'))]
        for m in methods:
            print m, getattr(x509,m)()
        print "receive:", line
        if line==self.end:
            self.transport.loseConnection()

class EchoClientFactory(ClientFactory):
    protocol = EchoClient

    def clientConnectionFailed(self, connector, reason):
        print 'connection failed:', reason.getErrorMessage()
        reactor.stop()

    def clientConnectionLost(self, connector, reason):
        print 'connection lost:', reason.getErrorMessage()
        reactor.stop()

def main():
    if len(sys.argv) > 1:
        host = sys.argv[1]
    else:
        host = 'localhost'

    factory = EchoClientFactory()
    reactor.connectSSL(host, 9000, factory, ClientContextFactory())
    reactor.run()

if __name__ == '__main__':
    main()





More information about the Twisted-Python mailing list