I have a Boost Beast asio based websocket server, which (in abbreviated form) starts like this
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));
and then, I have
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)
{
// Using Boost's asio paradigm, websocket binds its completion handlers to a shared ptr of this object.
// This allows the object to extend its lifetime beyond this scope until it's operations are done.
// Once the all async operations are done, no reference remains, finally ending the lifetime of this object.
const auto ws(std::make_shared<websocket>(std::move(socket), ssl_context_, auth_));
// Initiate websocket handshake
ws->handshake();
// Continue with accepting the next available connection
acceptor_.async_accept(io_context_, boost::beast::bind_front_handler(&server::accept_handler, this));
}
else
{
LOG_ERROR("Acceptor error: " << error.message());
}
}
This works great for validating client certificates on initial connection. The problem is that this websocket server expects clients to be connected for a long time, possibly longer than the validity of the client certificate. In other words, I need to periodically re-check the client certificate after the initial connection is made. What is the best way to do that?