SSL_Read()返回SSL_ERROR_ZERO_RETURN但ERR_get_error()为0

时间:2018-05-07 22:14:24

标签: c++ sockets ssl websocket openssl

我正在编写一个非阻塞的Websocket客户端,并使用OpenSSL作为TLS层。我可以连接到远程服务器,完成TLS握手,发送升级请求,获得升级确认响应,然后在TLS层与SSL_ERROR_ZERO_RETURN断开连接之前获得实际的websocket响应。

SSL_get_error(...)返回:6 // SSL_ERROR_ZERO_RETURN

ERR_error_string(ERR_get_error(), nullptr)返回:error:00000000:lib(0):func(0):reason(0)

根据我的理解,ERR_get_error()应弹出并返回错误队列中的第一个错误,SSL_get_error()返回SSL_*函数的最后一个错误。我不明白为什么SSL_get_error()会返回错误值,但ERR_get_error()却没有。根据之前的Stack Overflow QuestionSSL_get_error()不会调用ERR_get_error()

重复调用以下代码(因为它是非阻塞套接字):

ERR_clear_error();
int ret = SSL_read(...);
if (ret > 0) {
  // read bytes from socket
} else {
  int err_code = SSL_get_error(ssl_session_, ret);
  if (err_code == SSL_ERROR_ZERO_RETURN || err_code == SSL_ERROR_SYSCALL || err_code == SSL_ERROR_SSL) {
    sprintf("Disconnected: %d %s", err_code, ERR_error_string(ERR_get_error(), nullptr));
    // Disconnect Code
  }
}

我有两个问题:

  1. 为什么我没有收到ERR_get_error()的错误值?

  2. 为什么我在建立TLS和Websocket会话后如此迅速地断开连接?

  3. 编辑1

    我使用wireshark捕获客户端和服务器之间的数据包。我确认TLS握手,websocket升级和初始服务器响应都是成功的。我注意到在初始服务器响应之后,我的客户端从服务器获得Encrypted Alert 21,我认为这是一个致命的错误,并解释了为什么TLS会话立即终止并且我的SSL错误队列为空(虽然它可能是客户端)问题,我不认为这是最近行动的结果),并且解释了我在SSL_ERROR_ZERO_RETURN之后获得的SSL_Read价值。

    我不确定Encrypted Alert 21需要什么。它可能是我使用的证书(自签名)。需要进一步调查。

1 个答案:

答案 0 :(得分:-1)

好吧,这个问题的根本原因已经确定,但很多箍都被跳了出来,浪费了时间。我能够通过使用OpenSSL方法SSL_SESSION_get_master_key()获取主密钥以及使用wireshark的客户端Hello随机值来解密SSL流量。

SSL_Connect后输出主密钥的相关代码:

ERR_clear_error();
int ret = SSL_connect(ssl_ptr);
if (ret > 0) {
    SSL_SESSION * ssl_session = SSL_get_session(ssl_ptr);
    if(ssl_session != NULL) {
      unsigned char master_key_buf[256];
      size_t outlen = sizeof(master_key_buf);
      size_t buf_size = SSL_SESSION_get_master_key(ssl_session, master_key_buf, outlen);
      if(outlen > 0) {
        char hex_encoded_master_buf[513];
        // hex encode the master key
        for(size_t i = 0; i < buf_size; ++i) {
          sprintf(&hex_encoded_master_buf[2*i], "%02x", master_key_buf[i]);
        }
        hex_encoded_master_buf[(2*buf_size)] = '\0';
        // log out the hex-encoded master key in master buf here
      } 
    }
}

使用wireshark中的NSS Key Log CLIENT_RANDOM格式来解密捕获的SSL流量,我能够检查前面提到的Encrypted Alert 21,它最终只是一个WebSocket FIN和close_notify。

事实证明,根本原因是在握手期间,我的WSS升级请求消息确实包含了正确的标头,但我实际上是在发送垃圾有效负载。这是在发送消息时使用消息缓冲区的sizeof而不是strlen来设置大小的情况。服务器可以很好地解析升级消息并成功完成握手,但下次检查其套接字时,它会在预期WSS消息时读取垃圾。这导致了websocket连接的突然关闭。

在摘要中,回答我原来的两个问题:

  1. 为什么我没有收到ERR_get_error()的错误值?
  2. 连接正在服务器端终止,并且错误队列将包含客户端上的错误,至少在SSL / TLS层上没有错误。

    1. 为什么我在建立TLS和Websocket会话后如此迅速地断开连接?
    2. 我的初始升级请求包含一个有效的Websocket升级请求,其中包含垃圾数据。服务器解析了Websocket升级请求并确认了升级,并开始发回数据。下次服务器检查其套接字时,它仍然具有与原始Websocket升级请求一起发送的垃圾值。由于服务器无法将其识别为有效的Websocket消息或其他任何内容,因此它决定使用close_notify终止连接。