简体   繁体   中英

Certificate verify failed in client server communication with boost::asio and OpenSSL

I'm writing simple echo server and client for it as proof of concept for encrypted network communication with boost::asio and OpenSSL . I want both sides to authenticate each other with self signed certificate. I'm using the same private key for both sides with two certificates generated from it. But when I tried to establish secured communication between both sides I have the following error during the handshake phase:

certificate verify failed

First I generated both private key and server certificate with:

openssl req -x509 -newkey rsa:2048 -keyout private_key.pem -out certificate.pem -days 365 -nodes

Then I generated additional certificate for the client with

 openssl req -newkey rsa:2048 -nodes -keyout private_key.pem -x509 -days 365 -out client_certificate.pem

The initialization of the OpenSSL context in the server is:

...
, m_sslContext(ssl::context::sslv23)
...
m_sslContext.set_verify_mode(ssl::verify_peer | ssl::verify_fail_if_no_peer_cert);
m_sslContext.use_private_key_file(options.privateKeyFile, ssl::context::pem);
m_sslContext.use_certificate_file(options.certificateFile, ssl::context::pem);
...

In the client it is:

...
, m_sslContext(ssl::context::sslv23)
...
 m_sslContext.set_verify_mode(ssl::verify_peer);
 m_sslContext.use_private_key_file(options.privateKeyFile, ssl::context::pem);
 m_sslContext.use_certificate_file(options.certificateFile, ssl::context::pem);
...

The full code for the server is here:

#include <iostream>
#include <iomanip>
#include <fstream>
#include <vector>
#include <string>
#include <thread>
#include <atomic>
#include <cstdint>

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>

#include <boost/program_options.hpp>

#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>

#include <io_service_pool.hpp>

using namespace std;
using namespace boost::asio;
using namespace boost::system;

namespace po = boost::program_options;
namespace logging = boost::log;

#define CHECK_FOR_ERROR(err) \
    if (err) \
    { \
        BOOST_LOG_TRIVIAL(error) << err.message() << endl; \
        return; \
    }

namespace std
{

ostream& operator<<(ostream& ostr, const boost::asio::streambuf& buffer)
{
    for (size_t i = 0; i < buffer.size(); ++i)
    {
        ostr << hex << (unsigned short) buffer_cast<const char*>(buffer.data())[i] << " ";
    }
    return ostr;
}

} // std namespace

struct Statistics
{
    atomic<uint32_t> connectionsAccepted;
    atomic<uint32_t> requestsReceived;
    atomic<uint32_t> responsesSent;
    atomic<uint64_t> dataReceived;
    atomic<uint64_t> dataSent;

}; // Statistics struct

Statistics g_statistics;

class TCPConnection : public enable_shared_from_this<TCPConnection>
{
public:
    using SSLSocket = ssl::stream<ip::tcp::socket>;
    using Ptr = shared_ptr<TCPConnection>;

    static Ptr create(io_service& ioService, ssl::context& sslContext)
    {
        return TCPConnection::Ptr(new TCPConnection(ioService, sslContext));
    }

    SSLSocket::lowest_layer_type& socket()
    {
        return m_socket.lowest_layer();
    }

    void start()
    {
        ip::tcp::no_delay noDelay(true);
        socket().set_option(noDelay);

        auto remoteAddress = socket().remote_endpoint().address().to_string();
        BOOST_LOG_TRIVIAL(debug) << "Incoming connection from: " << remoteAddress;

        g_statistics.connectionsAccepted++;

        auto self(shared_from_this());

        m_socket.async_handshake(ssl::stream_base::server, [self, remoteAddress](const error_code& err)
        {
            CHECK_FOR_ERROR(err)

            BOOST_LOG_TRIVIAL(debug) << "Handshake with " << remoteAddress << " succeeded.";

            async_read_until(self->m_socket, self->m_buffer, '\0',
                             [self, remoteAddress](const error_code& err, size_t bytesTransfered)
            {
                CHECK_FOR_ERROR(err)
                BOOST_LOG_TRIVIAL(debug) << "Received " << bytesTransfered << " bytes from " << remoteAddress;
                BOOST_LOG_TRIVIAL(trace) << "Data: " << self->m_buffer;

                g_statistics.requestsReceived++;
                g_statistics.dataReceived += bytesTransfered;

                async_write(self->m_socket, self->m_buffer,
                            [self, remoteAddress](const error_code& err, size_t bytesTransfered)
                {
                    CHECK_FOR_ERROR(err)
                    BOOST_LOG_TRIVIAL(debug) << "Sent " << dec << bytesTransfered << " bytes to " << remoteAddress;

                    g_statistics.responsesSent++;
                    g_statistics.dataSent += bytesTransfered;
                });
            });
        });
    }

private:
    TCPConnection(io_service& ioService, ssl::context& sslContext)
        : m_socket(ioService, sslContext)
    {
    }

private:
    SSLSocket m_socket;
    boost::asio::streambuf m_buffer;

}; // TCPConnection class

