BOOST:: ASIO :: SSL ::上下文不发送客户端证书

问题描述 投票:0回答:1

对于一些奇怪的原因,请调用

#pragma once

#include "Config.hpp"
#include "util/concurrent/ThreadPool.hpp"
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <filesystem>
#include <memory>
#include <string>

using namespace boost::asio;
namespace fs = std::filesystem;

namespace sdk {

class HttpClient {
  public:
    explicit HttpClient(std::shared_ptr<ThreadPool> threadPool)
        : m_threadPool(std::move(threadPool)),
          m_ioContext(m_threadPool->getIoContext()),
          m_sslContext(ssl::context::tlsv12_client),
          m_sslSocket(m_ioContext, m_sslContext),
          m_host(Config::getData()->host),
          m_port(Config::getData()->port)
    {
        auto configData = Config::getData();

        // so dumb. why can't I just use the context directly?
        // i have to use the native handle to load the certificate and key
        // because boost::asio is not sending the client certificate
        SSL *sslHandle = m_sslSocket.native_handle();
        if (!sslHandle) {
            throw std::runtime_error("Failed to retrieve SSL handle!");
        }

        if (SSL_use_certificate_file(sslHandle, configData->keystore.path.c_str(), SSL_FILETYPE_PEM) <= 0) {
            // m_sslContext.use_certificate_chain_file(Config::getData()->keystore.path); // <== does not work
            throw std::runtime_error("Failed to load client certificate from: " + configData->keystore.path);
        }
        else if (SSL_use_PrivateKey_file(sslHandle, configData->key.path.c_str(), SSL_FILETYPE_PEM) <= 0) {
            // m_sslContext.use_private_key_file(Config::getData()->key.path, ssl::context::pem); // <== does not work
            throw std::runtime_error("Failed to load client private key from: " + configData->key.path);
        }
    }

    void connect()
    {
        ip::tcp::resolver resolver(m_ioContext);
        auto              endpoints = resolver.resolve(m_host, std::to_string(m_port));

        LOG(info) << "Resolving and connecting to: " << m_host << ":" << m_port;

        auto handshake_worker = [this](const boost::system::error_code &ec) {
            if (!ec) {
                LOG(info) << "SSL handshake successful!";
            }
            else {
                LOG(error) << "SSL handshake failed: " << ec.message();
                stop();
            }
        };

        auto connect_worker = [this, handshake_worker](const boost::system::error_code &ec,
                                                       const ip::tcp::endpoint         &endpoint) {
            if (!ec) {
                LOG(info) << "Connected to: " << endpoint;
                m_sslSocket.async_handshake(ssl::stream_base::client, handshake_worker);
            }
            else {
                LOG(error) << "Connection failed: " << ec.message();
                stop();
            }
        };

        async_connect(m_sslSocket.lowest_layer(), endpoints, connect_worker);
    }

    void stop()
    {
        if (m_sslSocket.lowest_layer().is_open()) {
            boost::system::error_code ec;
            m_sslSocket.lowest_layer().shutdown(ip::tcp::socket::shutdown_both, ec);
            m_sslSocket.lowest_layer().close();
        }
        m_threadPool->stop();
    }

    virtual ~HttpClient() { stop(); }

  private:
    std::shared_ptr<ThreadPool>  m_threadPool;
    io_context                  &m_ioContext;
    ssl::context                 m_sslContext;
    ssl::stream<ip::tcp::socket> m_sslSocket;
    std::string                  m_host;
    int                          m_port;
};

} // namespace sdk
实例设置证书,而私有键文件不起作用。它在握手中失败了。

Java服务器日志没有发送证书链。

boost::asio::ssl::context

,如果我从
javax.net.ssl|DEBUG|11|nioEventLoopGroup-2-2|2025-01-30 22:52:33.911 UTC|CertificateMessage.java:372|Consuming client Certificate handshake message ( "Certificates": <empty list> ) javax.net.ssl|ERROR|11|nioEventLoopGroup-2-2|2025-01-30 22:52:33.913 UTC|TransportContext.java:358|Fatal (BAD_CERTIFICATE): Empty client certificate chain ( "throwable" : {

中获取本机SSL句柄,并直接调用本机API以设置证书和私钥文件,则一切都可以。客户证书已发送并握手成功。

我只能得出结论,我错过了一个步骤或以错误的顺序做某事,但是我无法分辨什么。我也尝试了以下内容,没有成功
boost::asio::ssl::context

我太肛门了,无法将作品留在原地,然后走开了。

问题是您在构造SSL流对象之后设置上下文参数。

直接使用OpenSSL的工作是您正在使用


// Configure SSL settings m_sslContext.set_options( ssl::context::default_workarounds | ssl::context::no_sslv2 | ssl::context::no_sslv3 | ssl::context::single_dh_use ); // Enforce mutual TLS authentication m_sslContext.set_verify_mode( ssl::verify_peer | ssl::verify_fail_if_no_peer_cert);

vs.
SSL_use_certificate_file

c++ ssl boost
1个回答
0
投票
vs.

SSL_use_PrivateKey_file

    这里是一个独立的示例,可以使用
  • SSL_CTX_use_PrivateKey_file
    工厂来修复初始化顺序。请注意,我已经对验证进行了固定的验证,以允许我的自我签名的测试证书。
    live在coliru
    create_ssl_context()
  • 打印,例如
  • #include <boost/asio.hpp> #include <boost/asio/ssl.hpp> #include <iostream> #include <syncstream> namespace asio = boost::asio; namespace ssl = asio::ssl; using asio::ip::tcp; #define LOG(level) std::osyncstream(std::cout) << "\n[" #level "] " << __FUNCTION__ << ":" << __LINE__ << " " namespace sdk { static constexpr uint16_t SERVICE_PORT = 4433; namespace Config { struct Keystore { std::string path; }; struct Key { std::string path; }; struct Data { std::string host; int port; Keystore keystore; Key key; }; auto getData() -> std::shared_ptr<Data const> { static auto data = std::make_shared<Data>(Data{ "localhost", SERVICE_PORT, Keystore{"certs/client_cert.pem"}, Key{"certs/client_cert.pem"}, }); return data; } } // namespace Config using boost::system::error_code; asio::awaitable<void> server() try { auto ex = co_await asio::this_coro::executor; tcp::acceptor acc_(ex, {{}, SERVICE_PORT}); ssl::context ctx(ssl::context::tlsv12_server); ctx.set_password_callback([](std::size_t, ssl::context_base::password_purpose) { return "test"; }); ctx.set_verify_callback([](bool preverified, 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) << "Server Verifying: " << subject_name << " preverified: " << preverified; return true; }); ctx.set_verify_mode(ssl::context::verify_peer | ssl::context::verify_fail_if_no_peer_cert); ctx.use_certificate_file("certs/server_cert.pem", ssl::context::pem); ctx.use_private_key_file("certs/server_cert.pem", ssl::context::pem); for (;;) { auto s = co_await acc_.async_accept(); LOG(info) << "Accepted connection from: " << s.remote_endpoint(); ssl::stream<tcp::socket> ss(std::move(s), ctx); co_await ss.async_handshake(ssl::stream_base::server); LOG(info) << "Handshake successful"; break; // let's just accept one connection }; } catch(boost::system::system_error const& se) { LOG(error) << "Exception: " << se.code().message(); } struct ThreadPool { explicit ThreadPool(size_t threads) : m_impl(threads) {} virtual ~ThreadPool() { join(); } void join() { m_impl.join(); } void stop() { m_impl.stop(); } using executor_type = asio::thread_pool::executor_type; executor_type get_executor() { return m_impl.get_executor(); } private: asio::thread_pool m_impl; }; class HttpClient { public: explicit HttpClient(asio::any_io_executor ex) : m_io(ex) , m_ctx(create_ssl_context()) , m_host(Config::getData()->host) , m_port(Config::getData()->port) {} void connect() { tcp::resolver resolver(m_io); auto endpoints = resolver.resolve(m_host, std::to_string(m_port)); LOG(info) << "Client resolving and connecting to: " << m_host << ":" << m_port; auto on_handshake = [this](error_code ec) { LOG(info) << "Client SSL handshake " << ec.message(); stop(); }; auto on_connect = [this, on_handshake](error_code ec, tcp::endpoint const& endpoint) { if (!ec) { LOG(info) << "Client Connected to: " << endpoint; m_sslSocket.async_handshake(ssl::stream_base::client, on_handshake); } else { LOG(error) << "Client Connection failed: " << ec.message(); stop(); } }; async_connect(m_sslSocket.lowest_layer(), endpoints, on_connect); } void stop() { if (m_sslSocket.lowest_layer().is_open()) { error_code ec; m_sslSocket.lowest_layer().shutdown(tcp::socket::shutdown_both, ec); m_sslSocket.lowest_layer().close(); } } virtual ~HttpClient() { stop(); } private: asio::any_io_executor m_io; ssl::context m_ctx; ssl::stream<tcp::socket> m_sslSocket{m_io, m_ctx}; std::string m_host; int m_port; static ssl::context create_ssl_context() { ssl::context ctx(ssl::context::tlsv12_client); ctx.set_verify_callback([](bool preverified, 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) << "Client Verifying: " << subject_name << " preverified: " << preverified; return true; }); ctx.set_verify_mode(ssl::context::verify_peer | ssl::context::verify_fail_if_no_peer_cert); auto configData = Config::getData(); ctx.use_private_key_file(configData->key.path, ssl::context::pem); ctx.use_certificate_file(Config::getData()->keystore.path, ssl::context::pem); return ctx; } }; } // namespace sdk int main() { auto threadPool = std::make_shared<sdk::ThreadPool>(16); auto ex = threadPool->get_executor(); auto client = std::make_unique<sdk::HttpClient>(ex); // start server co_spawn(ex, sdk::server, asio::detached); // connect the client client->connect(); threadPool->join(); LOG(info) << "Done!\n"; }
    live:
        
© www.soinside.com 2019 - 2024. All rights reserved.