[Twisted-Python] Implementing STARTTLS in a protocol

Kevin Horn kevin.horn at gmail.com
Fri May 23 13:29:44 EDT 2008


Howdy list,

I'm trying to implement a protocol using Twisted which has a "STARTTLS"
command to switch the protocol from plain TCP to TCP over TLS.

I've mostly been going by the way that the imap4.py module seems to do it,
but I can't seem to get a handshake to complete.

I found this page ( http://wiki.vislab.usyd.edu.au/moin.cgi/SSLCertNotes )
which was helpful, but I don't want to force client cert authentication.

In order to separate this problem from other issues, I've adapted the echo
protocol code from above the above page to try and get a simple test case
(my code below)

I am recieving the following output and traceback when running the client
code ( on both Windows and Linux ):

using TLSv1:

>tls_echoclient.py

Sending: Hello, world!
receive: ERROR: Must authenticate
Sending: STARTTLS
receive: READY
Sending: Continuing
connection lost (protocol)
connection lost: [('SSL routines', 'SSL3_READ_BYTES', 'sslv3 alert handshake
failure'), ('SSL routines', 'SSL3_READ_BYTES', 'ssl handshake failure')]
Traceback (most recent call last):
  File "C:\Documents and
Settings\kevinh\Desktop\mine_id\sandbox\funsize\sslecho\tls_echoclient.py",
line 58, in <module>
    reactor.run()
  File "C:\Python25\lib\site-packages\twisted\internet\posixbase.py", line
223, in run
    self.mainLoop()
  File "C:\Python25\lib\site-packages\twisted\internet\posixbase.py", line
234, in mainLoop
    self.doIteration(t)
  File "C:\Python25\lib\site-packages\twisted\internet\selectreactor.py",
line 140, in doSelect
    _logrun(selectable, _drdw, selectable, method, dict)
--- <exception caught here> ---
  File "C:\Python25\lib\site-packages\twisted\python\log.py", line 51, in
callWithLogger
    return callWithContext({"system": lp}, func, *args, **kw)
<< SNIP >>
  File "C:\Python25\lib\site-packages\twisted\internet\base.py", line 490,
in stop
    "Can't stop reactor that isn't running.")
twisted.internet.error.ReactorNotRunning: Can't stop reactor that isn't
running.

What am I doing wrong?  Is there a SSL config option I'm setting
incorrectly?  Do I need to use a different SSL Context? Am I totally off
base?

Thanks,

Kevin Horn

========= tls_echoserver.py

# adapted from: http://wiki.vislab.usyd.edu.au/moin.cgi/SSLCertNotes

import sys
from OpenSSL import SSL
from twisted.python import log
from twisted.internet import ssl, reactor
from twisted.protocols.basic import LineReceiver
from twisted.internet.protocol import Factory

private_key_file = "privkey_dsa.pem"
cert_file_name = "cacert.pem"
sslmethod = ssl.SSL.TLSv1_METHOD
#~ sslmethod = ssl.SSL.SSLv23_METHOD

class Echo(LineReceiver):
    donetls = 0
    def sendLine(self, line):
        print "Sending:",line
        LineReceiver.sendLine(self,line)

    def lineReceived(self, line):
        print "Got:",line
        if self.donetls:
            print "Sending OK"
            self.transport.write("OK")
        elif line == "STARTTLS":
            self.sendLine("READY")
            #~ self.tlsCtx = CtxFactory()
            self.tlsCtx = ssl.DefaultOpenSSLContextFactory(private_key_file,

                                                cert_file_name,
                                                sslmethod)
            self.transport.startTLS(self.tlsCtx)
            self.donetls = 1
        else:
            self.sendLine("ERROR: Must authenticate")

factory = Factory()
factory.protocol = Echo
reactor.listenTCP(8000, factory)
reactor.run()

========= tls_echoclient.py

# adapted from: http://wiki.vislab.usyd.edu.au/moin.cgi/SSLCertNotes

from OpenSSL import SSL
import sys

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

class EchoClient(LineReceiver):
    end="Bye-bye!"
    def connectionMade(self):
        self.sendLine("Hello, world!")  # Signals error

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

    def sendLine(self, line):
        print "Sending:",line
        LineReceiver.sendLine(self,line)

    def lineReceived(self, line):
        print "receive:", line
        if line == "ERROR: Must authenticate":
            self.sendLine("STARTTLS")
        elif line == "READY":
            #~ self.ctx = CxtFactory()
            self.ctx = ssl.ClientContextFactory()
            self.ctx.method = ssl.SSL.TLSv1_METHOD
            #~ self.ctx.method = ssl.SSL.SSLv23_METHOD
            self.transport.startTLS(self.ctx)
            self.sendLine("Continuing");
        else:
            self.sendLine("wibble")

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()

factory = EchoClientFactory()
reactor.connectTCP('localhost', 8000, factory)
reactor.run()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://twistedmatrix.com/pipermail/twisted-python/attachments/20080523/5d57088c/attachment.htm 


More information about the Twisted-Python mailing list