struct ProgramOptions
{
    unsigned port         = 0;
    unsigned threadsCount = 0;
    string privateKeyFile;
    string certificateFile;

}; // ProgramOptions struct

class Server
{
public:
    Server(const ProgramOptions& options)
        : m_ioServicePool(options.threadsCount)
        , m_acceptor(m_ioServicePool.getIOService(),
                     ip::tcp::endpoint(ip::tcp::v4(), options.port))
        , m_signals(m_ioServicePool.getIOService())
        , m_sslContext(ssl::context::sslv23)
    {
        m_acceptor.set_option(ip::tcp::acceptor::reuse_address(true));

        m_signals.add(SIGINT);
        m_signals.add(SIGTERM);
        m_signals.add(SIGQUIT);
        m_signals.async_wait([this](const error_code& err, int signalNumber)
        {
           if (err)
           {
               BOOST_LOG_TRIVIAL(error) << err.message();
           }

           BOOST_LOG_TRIVIAL(debug) << "Received signal: " << signalNumber;

           BOOST_LOG_TRIVIAL(info) << "Statistics:";
           BOOST_LOG_TRIVIAL(info) << "Connections accepted: " << g_statistics.connectionsAccepted;
           BOOST_LOG_TRIVIAL(info) << "Requests received: " << g_statistics.requestsReceived;
           BOOST_LOG_TRIVIAL(info) << "Responses sent: " << g_statistics.responsesSent;
           BOOST_LOG_TRIVIAL(info) << "Data received: " << g_statistics.dataReceived << " bytes.";
           BOOST_LOG_TRIVIAL(info) << "Data sent: " << g_statistics.dataSent << " bytes.";

           m_ioServicePool.stop();
        });

        m_sslContext.set_verify_mode(ssl::verify_peer | ssl::verify_fail_if_no_peer_cert);
        m_sslContext.use_private_key_file(options.privateKeyFile, ssl::context::pem);
        m_sslContext.use_certificate_file(options.certificateFile, ssl::context::pem);

        startAccept();
    }

    void run()
    {
        BOOST_LOG_TRIVIAL(info) << "Server started!";
        m_ioServicePool.run();
    }

private:
    void startAccept()
    {
        auto conn = TCPConnection::create(m_ioServicePool.getIOService(), m_sslContext);

        m_acceptor.async_accept(conn->socket(), [this, conn](const error_code& err)
        {
           CHECK_FOR_ERROR(err)
           conn->start();

           startAccept();
        });
    }

private:
    IOServicePool m_ioServicePool;
    ip::tcp::acceptor m_acceptor;
    signal_set m_signals;
    ssl::context m_sslContext;

}; // Server class

#undef CHECK_FOR_ERROR

#define SET_SEVERITY_FILTER(svrt) \
    logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::svrt);

void setLoggerSeverity(const string& loggerSeverity,
                       const po::options_description& options)
{
    if ("trace" == loggerSeverity)
    {
        SET_SEVERITY_FILTER(trace)
    }
    else if ("debug" == loggerSeverity)
    {
        SET_SEVERITY_FILTER(debug)
    }
    else if ("info" == loggerSeverity)
    {
        SET_SEVERITY_FILTER(info)
    }
    else if ("warning" == loggerSeverity)
    {
        SET_SEVERITY_FILTER(warning)
    }
    else if ("error" == loggerSeverity)
    {
        SET_SEVERITY_FILTER(error)
    }
    else if ("fatal" == loggerSeverity)
    {
        SET_SEVERITY_FILTER(fatal)
    }
    else
    {
        cerr << "Error: Invalid logger severity: " << loggerSeverity << endl << endl;
        cerr << options << endl;
        exit(1);
    }
}

