【问题标题】:Why does my socket connect() fail after WSASetSocketSecurity()?为什么我的套接字 connect() 在 WSASetSocketSecurity() 之后失败?
【发布时间】:2018-02-17 15:43:55
【问题描述】:

我正在教一门课,我想向学生展示如何在 C++ 中打开套接字并在 Linux 和 Windows 下阅读 http: 和 https: 网页。使用OpenSSL,Linux 版本变得轻而易举。但是在使用微软的 WSA 套接字库的 Windows 下,我只能读取非 ssl 页面。我不知道如何让他们的WSASetSocketSecurity() 函数工作。

通过注释掉以下代码片段中的 WSASetSocketSecurity 调用,我可以读取 http:(端口 80)页面。我可以 connect() 到 https: (端口 443) 主机,但尝试发送 HTML GET 请求,然后 recv() 页面按预期失败,没有任何返回或来自某些服务器的 400 错误请求页面,因为我没有'不协商加密。

取消注释 WSASetSocketSecurity 调用以保证加密,连接调用在 http: 和 https: 页面上总是失败并显示 WSAGetLastError = 10060(连接超时)。

调用 WSASetSocketSecurity 但指定允许不安全的连接允许我读取 http: 页面但由于 https: 页面失败,就像根本没有调用 WSASetSocketSecurity 一样。

基本上,我无法打开加密然后连接,我不知道为什么。

我已经尝试过用 WSAxxx() 版本替换套接字、连接和其他调用的实验,想象可能会有类似于在 Linux 中连接之后执行 SSL_connect 调用的方式的区别,但这没有区别。我能想到的唯一尚未尝试的事情是使用 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( );          

【问题讨论】:

  • 我的建议是在 Windows 上安装和使用 OpenSSL,就像在 Linux 上一样。 Microsoft 的实现称为 schannel,但坦率地说,使用起来相当痛苦,文档也可以提供一些重要的帮助。如果您坚持要深入了解血腥细节,请联系article (with code) on CodeProject that shows both a client and a server。不过,这里的答案远远超出了答案。
  • @JerryCoffin 谢谢你,杰瑞,这很有帮助。我真的很惊讶和震惊,微软没有提供一种简单的方法来获得类似于 OpenSSL 提供的 SSL。

标签: c++ sockets windows-socket-api


【解决方案1】:

您对套接字安全性启用 SSL / TLS 的假设是错误的。它实际上是为了强制使用 IPsec 协议。见Winsock Secure Socket Extensions。如果您需要 SSL/TLS,那么您应该使用Secure Channel(内置 Windows SSL/TLS 库)、OpenSSL 或其他在套接字之上工作的专用库。

【讨论】:

  • 好的,我知道我做错了什么。我试过 SOCKET_SECURITY_PROTOCOL_IPSEC 没有成功。我到底做错了什么,需要什么改变来解决它?
  • 好的,但这只是指向有关 Microsoft 安全通道的一般信息页面的链接。它甚至不是我需要使用的函数调用或一组调用的链接。是的,我知道我可以使用 OpenSSL;这就是我对我的 LinuxVersion 所做的。但我试图使用原生 Windows 函数在 Windows 上进行这项工作,只是为了演示这两个操作系统的比较。
  • @NicoleHamilton 该 MSDN 部分链接到 CryptoAPI reference。有一些使用它的例子,见Window C/C++ Crypto API Examples and tips
  • SChannel 依赖于 SSPI。请参阅 MSDN 上的 Using SSPICreating a Secure Connection Using Schannel
  • 好的,我现在明白你的意思了。谢谢你。这很有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多