c++ - SSL_Read() 返回 SSL_ERROR_ZERO_RETURN 但 ERR_get_error() 为 0

标签 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 session 后很快就断开连接?

编辑 1

我使用wireshark来捕获客户端和服务器之间的数据包。我确认 TLS 握手、Websocket 升级和初始服务器响应均成功。我注意到在初始服务器响应后,我的客户端从服务器收到一个Encrypted Alert 21,我认为这是一个 fatal error ,并解释了为什么 TLS session 立即终止并且我的 SSL 错误队列为空(虽然它可能是客户端问题,我不认为这是最近操作的结果),并且有点解释了我在 SSL_Read 之后得到的 SSL_ERROR_ZERO_RETURN 值。

我不确定加密警报 21 意味着什么。这可能是我正在使用的证书(自签名)。需要进一步调查。

最佳答案

好吧,问题的根本原因已经确定,但还是跳过了很多麻烦,并且浪费了时间。我能够通过使用 OpenSSL 方法获取主 key 来解密 SSL 流量,SSL_SESSION_get_master_key() ,以及带有wireshark的Client Hello的随机值。

SSL_Connect后输出主 key 的相关代码:

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
      } 
    }
}

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

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

总而言之,回答我原来的两个问题:

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

服务器端正在终止连接,错误队列将包含客户端的错误,但至少在 SSL/TLS 层上没有任何错误。

  • 为什么我在建立 TLS 和 Websocket session 后很快就断开连接?
  • 我的初始升级请求包含有效的 Websocket 升级请求,其后带有垃圾数据。 Server解析Websocket升级请求并确认升级,并开始发回数据。下次服务器检查其套接字时,它仍然具有与原始 Websocket 升级请求一起发送的垃圾值。由于服务器无法将其识别为有效的 Websocket 消息或任何其他相关消息,因此它决定使用 close_notify 终止连接。

    关于c++ - SSL_Read() 返回 SSL_ERROR_ZERO_RETURN 但 ERR_get_error() 为 0,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50223224/

    相关文章:

    ssl - yajsw 播放 framewok ssl 错误

    安卓 ssl 证书

    ssl - 我从 godaddy 购买了 ssl,但域 url 重定向有问题

    c++ - 使用命名空间中的模板调用模板模板方法时出现 False clang 错误

    c++ - 套接字编程(服务器和客户端在同一台计算机上)连接有问题

    java - 数据未准备好从流中读取

    c - 使用TCP从服务器下载文件

    c++ - 使用 GetDIBits 访问图像的像素颜色

    c++ - 基于条件然后三元运算符获取值的更快方法?

    c++ - 我正在将官方 SQLite 源代码添加到我的 C++ 项目中,但出现链接器错误