mirror of
https://github.com/yhirose/cpp-httplib
synced 2024-11-21 14:29:10 -07:00
Pass certs and keys from memory (#432)
* SSLServer: add constructor to pass ssl-certificates and key from memory * SSLClient: add constructor to pass ssl-certificates and key from memory * add TestCase for passing certificates from memory to SSLClient/SSLServer
This commit is contained in:
parent
c2b6e4ac04
commit
2ece5f116b
2 changed files with 139 additions and 2 deletions
67
httplib.h
67
httplib.h
|
@ -848,6 +848,9 @@ public:
|
|||
const char *client_ca_cert_file_path = nullptr,
|
||||
const char *client_ca_cert_dir_path = nullptr);
|
||||
|
||||
SSLServer(X509 *cert, EVP_PKEY *private_key,
|
||||
X509_STORE *client_ca_cert_store = nullptr);
|
||||
|
||||
~SSLServer() override;
|
||||
|
||||
bool is_valid() const override;
|
||||
|
@ -865,6 +868,9 @@ public:
|
|||
const std::string &client_cert_path = std::string(),
|
||||
const std::string &client_key_path = std::string());
|
||||
|
||||
SSLClient(const std::string &host, int port, X509 *client_cert,
|
||||
EVP_PKEY *client_key);
|
||||
|
||||
~SSLClient() override;
|
||||
|
||||
bool is_valid() const override;
|
||||
|
@ -872,6 +878,8 @@ public:
|
|||
void set_ca_cert_path(const char *ca_ceert_file_path,
|
||||
const char *ca_cert_dir_path = nullptr);
|
||||
|
||||
void set_ca_cert_store(X509_STORE *ca_cert_store);
|
||||
|
||||
void enable_server_certificate_verification(bool enabled);
|
||||
|
||||
long get_openssl_verify_result() const;
|
||||
|
@ -897,6 +905,7 @@ private:
|
|||
|
||||
std::string ca_cert_file_path_;
|
||||
std::string ca_cert_dir_path_;
|
||||
X509_STORE *ca_cert_store_ = nullptr;
|
||||
bool server_certificate_verification_ = false;
|
||||
long verify_result_ = 0;
|
||||
};
|
||||
|
@ -4654,6 +4663,33 @@ inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
|
|||
}
|
||||
}
|
||||
|
||||
inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
|
||||
X509_STORE *client_ca_cert_store) {
|
||||
ctx_ = SSL_CTX_new(SSLv23_server_method());
|
||||
|
||||
if (ctx_) {
|
||||
SSL_CTX_set_options(ctx_,
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||
SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
|
||||
if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
|
||||
SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
|
||||
SSL_CTX_free(ctx_);
|
||||
ctx_ = nullptr;
|
||||
} else if (client_ca_cert_store) {
|
||||
|
||||
SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
|
||||
|
||||
SSL_CTX_set_verify(
|
||||
ctx_,
|
||||
SSL_VERIFY_PEER |
|
||||
SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE,
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline SSLServer::~SSLServer() {
|
||||
if (ctx_) { SSL_CTX_free(ctx_); }
|
||||
}
|
||||
|
@ -4693,6 +4729,24 @@ inline SSLClient::SSLClient(const std::string &host, int port,
|
|||
}
|
||||
}
|
||||
|
||||
inline SSLClient::SSLClient(const std::string &host, int port,
|
||||
X509 *client_cert, EVP_PKEY *client_key)
|
||||
: Client(host, port) {
|
||||
ctx_ = SSL_CTX_new(SSLv23_client_method());
|
||||
|
||||
detail::split(&host_[0], &host_[host_.size()], '.',
|
||||
[&](const char *b, const char *e) {
|
||||
host_components_.emplace_back(std::string(b, e));
|
||||
});
|
||||
if (client_cert != nullptr && client_key != nullptr) {
|
||||
if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
|
||||
SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
|
||||
SSL_CTX_free(ctx_);
|
||||
ctx_ = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline SSLClient::~SSLClient() {
|
||||
if (ctx_) { SSL_CTX_free(ctx_); }
|
||||
}
|
||||
|
@ -4705,6 +4759,10 @@ inline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path,
|
|||
if (ca_cert_dir_path) { ca_cert_dir_path_ = ca_cert_dir_path; }
|
||||
}
|
||||
|
||||
void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
|
||||
if (ca_cert_store) { ca_cert_store_ = ca_cert_store; }
|
||||
}
|
||||
|
||||
inline void SSLClient::enable_server_certificate_verification(bool enabled) {
|
||||
server_certificate_verification_ = enabled;
|
||||
}
|
||||
|
@ -4728,14 +4786,19 @@ inline bool SSLClient::process_and_close_socket(
|
|||
true, sock, request_count, read_timeout_sec_, read_timeout_usec_,
|
||||
ctx_, ctx_mutex_,
|
||||
[&](SSL *ssl) {
|
||||
if (ca_cert_file_path_.empty()) {
|
||||
if (ca_cert_file_path_.empty() && ca_cert_store_ == nullptr) {
|
||||
SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr);
|
||||
} else {
|
||||
} else if (!ca_cert_file_path_.empty()) {
|
||||
if (!SSL_CTX_load_verify_locations(
|
||||
ctx_, ca_cert_file_path_.c_str(), nullptr)) {
|
||||
return false;
|
||||
}
|
||||
SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr);
|
||||
} else if (ca_cert_store_ != nullptr) {
|
||||
if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store_) {
|
||||
SSL_CTX_set_cert_store(ctx_, ca_cert_store_);
|
||||
}
|
||||
SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr);
|
||||
}
|
||||
|
||||
if (SSL_connect(ssl) != 1) { return false; }
|
||||
|
|
74
test/test.cc
74
test/test.cc
|
@ -2530,6 +2530,80 @@ TEST(SSLClientServerTest, ClientCertPresent) {
|
|||
t.join();
|
||||
}
|
||||
|
||||
TEST(SSLClientServerTest, MemoryClientCertPresent) {
|
||||
X509 *server_cert;
|
||||
EVP_PKEY *server_private_key;
|
||||
X509_STORE *client_ca_cert_store;
|
||||
X509 *client_cert;
|
||||
EVP_PKEY *client_private_key;
|
||||
|
||||
FILE *f = fopen(SERVER_CERT_FILE, "r+");
|
||||
server_cert = PEM_read_X509(f, nullptr, nullptr, nullptr);
|
||||
fclose(f);
|
||||
|
||||
f = fopen(SERVER_PRIVATE_KEY_FILE, "r+");
|
||||
server_private_key = PEM_read_PrivateKey(f, nullptr, nullptr, nullptr);
|
||||
fclose(f);
|
||||
|
||||
f = fopen(CLIENT_CA_CERT_FILE, "r+");
|
||||
client_cert = PEM_read_X509(f, nullptr, nullptr, nullptr);
|
||||
client_ca_cert_store = X509_STORE_new();
|
||||
X509_STORE_add_cert(client_ca_cert_store, client_cert);
|
||||
X509_free(client_cert);
|
||||
fclose(f);
|
||||
|
||||
f = fopen(CLIENT_CERT_FILE, "r+");
|
||||
client_cert = PEM_read_X509(f, nullptr, nullptr, nullptr);
|
||||
fclose(f);
|
||||
|
||||
f = fopen(CLIENT_PRIVATE_KEY_FILE, "r+");
|
||||
client_private_key = PEM_read_PrivateKey(f, nullptr, nullptr, nullptr);
|
||||
fclose(f);
|
||||
|
||||
SSLServer svr(server_cert, server_private_key, client_ca_cert_store);
|
||||
ASSERT_TRUE(svr.is_valid());
|
||||
|
||||
svr.Get("/test", [&](const Request &req, Response &res) {
|
||||
res.set_content("test", "text/plain");
|
||||
svr.stop();
|
||||
ASSERT_TRUE(true);
|
||||
|
||||
auto peer_cert = SSL_get_peer_certificate(req.ssl);
|
||||
ASSERT_TRUE(peer_cert != nullptr);
|
||||
|
||||
auto subject_name = X509_get_subject_name(peer_cert);
|
||||
ASSERT_TRUE(subject_name != nullptr);
|
||||
|
||||
std::string common_name;
|
||||
{
|
||||
char name[BUFSIZ];
|
||||
auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
|
||||
name, sizeof(name));
|
||||
common_name.assign(name, static_cast<size_t>(name_len));
|
||||
}
|
||||
|
||||
EXPECT_EQ("Common Name", common_name);
|
||||
|
||||
X509_free(peer_cert);
|
||||
});
|
||||
|
||||
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
|
||||
httplib::SSLClient cli(HOST, PORT, client_cert, client_private_key);
|
||||
auto res = cli.Get("/test");
|
||||
cli.set_timeout_sec(30);
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
ASSERT_EQ(200, res->status);
|
||||
|
||||
X509_free(server_cert);
|
||||
EVP_PKEY_free(server_private_key);
|
||||
X509_free(client_cert);
|
||||
EVP_PKEY_free(client_private_key);
|
||||
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(SSLClientServerTest, ClientCertMissing) {
|
||||
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE,
|
||||
CLIENT_CA_CERT_DIR);
|
||||
|
|
Loading…
Reference in a new issue