#undef SET_SEVERITY_FILTER

void readProgramOptions(int argc, char* argv[], ProgramOptions& options)
{
    string configFileName;
    string loggerSeverity;

    po::options_description genericOptions("Generic options");
    genericOptions.add_options()
        ("help,h", "Produce help message.")
        ("config,c", po::value<string>(&configFileName),
         "Name of a file of a configuration.");

    po::options_description configOptions("Configuration");
    configOptions.add_options()
        ("port,p", po::value<unsigned>(&options.port), "Port on which the server listens.")
        ("threads_count,t", po::value<unsigned>(&options.threadsCount)->default_value(0),
         "Number of server threads. 0 for to use hardware threads count.")
        ("private_key_file,r", po::value<string>(&options.privateKeyFile)->default_value("private_key.pem"),
         "Server private key file name.")
        ("certificate_file,e", po::value<string>(&options.certificateFile)->default_value("certificate.pem"),
         "Server certificate file name.")
        ("log_severity,l", po::value<string>(&loggerSeverity)->default_value("info"),
         "Minimum severity of the log messages: [trace, debug, info, warning, error, fatal]");

    po::options_description commandLineOptions("Allowed options");
    commandLineOptions.add(genericOptions).add(configOptions);

    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, commandLineOptions), vm);
    po::notify(vm);

    if (vm.count("config"))
    {
        ifstream configFile(configFileName);
        if (!configFile)
        {
            cerr << "Error: Can not open config file: " << configFileName << endl;
            exit(1);
        }
        else
        {
            po::store(po::parse_config_file(configFile, configOptions), vm);
            po::notify(vm);
        }
    }

    if (vm.count("help"))
    {
        cout << commandLineOptions << endl;
        exit(1);
    }

    if (0 == options.threadsCount)
    {
        options.threadsCount = thread::hardware_concurrency();
    }

    if (!vm.count("port"))
    {
        cerr << "Error: Must specify port on which the server listens!" << endl << endl;
        cerr << commandLineOptions << endl;
        exit(1);
    }

    setLoggerSeverity(loggerSeverity, commandLineOptions);

    BOOST_LOG_TRIVIAL(info) << "Configuration:";
    BOOST_LOG_TRIVIAL(info) << "Listening port: " << options.port;
    BOOST_LOG_TRIVIAL(info) << "Worker threads count: " << options.threadsCount;
    BOOST_LOG_TRIVIAL(info) << "Private key file name: " << options.privateKeyFile;
    BOOST_LOG_TRIVIAL(info) << "Certificate file name: " << options.certificateFile;
}

int main(int argc, char* argv[])
{
    try
    {
        ProgramOptions programOptions;
        readProgramOptions(argc, argv, programOptions);

        Server server(programOptions);
        server.run();
    }
    catch (const exception& e)
    {
        BOOST_LOG_TRIVIAL(fatal) << e.what();;
    }
    catch (...)
    {
        BOOST_LOG_TRIVIAL(fatal) << "Unknown error!!!" << endl;
    }

    return 0;
}

The client code is here:

#include <iostream>
#include <fstream>
#include <string>
#include <thread>
#include <atomic>
#include <cstdint>

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>

#include <boost/program_options.hpp>

#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>

#include <io_service_pool.hpp>
#include <speed_limiter.hpp>

using namespace std;
using namespace boost::asio;
using namespace boost::system;

namespace po = boost::program_options;
namespace logging = boost::log;

#define CHECK_FOR_ERROR(err) \
    if (err) \
    { \
        BOOST_LOG_TRIVIAL(error) << err.message() << endl; \
        return; \
    }

namespace std
{

ostream& operator<<(ostream& ostr, const boost::asio::streambuf& buffer)
{
    for (size_t i = 0; i < buffer.size(); ++i)
    {
        ostr << hex << (unsigned short) buffer_cast<const char*>(buffer.data())[i] << " ";
    }
    return ostr;
}

} // std namespace

