CopernicaMarketingSoftware / AMQP-CPP

C++ library for asynchronous non-blocking communication with RabbitMQ
Apache License 2.0
885 stars 340 forks source link

[question] Example for onSecuring for SSL certficate? #527

Open harsszegi opened 7 months ago

harsszegi commented 7 months ago

Hi, I'm trying to use SSL and I fail to do so:

bool onSecuring(AMQP::TcpConnection / connection /, SSL ssl) override { auto* ssl_ctx = SSL_get_SSL_CTX(ssl);

if (!connection_parameters_.tls_params_.has_value())
{
  return false;
}

if (SSL_CTX_load_verify_locations(ssl_ctx, connection_parameters_.tls_params_->ca_cert_path_.c_str(), nullptr) != 1)
{
  std::cout << "Invalid ca cert" << std::endl;
  ERR_print_errors_fp(stderr);
  return false;
}

if (SSL_CTX_use_certificate_chain_file(ssl_ctx, connection_parameters_.tls_params_->client_cert_path_.c_str()) != 1)
{
  std::cout << "Invalid client cert" << std::endl;
  ERR_print_errors_fp(stderr);
  return false;
}

if (SSL_CTX_use_PrivateKey_file(ssl_ctx, connection_parameters_.tls_params_->client_key_path_.c_str(), SSL_FILETYPE_PEM) != 1)
{
  std::cout << "Invalid client private key" << std::endl;
  ERR_print_errors_fp(stderr);
  return false;
}

if (SSL_CTX_check_private_key(ssl_ctx) != 1)
{
  std::cout << "Check failed client private key" << std::endl;
  ERR_print_errors_fp(stderr);
  return false;
}

if (connection_parameters_.tls_params_->verify_peer_)
{
  SSL_set_verify(ssl, SSL_VERIFY_PEER, nullptr);
}
else
{
  SSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr);
}

return true;

}

This works fine, context is configured, however the server rejects this with "no_client_certificate_provided".

The same set of certificates with SimpleAmqpClient work fine, e.g. I can connect to the same broker. Any documentation / hint how to use SSL with amqp-cpp? Thanks,

harsszegi commented 7 months ago

Hmmm afaik I know what is going on. OpenSSL is initialized a bit "upside-down". First you need to the create SSL_CTX, then you need to create SSL with the preconfigured SSL_CTX. During SSL creation OpenSSL copies a whole lot of stuff from the SSL_CTX to SSL. So if OnSecuring is called after the SSL is initialized, you are way to late. I have modified amqp-cpp locally, basically like this:

SslHandshake(TcpExtState *state, const std::string &hostname, TcpOutBuffer &&buffer) : 
    TcpExtState(state),
    _ctx(OpenSSL::TLS_client_method()),
    _out(std::move(buffer))
{
    // use the default directories for verifying certificates
    OpenSSL::SSL_CTX_set_default_verify_paths(_ctx);

    // we allow userspace to make changes to the SSL structure
    if (!_parent->onSecuring(this, _ctx)) throw std::runtime_error("failed to initialize SSL structure in user space");

    _ssl = std::make_unique<SslWrapper>(_ctx);

    // we will be using the ssl context as a client
    OpenSSL::SSL_set_connect_state(*_ssl);

    // associate domain name with the connection
    OpenSSL::SSL_ctrl(*_ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, (void *)hostname.data());

    // associate the ssl context with the socket filedescriptor
    if (OpenSSL::SSL_set_fd(*_ssl, _socket) == 0) throw std::runtime_error("failed to associate filedescriptor with ssl socket");

    // we are going to wait until the socket becomes writable before we start the handshake
    _parent->onIdle(this, _socket, writable);
}

and it seems to fix my issue

AstroGoGo commented 6 months ago

Tibor Harsszegi, thanks plenty! I was really hitting the wall with the same problem because I couldn't do two-way SSL until I saw your fix. I did make it to trying to configure the onSecuring but I hit the same wall you did...until I read your post. I ended up throwing my SSL_CTX_use_PrivateKey_file call in the SslContext constructor and calling it a day. Thanks again!