Boost ASIO: SSL-квитирование() никогда не заканчивается
У меня есть клиентское приложение на С++, которое использует Boost ASIO для подключения SSL к различным серверам. Но против 2 конкретных серверов соединение SSL не может быть установлено. Он зависает при вызове boost::asio::ssl::stream::handshake()
.
Я использовал Wireshark для наблюдения за разговором между клиентом и сервером. Кажется, что работает SSL-соединение:
sslsocket.lowest_layer().connect( endpoint, ec );
C -> SYN -> S
C <- SYN ACK <- S
C -> ACK -> S
sslsocket.handshake( SSLSocket::client, ec );
C -> 209 bytes -> S
C <- 690 bytes <- S
C -> 198 bytes -> S
C <- 415 bytes <- S
... и на этот момент вызов ASIO handshake()
возвращает, показывая, что все хорошо, и соединение сокета SSL работает нормально.
Но против двух разных серверов [*] рукопожатие выглядит так:
sslsocket.lowest_layer().connect( endpoint, ec );
C -> SYN -> S
C <- SYN ACK <- S
C -> ACK -> S
sslsocket.handshake( SSLSocket::client, ec );
C -> 209 bytes -> S
...2 minute pause...
C <- RST <- S
Рассматривая файлы журналов на этих серверах, кажется, что после того, как начальные 209 байтов отправлены в рукопожатии, сервер считает, что соединение SSL полностью установлено. Но клиент все еще сидит в вызове Boost ASIO handshake() и в итоге возвращает ec = 104, когда соединение reset.
Итак, я думаю, возможно, существуют разные типы рукопожатий SSL, и, может быть, есть "более простой", который я должен использовать?
[*] Я знаю, что кто-то захочет узнать: один из серверов, вызывающих эту проблему с клиентским приложением, - это установка FileZilla Server для Windows, использующая SSL/TLS [FTPS], а другая - проприетарная служба, работающая на Linux.)
ОБНОВЛЕНИЕ: Сэм Миллер попросил, чтобы я опубликовал свой код, описывающий, как настраивается контекст ssl:
Класс (файл .hpp) содержит следующее:
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SSLSocket;
boost::asio::io_service ioservice;
boost::asio::ssl::context sslcontext;
SSLSocket sslDataSocket;
boost::system::error_code ec;
Конструктор имеет эти инициализаторы:
ioservice ( 2 ),
sslcontext ( ioservice, boost::asio::ssl::context::sslv23 ),
sslDataSocket ( ioservice, sslcontext ),
... и этот код:
sslcontext.set_options( boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::verify_none );
И это код, в котором установлен SSL-сокет, и рукопожатие зависает:
std::cout << "connecting SSL socket to endpoint " << buffer << std::endl;
sslDataSocket.lowest_layer().connect( tcpEndpoint, ec );
std::cout << "connect() done, ec=" << ec.value() << std::endl;
if ( ec ) throw "test 1";
std::cout << "starting ssl handshake" << std::endl;
sslDataSocket.handshake( SSLSocket::client, ec );
std::cout << "handshake done, ec=" << ec.value() << std::endl;
if ( ec ) throw "test 2";
Ответы
Ответ 1
Я понял это. В этом учебнике по SSL (http://h71000.www7.hp.com/doc/83final/ba554_90007/ch04s03.html) содержится ключ, который, наконец, получил это для меня. Цитата:
Вы можете повторно использовать информацию из уже установленной сессии SSL для создания нового SSL-соединения. Поскольку новое соединение SSL повторное использование одного и того же главного секрета, может быть выполнено SSL-квитирование быстрее. В результате возобновление сеанса SSL может снизить нагрузку сервера, который принимает множество SSL-соединений.
Итак, вот как я работал с Boost ASIO:
- установить нормальный сокет управления SSL (много примеров, включая этот вопрос)
- когда вам нужно установить второй сокет данных SSL, сделайте следующее:
sslSocket2.lowest_layer().connect( tcpEndpoint, ec );
SSLSocket::impl_type impl1 = sslSocket1.impl();
SSLSocket::impl_type impl2 = sslSocket2.impl();
SSL_SESSION *savedSession = SSL_get1_session( impl1->ssl );
SSL_set_session( impl2->ssl, savedSession );
SSL_connect( impl2->ssl );
Что это. На данный момент не нужно называть sslSocket2.handshake(). Просто прочитайте и напишите в сокет, зная, что соединение установлено.