string generateRandomData(size_t size)
{
     static random_device randomDevice;
     static default_random_engine randomEngine(randomDevice());
     static uniform_int_distribution<unsigned char> randomByte(1, 255);

     string data;
     data.reserve(size);

     for (size_t i = 0; i < size - 1; ++i)
     {
         data += randomByte(randomEngine);
     }
     data += '\0';

     return data;
}

struct Statistics
{
    atomic<uint32_t> connectionsEstablished;
    atomic<uint32_t> requestsSent;
    atomic<uint32_t> responsesReceived;
    atomic<uint64_t> dataSent;
    atomic<uint64_t> dataReceived;

}; // Statistics struct

Statistics g_statistics;

void printStatistics()
{
    BOOST_LOG_TRIVIAL(info) << "Statistics:";
    BOOST_LOG_TRIVIAL(info) << "Connections established: " << g_statistics.connectionsEstablished;
    BOOST_LOG_TRIVIAL(info) << "Requests sent: " << g_statistics.requestsSent;
    BOOST_LOG_TRIVIAL(info) << "Responses received: " << g_statistics.requestsSent;
    BOOST_LOG_TRIVIAL(info) << "Data sent: " << g_statistics.dataSent << " bytes.";
    BOOST_LOG_TRIVIAL(info) << "Data received: " << g_statistics.dataReceived << " bytes.";
}

class TCPConnection : public enable_shared_from_this<TCPConnection>
{
public:
    using SSLSocket = ssl::stream<ip::tcp::socket>;
    using Ptr = shared_ptr<TCPConnection>;

    static Ptr create(io_service& ioService, ssl::context& sslContext)
    {
        return TCPConnection::Ptr(new TCPConnection(ioService, sslContext));
    }

    SSLSocket::lowest_layer_type& socket()
    {
        return m_socket.lowest_layer();
    }

    void setSendData(const std::string& data)
    {
        ostream stream(&m_writeBuffer);
        stream.write(data.data(), data.size());
    }

    void startConnect(ip::tcp::resolver::iterator endpointIter)
    {
        if (endpointIter != ip::tcp::resolver::iterator())
        {
            BOOST_LOG_TRIVIAL(debug) << "Trying " << endpointIter->endpoint() << " ...";

            auto self(shared_from_this());

            socket().async_connect(endpointIter->endpoint(), [self, endpointIter](const error_code& err) mutable
            {
                if (!err)
                {
                    ip::tcp::no_delay noDelay(true);
                    self->socket().set_option(noDelay);

                    auto endpoint = endpointIter->endpoint();

                    BOOST_LOG_TRIVIAL(debug) << "Connected to " << endpoint;

                    g_statistics.connectionsEstablished++;

                    self->m_socket.async_handshake(ssl::stream_base::client, [self, endpoint](const error_code& err)
                    {
                        CHECK_FOR_ERROR(err)

                        BOOST_LOG_TRIVIAL(debug) << "Handshake with " << endpoint << " succeeded.";

                        async_write(self->m_socket, self->m_writeBuffer,
                                    [self, endpoint](const error_code& err, size_t bytesTransfered)
                        {
                            CHECK_FOR_ERROR(err)
                            BOOST_LOG_TRIVIAL(debug) << "Sent " << bytesTransfered << " bytes to " << endpoint;

                            g_statistics.requestsSent++;
                            g_statistics.dataSent += bytesTransfered;

                            async_read_until(self->m_socket, self->m_readBuffer, '\0',
                                       [self, endpoint](const error_code& err, size_t bytesTransfered)
                            {
                                CHECK_FOR_ERROR(err)
                                BOOST_LOG_TRIVIAL(debug) << "Received " << bytesTransfered
                                                         << " bytes from " << endpoint;
                                BOOST_LOG_TRIVIAL(trace) << "Data: " << self->m_readBuffer;

                                g_statistics.responsesReceived++;
                                g_statistics.dataReceived += bytesTransfered;
                            });
                        });
                    });
                }
                else
                {
                    BOOST_LOG_TRIVIAL(error) << err.message();
                    self->startConnect(++endpointIter);
                }
            });
        }
        else
        {
            BOOST_LOG_TRIVIAL(warning) << "There are no more endpoints to try. Shut down the client.";
            socket().close();
        }
    }

private:
    TCPConnection(io_service& ioService, ssl::context& sslContext)
        : m_socket(ioService, sslContext)
    {
    }

private:
    SSLSocket m_socket;
    boost::asio::streambuf m_writeBuffer;
    boost::asio::streambuf m_readBuffer;

}; // TCPConnection class

