Как проверить завершение рукопожатия TLS в Twisted

Это продолжение этого вопроса: сбои рукопожатия SSL, когда данные не были отправлены через Twisted TLSConnection

Я реализовал простой SSL-сервер, который закрывает соединение, как только клиент подключается.

Я тестирую его с помощью openssl, и я получил этот сбой рукопожатия:

$ openssl s_client -connect localhost:12345                             
CONNECTED(00000003) 2329:error:140790E5:SSL routines:SSL23_WRITE
:ssl handshake failure:s23_lib.c:188: 

Проблема в том, что TLS.Connection.loseConnection не ждет выполнения текущего рукопожатия, а просто отключает клиента.

Обратный вызов, прикрепленный к OpenSSL.SSL.Connection.do_handshake, был бы замечательным... но, к сожалению, я не знаю, можно ли это сделать... или как это сделать.

Любые подсказки о том, как я могу проверить, было ли выполнено рукопожатие TLS, очень ценятся. Большое спасибо!

Вот код

class ApplicationProtocol(Protocol):
        '''Protocol that closes the connection when connection is made.'''
        def connectionMade(self):
            self.transport.loseConnection()

# Here is a barebone TLS Server
serverFactory = ServerFactory()
serverFactory.protocol = ApplicationProtocol
server_cert_path = 'server.pem'
serverContextFactory = DefaultOpenSSLContextFactory(
            privateKeyFileName = server_cert_path,
            certificateFileName = server_cert_path,
            sslmethod=SSL.SSLv23_METHOD)

tlsFactory = TLSMemoryBIOFactory(serverContextFactory, False, serverFactory)
reactor.listenTCP(12345, tlsFactory)
#reactor.listenSSL(12345, serverFactory, serverContextFactory)

На данный момент я решаю это очень грязно и не на 100% действительно.

def tls_lose_connection(self):
    """
    Monkey patching for TLSMemoryBIOProtocol to wait for handshake to end,
    before closing the connection.

    Send a TLS close alert and close the underlying connection.
    """

    def close_connection():
        self.disconnecting = True
        if not self._writeBlockedOnRead:
            self._tlsConnection.shutdown()
            self._flushSendBIO()
            self.transport.loseConnection()

    # If we don't know if the handshake was done, we wait for a bit
    # and the close the connection.
    # This is done to avoid closing the connection in the middle of a
    # handshake.
    if not self._handshakeDone:
        reactor.callLater(0.5, close_connection)
    else:
        close_connection()


TLSMemoryBIOProtocol.loseConnection = tls_lose_connection

person Adi Roiban    schedule 10.02.2011    source источник


Ответы (3)


Объект контекста SSL можно настроить с помощью «информационного обратного вызова» — Context.set_info_callback. Это оболочка для SSL_CTX_set_info_callback. Немного более удобный (в данном случае) SSL_set_info_callback для указания обратного вызова для одного соединения, к сожалению, не предоставляется pyOpenSSL.

Помимо прочего, информационный обратный вызов вызывается после завершения рукопожатия. С помощью нескольких акробатических трюков вы сможете превратить это уведомление в отложенный или какой-либо другой обратный вызов в протоколе.

См. документацию pyOpenSSL set_info_callback и документация по OpenSSL SSL_CTX_set_info_callback для получения подробной информации.

person Jean-Paul Calderone    schedule 10.02.2011
comment
Огромное спасибо :). Из документации pyOpenSSL видно, что Context.set_info_callback передает объект Connection обратному вызову... Думаю, я могу подключить этот обратный вызов, чтобы установить флаги handshake_in_progress и handshake_done в соединении. - person Adi Roiban; 10.02.2011
comment
@ Ади, ты нашел способ сделать это? Я проверил, что info_callback дает вам экземпляр OpenSSL.SSL.Connection, и вы можете проверить флаги where, чтобы определить, что рукопожатие было завершено, но я не вижу никакого готового способа получить экземпляр Twisted Protocol из экземпляра Connection. - person Von; 25.08.2011
comment
@Von Я добавил свое текущее грязное решение. Я попытался просмотреть значения set_info_callback, но не смог определить правильное значение, чтобы увидеть, было ли выполнено рукопожатие. - person Adi Roiban; 29.08.2011
comment
Из ‹openssl.org/docs/ssl/SSL_CTX_set_info_callback.html›: < i>SSL_CB_HANDSHAKE_DONE 0x20. Это помогает? - person Jean-Paul Calderone; 29.08.2011
comment
@Ади - Спасибо. Действителен, но не помогает в том, что я пытаюсь сделать, а именно получить одноранговый сертификат без фактической проверки OpenSSL. Я напишу то, что узнал, в другом ответе. - person Von; 11.09.2011
comment
В какой-то момент в будущем Twisted должен будет поддерживать такой обратный вызов. Билет (к сожалению, задержался на большую часть года!) здесь: twistedmatrix.com/trac /ticket/6024, который в какой-то момент должен быть закрыт. - person Glyph; 23.02.2015

Я предоставляю код, реализующий ответ Жан-Поля.

class ProxyClientTLSContextFactory(ssl.ClientContextFactory):
    isClient = 1

def getContext(self):
    ctx = SSL.Context(SSL.TLSv1_METHOD)
    logger = logging.GetLogger()
    def infoCallback(conn, where, ret):
        # conn is a OpenSSL.SSL.Connection
        # where is a set of flags telling where in the handshake we are
        # See http://www.openssl.org/docs/ssl/SSL_CTX_set_info_callback.html
        logger.debug("infoCallback %s %d %d" % (conn, where, ret))
        if where & SSL.SSL_CB_HANDSHAKE_START:
            logger.debug("Handshake started")
        if where & SSL.SSL_CB_HANDSHAKE_DONE:
            logger.debug("Handshake done")
    ctx.set_info_callback(infoCallback)
    return ctx

Проблема, с которой я столкнулся внутри infoCallback(), заключается в том, что я понятия не имею, как вернуться из SSL.Connection обратно в связанный экземпляр Twisted Protocol.

Что я хотел бы сделать, так это вызвать обратный вызов в моем экземпляре протокола после того, как соединение было установлено и рукопожатие TLS было завершено, чтобы я мог быть уверен, что проверка сертификата мне нравится, прежде чем продолжить.

person Von    schedule 11.09.2011
comment
Вы могли бы сделать: ctx.set_info_callback(lambda conn,where,ret: self.infoCallback(conn,where,ret)) и тогда ваш infoCallback является истинным методом для объекта self и затем может использовать его по своему усмотрению. - person Patrick Mevzek; 04.01.2018

Я нашел использование loseConnection() ненадежным из-за проблемы с подтверждением связи. На него можно позвонить, и соединение никогда полностью не отключается. Поэтому для TLS я всегда использую abortConnection(). Это гарантирует, что соединение закрыто независимо от состояния рукопожатия.

person Tim Tisdall    schedule 27.03.2015