为什么在使用此代码尝试通过TLS运行TLS时出现握手失败?

我尝试使用twisted.protocols.tls实现一个可以通过TLS运行TLS的协议,这是一个使用内存BIO的OpenSSL接口。

我把它作为一个协议包装器实现,它看起来像一个普通的TCP传输,但是它有startTLSstopTLS方法来分别添加和删除一层TLS。 这对TLS的第一层工作正常。 如果我通过“原生”扭曲TLS传输运行它也工作正常。 但是,如果尝试使用此包装提供的startTLS方法添加第二个TLS层, startTLS立即发生握手错误,并且连接以某种未知的不可用状态结束。

包装器和让它工作的两个助手看起来像这样:

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

我有简单的客户端和服务器程序来执行此操作,可从launchpad( bzr branch lp:~exarkun/+junk/onion )中获得。 当我使用它来调用上面的startTLS方法两次,没有stopTLS呼叫stopTLS ,这个OpenSSL错误出现:

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

为什么事情出错?

如果该设备具有该function,则可能需要在启动之前通知远程设备您希望启动环境并为第二层分配资源。

如果您对两个图层使用相同的TLS参数,并且您正在连接到相同的主机,那么您可能使用相同的密钥对来进行两层encryption。 尝试为嵌套层使用不同的密钥对,例如隧道到第三个主机/端口。 即localhost:30000 (client) – > localhost:8080 (使用密钥对A的TLS层1) – > localhost:8081 (使用密钥对B的TLS层2)。