struct ProgramOptions
{
    string serverIP;
    string privateKeyFile;
    string certificateFile;
    unsigned serverPort        = 0;
    unsigned requestSize       = 0;
    unsigned threadsCount      = 0;
    unsigned totalRequests     = 0;
    unsigned requestsPerSecond = 0;

}; // ProgramOptions struct

class Client
{
public:
    Client(const ProgramOptions& options)
        : m_requestSize(options.requestSize)
        , m_totalRequests(options.totalRequests)
        , m_requestsPerSecond(options.requestsPerSecond)
        , m_ioServicePool(options.threadsCount)
        , m_resolver(m_ioServicePool.getIOService())
        , m_sslContext(ssl::context::sslv23)

    {
        ip::tcp::resolver::query query(options.serverIP, to_string(options.serverPort));

        m_resolver.async_resolve(query, [this](const error_code& err, ip::tcp::resolver::iterator endpointIter)
        {
            CHECK_FOR_ERROR(err)

            m_dispatcherThread = thread([this, endpointIter]()
            {
                SpeedLimiter<> speedLimiter(m_requestsPerSecond);

                for (unsigned i = 0; i < m_totalRequests; ++i)
                {
                    auto conn = TCPConnection::create(m_ioServicePool.getIOService(), m_sslContext);
                    conn->setSendData(generateRandomData(m_requestSize));

                    conn->startConnect(endpointIter);

                    if (m_requestsPerSecond != 0)
                        speedLimiter.limit();
                }

                this_thread::sleep_for(chrono::seconds(1));
                m_ioServicePool.stop();
            });
        });

        m_sslContext.set_verify_mode(ssl::verify_peer);
        m_sslContext.use_private_key_file(options.privateKeyFile, ssl::context::pem);
        m_sslContext.use_certificate_file(options.certificateFile, ssl::context::pem);
    }

    ~Client()
    {
        m_dispatcherThread.join();
    }

    void run()
    {
        BOOST_LOG_TRIVIAL(info) << "Client started!";
        m_ioServicePool.run();
    }

private:
    unsigned m_requestSize;
    unsigned m_totalRequests;
    unsigned m_requestsPerSecond;

    IOServicePool m_ioServicePool;
    ip::tcp::resolver m_resolver;
    thread m_dispatcherThread;
    ssl::context m_sslContext;

}; // Client class

#define SET_SEVERITY_FILTER(svrt) \
    logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::svrt);

void setLoggerSeverity(const string& loggerSeverity,
                       const po::options_description& options)
{
    if ("trace" == loggerSeverity)
    {
        SET_SEVERITY_FILTER(trace)
    }
    else if ("debug" == loggerSeverity)
    {
        SET_SEVERITY_FILTER(debug)
    }
    else if ("info" == loggerSeverity)
    {
        SET_SEVERITY_FILTER(info)
    }
    else if ("warning" == loggerSeverity)
    {
        SET_SEVERITY_FILTER(warning)
    }
    else if ("error" == loggerSeverity)
    {
        SET_SEVERITY_FILTER(error)
    }
    else if ("fatal" == loggerSeverity)
    {
        SET_SEVERITY_FILTER(fatal)
    }
    else
    {
        cerr << "Error: Invalid logger severity: " << loggerSeverity << endl << endl;
        cerr << options << endl;
        exit(1);
    }
}

#undef SET_SEVERITY_FILTER

void readProgramOptions(int argc, char* argv[], ProgramOptions& options)
{
    string configFileName;
    string loggerSeverity;

    po::options_description genericOptions("Generic options");
    genericOptions.add_options()
        ("help,h", "Produce help message.")
        ("config,c", po::value<string>(&configFileName),
         "Name of a file of a configuration.");

    po::options_description configOptions("Configuration");
    configOptions.add_options()
        ("ip,i", po::value<string>(&options.serverIP), "IP address of the server.")
        ("port,p", po::value<unsigned>(&options.serverPort), "Port number of the server.")
        ("threads_count,t", po::value<unsigned>(&options.threadsCount)->default_value(0),
         "Number of client threads. 0 for to use hardware threads count.")
        ("private_key_file,r", po::value<string>(&options.privateKeyFile)->default_value("private_key.pem"),
         "Client private key file name.")
        ("certificate_file,e", po::value<string>(&options.certificateFile)->default_value("certificate.pem"),
         "Client certificate file name.")
        ("request_size,m", po::value(&options.requestSize), "The size of the request in bytes.")
        ("total_requests,t", po::value(&options.totalRequests), "The total number of requests to be send.")
        ("requests_per_second,s", po::value(&options.requestsPerSecond)->default_value(0),
         "Speed in which requests are send. 0 for unlimited speed.")
        ("log_severity,l", po::value<string>(&loggerSeverity)->default_value("info"),
         "Minimum severity of the log messages: [trace, debug, info, warning, error, fatal]");

    po::options_description commandLineOptions("Allowed options");
    commandLineOptions.add(genericOptions).add(configOptions);

    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, commandLineOptions), vm);
    po::notify(vm);

    if (vm.count("config"))
    {
        ifstream configFile(configFileName);
        if (!configFile)
        {
            cerr << "Error: Can not open config file: " << configFileName << endl;
            exit(1);
        }
        else
        {
            po::store(po::parse_config_file(configFile, configOptions), vm);
            po::notify(vm);
        }
    }

    if (vm.count("help"))
    {
        cout << commandLineOptions << endl;
        exit(1);
    }

    if (0 == options.threadsCount)
    {
        options.threadsCount = thread::hardware_concurrency();
    }

    if (!vm.count("ip"))
    {
        cerr << "Error: Must specify the IP address of the server!" << endl << endl;
        cerr << commandLineOptions << endl;
        exit(1);
    }

    if (!vm.count("port"))
    {
        cerr << "Error: Must specify port on which to connect!" << endl << endl;
        cerr << commandLineOptions << endl;
        exit(1);
    }

    if (!vm.count("request_size"))
    {
        cerr << "Error: Must specify bytes size of the request!" << endl << endl;
        cerr << commandLineOptions << endl;
        exit(1);
    }

    setLoggerSeverity(loggerSeverity, commandLineOptions);

    BOOST_LOG_TRIVIAL(info) << "Configuration:";
    BOOST_LOG_TRIVIAL(info) << "Server IP: " << options.serverIP;
    BOOST_LOG_TRIVIAL(info) << "Server port: " << options.serverPort;
    BOOST_LOG_TRIVIAL(info) << "Threads count: " << options.threadsCount;
    BOOST_LOG_TRIVIAL(info) << "Private key file name: " << options.privateKeyFile;
    BOOST_LOG_TRIVIAL(info) << "Certificate file name: " << options.certificateFile;
    BOOST_LOG_TRIVIAL(info) << "Request size: " << options.requestSize;
    BOOST_LOG_TRIVIAL(info) << "Total requests: " << options.totalRequests;
    BOOST_LOG_TRIVIAL(info) << "Requests per second: "
                            << (options.requestsPerSecond == 0 ? "unlimited" : to_string(options.requestsPerSecond));
}

int main(int argc, char* argv[])
{
    try
    {
        ProgramOptions programOptions;
        readProgramOptions(argc, argv, programOptions);

        Client client(programOptions);
        client.run();

        printStatistics();
    }
    catch (const exception& e)
    {
        BOOST_LOG_TRIVIAL(fatal) << e.what();;
    }
    catch (...)
    {
        BOOST_LOG_TRIVIAL(fatal) << "Unknown error!!!" << endl;
    }

    return 0;
}

use_certificate_file() and use_private_key_file() let you provide a public key to the peer and decrypt what the peer returns, but they don't handle verification. You should use one (or more) of add_certificate_authority() , add_verify_path() , load_verify_file() , set_default_verify_paths() , and set_verify_callback() to configure trust verification. It looks like load_verify_file() is probably the one that you want.

It looks like you may be under the misconception that the private key is used to verify the peer certificate. That is not the case - a key is not used to verify a signature and typically the client and server private keys will be different. A CA certificate is used to verify a peer certificate. In your case, because you are using self-signed certificates, the CA certificate and the peer certificate are the same. Each side needs to load the other's certificate for verification.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM