You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Boost Beast WebSocket连接后定期重新验证客户端证书?

问题:基于Boost Beast Asio的WebSocket服务器定期验证客户端证书

我有一个基于Boost Beast Asio的WebSocket服务器,其初始化代码(简化版)如下:

ssl_context_.set_options(
    boost::asio::ssl::context::default_workarounds |
    boost::asio::ssl::context::no_sslv2            |
    boost::asio::ssl::context::no_sslv3            |
    boost::asio::ssl::context::no_tlsv1            |
    boost::asio::ssl::context::no_tlsv1_1
    );

ssl_context_.use_certificate_file(
    auth->server_cert,
    boost::asio::ssl::context::file_format::pem
    );

ssl_context_.use_private_key_file(
    auth->server_key,
    boost::asio::ssl::context::file_format::pem
    );

ssl_context_.load_verify_file(auth->ca);

ssl_context_.set_verify_mode(
    boost::asio::ssl::verify_peer |
    boost::asio::ssl::verify_fail_if_no_peer_cert
    );

ssl_context_.set_verify_callback(
    std::bind(
      &verify_certificate_cb, std::placeholders::_1, std::placeholders::_2
      )
    );
    
try
{
  const boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::make_address(ip), port);
  acceptor_.open(endpoint.protocol());
  acceptor_.set_option(boost::asio::socket_base::reuse_address(true));
  acceptor_.bind(endpoint);
  acceptor_.listen();
}
catch(const boost::system::system_error &e)
{
  LOG_ERROR("Acceptor error: " << e.what());
  return;
}
 
acceptor_.async_accept(io_context_, boost::beast::bind_front_handler(&server::accept_handler, this));

后续相关代码如下:

static bool verify_certificate_cb(bool preverified, boost::asio::ssl::verify_context& ctx)
{
  char subject_name[256];
  X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
  X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
  LOG_INFO("TLS connection verification for: " << subject_name);
  return preverified;
}

void server::accept_handler(const boost::system::error_code& error, boost::asio::ip::tcp::socket socket) noexcept
{
  if (!error)
  {
    // 使用Boost Asio的范式,websocket将其完成处理程序绑定到该对象的shared_ptr
    // 这允许对象的生命周期超出当前作用域,直到其操作完成
    // 一旦所有异步操作完成,不再有引用,对象生命周期结束
    const auto ws(std::make_shared<websocket>(std::move(socket), ssl_context_, auth_));
    
    // 启动websocket握手
    ws->handshake();

    // 继续接受下一个可用连接
    acceptor_.async_accept(io_context_, boost::beast::bind_front_handler(&server::accept_handler, this));
  }
  else
  {
    LOG_ERROR("Acceptor error: " << error.message());
  }
}

该实现能在初始连接时正常验证客户端证书,但问题在于服务器允许客户端长期连接,连接时长可能超过客户端证书的有效期,因此需要在初始连接建立后定期重新检查客户端证书,请问最佳实现方式是什么?


更新1

参考@sehe的评论后,我将ssl_context_.set_verify_mode()替换为SSL_CTX_set_verify(),因为前者不支持SSL_VERIFY_POST_HANDSHAKE。随后在WebSocket异步写入处理函数中添加了SSL_verify_client_post_handshake(websocket_.next_layer().native_handle());,但该调用始终返回0(失败),我似乎遗漏了某些配置。

我的测试客户端是基于python3-websockets的应用,代码如下:

import websocket, ssl
import time

my_context = ssl.create_default_context()
my_context.load_verify_locations('/usr/share/www/daikin-txrx-ws/public_html/daikin-txrx-ws-ca.crt')
my_context.load_cert_chain(
        '/usr/share/www/daikin-txrx-ws/public_html/daikin-txrx-ws-client.crt',
        '/usr/share/www/daikin-txrx-ws/public_html/daikin-txrx-ws-client.key')
my_context.check_hostname = False
my_context.post_handshake_auth = True
my_context.verify_mode = ssl.CERT_REQUIRED

ws = websocket.WebSocket(sslopt={'context': my_context})
ws.connect('wss://localhost:889');

while True:
    ws.send("Hello, Server")
    print(ws.recv())
    time.sleep(1)

我还遗漏了什么配置?


更新2

情况有所改善,但仍存在问题。更新1中的问题源于SSL_verify_client_post_handshake()是TLSv1.3特性,而openssl s_client和python-websockets默认使用TLSv1.2连接。当我在openssl s_client中添加-tls1_3强制使用TLSv1.3时,Boost Beast WebSocket拒绝连接并提示“unsupported protocol”。

随后我发现了一个事后看来很明显的问题:我原本使用boost::asio::ssl::context::tlsv12初始化ssl_context,将其改为boost::asio::ssl::context::tlsv13后,两个客户端均能通过TLSv1.3连接!此时SSL_verify_client_post_handshake()的首次调用返回1(成功),但约1秒后的第二次调用返回0,我似乎还差最后一步...


更新3

问题已解决!实际上更新2的实现是有效的,第二次调用SSL_verify_client_post_handshake()返回0仅表示前一次验证请求尚未完成。


内容的提问来源于stack exchange,提问作者Paul Grinberg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 12:57:33