Почему мой сокет connect() не работает после WSASetSocketSecurity()?

Я преподаю класс, где я хотел бы показать студентам, как открыть сокет в C++ и читать как http:, так и https: веб-страницы как в Linux, так и в Windows. Версия для Linux была легкой с OpenSSL. Но под Windows, используя библиотеку сокетов Microsoft WSA, я мог читать только страницы без SSL. Я не могу понять, как получить их WSASetSocketSecurity() для работы.

Закомментировав вызов WSASetSocketSecurity в следующем фрагменте кода, я могу читать страницы http: (порт 80). Я могу connect( ) на хосты https: (порт 443), но пытается отправить запрос HTML GET, а затем recv() страница завершается сбоем, как и ожидалось, либо ничего не возвращается, либо страница 400 неверных запросов с некоторых серверов, потому что я не согласовал шифрование.

Если раскомментировать вызов WSASetSocketSecurity, чтобы гарантировать шифрование, вызов подключения всегда завершается с ошибкой WSAGetLastError = 10060 (время ожидания подключения истекло) на страницах http: и https:.

Вызов WSASetSocketSecurity, но указание разрешения небезопасных подключений позволяет мне читать http: страницы, но не работает с https: страницами так же, как если бы WSASetSocketSecurity вообще не вызывался.

Принципиально не могу включить шифрование и потом подключиться и не знаю почему.

Я пробовал экспериментировать с заменой сокета, подключения и других вызовов версиями WSAxxx(), предполагая, что может быть различие, подобное тому, как вы должны выполнять вызов SSL_connect после подключения в Linux, но это не имеет значения. Единственное, что я могу придумать, что я еще не пробовал, - это аутентификация хоста с помощью WSASetSocketPeerTargetName(), но мне не кажется, что мне нужно это делать, если все, что мне нужно, это ссылка SSL.

Что мне здесь не хватает? Кто-нибудь сделал эту работу?

   // Initialize the socket library.
   // wVersionRequested = 2.2 (current latest)

   WSADATA wsaData;
   int wsaStartupResult = WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
   assert( wsaStartupResult == 0 );

   // Get the host address.

   ADDRINFOA *addressInfo;
   int addrInfoResult = getaddrinfo( url.Host, url.Service,
         nullptr, &addressInfo );
   assert( addrInfoResult == 0 );

   sockaddr *socketAddress = addressInfo->ai_addr;
   size_t socketAddressLength = addressInfo->ai_addrlen;
   PrintAddress( socketAddress, socketAddressLength );

   // Create a TCP/IP socket.

   SOCKET s = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
   assert( s != INVALID_SOCKET );

   // Turn on SSL.

   SOCKET_SECURITY_SETTINGS security =
      {
      SOCKET_SECURITY_PROTOCOL_DEFAULT,
      SOCKET_SETTINGS_GUARANTEE_ENCRYPTION
      // SOCKET_SETTINGS_ALLOW_INSECURE
      };
/*
   int setSecurityResult = WSASetSocketSecurity( s,
         &security, sizeof( security ), nullptr, nullptr );
   assert( setSecurityResult == 0 );
*/

   // Connect to the host.

   int connectResult = connect( s, socketAddress, socketAddressLength );

   if ( connectResult != 0 )
      cerr << "Connect failed, WSAGetLastError = " << WSAGetLastError( ) << endl;
   assert( connectResult == 0 );

   // Send a GET message for the desired page.

   string getMessage = "GET ";
   getMessage += url.CompleteUrl;
   getMessage += " HTTP/1.1\r\nHost: ";
   getMessage += url.Host;
   getMessage += "\r\nConnection: close\r\n\r\n";

   cout << getMessage << endl;
   send( s, getMessage.c_str( ), getMessage.length( ), 0 );

   // Read from the socket until there's no more data.

   HANDLE Stdout = GetStdHandle( STD_OUTPUT_HANDLE );
   char buffer[ 10240 ];
   int bytes;
   DWORD length;

   while ( ( bytes = recv( s, buffer, sizeof( buffer ), 0 ) ) > 0 )
      WriteFile( Stdout, buffer, bytes, &length, nullptr );

   freeaddrinfo( addressInfo );
   closesocket( s );
   WSACleanup( );          

person Nicole Hamilton    schedule 17.02.2018    source источник
comment
Я бы посоветовал установить и использовать OpenSSL для Windows так же, как вы это делаете для Linux. Реализация Microsoft называется schannel, но, честно говоря, ее довольно сложно использовать, и документация также могла бы серьезно помочь. Если вы настаиваете на кровавых подробностях, есть статья (с кодом) на CodeProject, в которой показаны как клиент, так и сервер. Тем не менее, это немного больше, чем то, что подходит для ответа здесь.   -  person Jerry Coffin    schedule 17.02.2018
comment
@JerryCoffin Спасибо, Джерри, это было очень полезно. Я искренне удивлен и потрясен тем, что Microsoft не предоставила простой способ получить SSL, аналогичный тому, что предоставляет OpenSSL.   -  person Nicole Hamilton    schedule 18.02.2018


Ответы (1)


Ваше предположение о том, что безопасность сокетов включает SSL/TLS, неверно. На самом деле это для принудительного использования протокола IPsec. См. расширения Winsock Secure Socket. Если вам нужен SSL/TLS, вы должны использовать Secure Channel (встроенная библиотека Windows SSL/TLS), OpenSSL или другая специализированная библиотека, работающая поверх сокетов.

person user7860670    schedule 17.02.2018
comment
Хорошо, я знаю, что делаю что-то не так. Я безуспешно пробовал SOCKET_SECURITY_PROTOCOL_IPSEC. Что именно я делаю неправильно и какие изменения необходимы, чтобы это исправить? - person Nicole Hamilton; 17.02.2018
comment
Хорошо, но это всего лишь ссылка на страницу с общей информацией о безопасном канале Microsoft. Это даже не ссылка на вызов функции или набор вызовов, которые мне нужно было бы использовать. Да, я понимаю, что могу использовать OpenSSL; это то, что я сделал с моей версией Linux. Но я пытаюсь заставить это работать в Windows, используя собственные функции Windows, просто чтобы продемонстрировать, как сравниваются две ОС. - person Nicole Hamilton; 17.02.2018
comment
@NicoleHamilton Этот раздел MSDN ссылается на Справочник по криптоAPI. Есть несколько примеров его использования, см. Window C/C++ Crypto API Примеры и советы . - person user7860670; 17.02.2018
comment
Хорошо, теперь я понимаю вашу точку зрения. Спасибо. Это было очень полезно. - person Nicole Hamilton; 18.02.2018