66 votes

Pourquoi y a-t-il un échec de la négociation lorsque vous essayez d'exécuter TLS sur TLS avec ce code?

J'ai essayé de mettre en œuvre un protocole qui peut s'exécuter sur TLS TLS utilisant twisted.protocols.tls, une interface pour OpenSSL l'aide d'une mémoire BIO.

J'ai mis en œuvre ce protocole wrapper qui la plupart du temps ressemble à un régulière de transport TCP, mais qui a startTLS et stopTLS méthodes pour l'ajout et la suppression d'un calque de TLS, respectivement. Cela fonctionne très bien pour la première couche de TLS. Il fonctionne aussi très bien si je le lance sur un "natif" Tordu TLS transport. Cependant, si j'essaie d'ajouter une deuxième couche TLS à l'aide de l' startTLS méthode prévue par le présent wrapper, il y a d'emblée une poignée de main d'erreur et la connexion se termine dans quelques inconnus inutilisables en l'état.

L'enveloppe et les deux assistants qui permettent ce travail ressemble à ceci:

from twisted.python.components import proxyForInterface
from twisted.internet.error import ConnectionDone
from twisted.internet.interfaces import ITCPTransport, IProtocol
from twisted.protocols.tls import TLSMemoryBIOFactory, TLSMemoryBIOProtocol
from twisted.protocols.policies import ProtocolWrapper, WrappingFactory

class TransportWithoutDisconnection(proxyForInterface(ITCPTransport)):
    """
    A proxy for a normal transport that disables actually closing the connection.
    This is necessary so that when TLSMemoryBIOProtocol notices the SSL EOF it
    doesn't actually close the underlying connection.

    All methods except loseConnection are proxied directly to the real transport.
    """
    def loseConnection(self):
        pass


class ProtocolWithoutConnectionLost(proxyForInterface(IProtocol)):
    """
    A proxy for a normal protocol which captures clean connection shutdown
    notification and sends it to the TLS stacking code instead of the protocol.
    When TLS is shutdown cleanly, this notification will arrive.  Instead of telling
    the protocol that the entire connection is gone, the notification is used to
    unstack the TLS code in OnionProtocol and hidden from the wrapped protocol.  Any
    other kind of connection shutdown (SSL handshake error, network hiccups, etc) are
    treated as real problems and propagated to the wrapped protocol.
    """
    def connectionLost(self, reason):
        if reason.check(ConnectionDone):
            self.onion._stopped()
        else:
            super(ProtocolWithoutConnectionLost, self).connectionLost(reason)


class OnionProtocol(ProtocolWrapper):
    """
    OnionProtocol is both a transport and a protocol.  As a protocol, it can run over
    any other ITransport.  As a transport, it implements stackable TLS.  That is,
    whatever application traffic is generated by the protocol running on top of
    OnionProtocol can be encapsulated in a TLS conversation.  Or, that TLS conversation
    can be encapsulated in another TLS conversation.  Or **that** TLS conversation can
    be encapsulated in yet *another* TLS conversation.

    Each layer of TLS can use different connection parameters, such as keys, ciphers,
    certificate requirements, etc.  At the remote end of this connection, each has to
    be decrypted separately, starting at the outermost and working in.  OnionProtocol
    can do this itself, of course, just as it can encrypt each layer starting with the
    innermost.
    """
    def makeConnection(self, transport):
        self._tlsStack = []
        ProtocolWrapper.makeConnection(self, transport)


    def startTLS(self, contextFactory, client, bytes=None):
        """
        Add a layer of TLS, with SSL parameters defined by the given contextFactory.

        If *client* is True, this side of the connection will be an SSL client.
        Otherwise it will be an SSL server.

        If extra bytes which may be (or almost certainly are) part of the SSL handshake
        were received by the protocol running on top of OnionProtocol, they must be
        passed here as the **bytes** parameter.
        """
        # First, create a wrapper around the application-level protocol
        # (wrappedProtocol) which can catch connectionLost and tell this OnionProtocol 
        # about it.  This is necessary to pop from _tlsStack when the outermost TLS
        # layer stops.
        connLost = ProtocolWithoutConnectionLost(self.wrappedProtocol)
        connLost.onion = self
        # Construct a new TLS layer, delivering events and application data to the
        # wrapper just created.
        tlsProtocol = TLSMemoryBIOProtocol(None, connLost, False)
        tlsProtocol.factory = TLSMemoryBIOFactory(contextFactory, client, None)

        # Push the previous transport and protocol onto the stack so they can be
        # retrieved when this new TLS layer stops.
        self._tlsStack.append((self.transport, self.wrappedProtocol))

        # Create a transport for the new TLS layer to talk to.  This is a passthrough
        # to the OnionProtocol's current transport, except for capturing loseConnection
        # to avoid really closing the underlying connection.
        transport = TransportWithoutDisconnection(self.transport)

        # Make the new TLS layer the current protocol and transport.
        self.wrappedProtocol = self.transport = tlsProtocol

        # And connect the new TLS layer to the previous outermost transport.
        self.transport.makeConnection(transport)

        # If the application accidentally got some bytes from the TLS handshake, deliver
        # them to the new TLS layer.
        if bytes is not None:
            self.wrappedProtocol.dataReceived(bytes)


    def stopTLS(self):
        """
        Remove a layer of TLS.
        """
        # Just tell the current TLS layer to shut down.  When it has done so, we'll get
        # notification in *_stopped*.
        self.transport.loseConnection()


    def _stopped(self):
        # A TLS layer has completely shut down.  Throw it away and move back to the
        # TLS layer it was wrapping (or possibly back to the original non-TLS
        # transport).
        self.transport, self.wrappedProtocol = self._tlsStack.pop()

J'ai de simples programmes client et serveur pour l'exercice de cette, disponible à partir de launchpad (bzr branch lp:~exarkun/+junk/onion). Lorsque je l'utilise pour appeler l' startTLS méthode ci-dessus deux fois, sans le moindre appel à l' stopTLS, ce OpenSSL erreur apparaît:

OpenSSL.SSL.Error: [('SSL routines', 'SSL23_GET_SERVER_HELLO', 'unknown protocol')]

Pourquoi les choses vont mal?

1voto

developer Points 23

Vous devrez peut-être informer le périphérique distant que vous souhaitez démarrer un environnement et allouer des ressources pour la deuxième couche avant de le démarrer, si ce périphérique dispose des fonctionnalités correspondantes.

0voto

Leo Accend Points 222

Si vous utilisez les mêmes paramètres TLS pour les deux couches et que vous vous connectez au même hôte, vous utilisez probablement la même paire de clés pour les deux couches de cryptage. Essayez d'utiliser une paire de clés différente pour la couche imbriquée, telle que la mise en tunnel vers un troisième hôte / port. c'est-à-dire: localhost:30000 (client) -> localhost:8080 (couche TLS 1 utilisant la paire de clés A) -> localhost:8081 (couche TLS 2 utilisant la paire de clés B) .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by: