yhirose 5 年之前
父节点
当前提交
c4f3f9529b
共有 2 个文件被更改,包括 91 次插入21 次删除
  1. 72 18
      httplib.h
  2. 19 3
      test/test.cc

+ 72 - 18
httplib.h

@@ -152,6 +152,8 @@ using ssize_t = int;
 
 
 #ifdef _MSC_VER
 #ifdef _MSC_VER
 #pragma comment(lib, "ws2_32.lib")
 #pragma comment(lib, "ws2_32.lib")
+#pragma comment(lib, "crypt32.lib")
+#pragma comment(lib, "cryptui.lib")
 #endif
 #endif
 
 
 #ifndef strcasecmp
 #ifndef strcasecmp
@@ -1062,6 +1064,8 @@ private:
   bool connect_with_proxy(Socket &sock, Response &res, bool &success);
   bool connect_with_proxy(Socket &sock, Response &res, bool &success);
   bool initialize_ssl(Socket &socket);
   bool initialize_ssl(Socket &socket);
 
 
+  bool load_certs();
+
   bool verify_host(X509 *server_cert) const;
   bool verify_host(X509 *server_cert) const;
   bool verify_host_with_subject_alt_name(X509 *server_cert) const;
   bool verify_host_with_subject_alt_name(X509 *server_cert) const;
   bool verify_host_with_common_name(X509 *server_cert) const;
   bool verify_host_with_common_name(X509 *server_cert) const;
@@ -1069,12 +1073,14 @@ private:
 
 
   SSL_CTX *ctx_;
   SSL_CTX *ctx_;
   std::mutex ctx_mutex_;
   std::mutex ctx_mutex_;
+  std::once_flag initialize_cert_;
+
   std::vector<std::string> host_components_;
   std::vector<std::string> host_components_;
 
 
   std::string ca_cert_file_path_;
   std::string ca_cert_file_path_;
   std::string ca_cert_dir_path_;
   std::string ca_cert_dir_path_;
   X509_STORE *ca_cert_store_ = nullptr;
   X509_STORE *ca_cert_store_ = nullptr;
-  bool server_certificate_verification_ = false;
+  bool server_certificate_verification_ = true;
   long verify_result_ = 0;
   long verify_result_ = 0;
 
 
   friend class Client;
   friend class Client;
@@ -1313,9 +1319,7 @@ public:
 
 
   void stop() { cli_->stop(); }
   void stop() { cli_->stop(); }
 
 
-  void set_tcp_nodelay(bool on) {
-    cli_->set_tcp_nodelay(on);
-  }
+  void set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
 
 
   void set_socket_options(SocketOptions socket_options) {
   void set_socket_options(SocketOptions socket_options) {
     cli_->set_socket_options(socket_options);
     cli_->set_socket_options(socket_options);
@@ -2776,7 +2780,7 @@ inline std::string params_to_query_str(const Params &params) {
     if (it != params.begin()) { query += "&"; }
     if (it != params.begin()) { query += "&"; }
     query += it->first;
     query += it->first;
     query += "=";
     query += "=";
-    query += detail::encode_url(it->second);
+    query += encode_url(it->second);
   }
   }
 
 
   return query;
   return query;
@@ -3223,6 +3227,33 @@ inline std::string SHA_512(const std::string &s) {
 #endif
 #endif
 
 
 #ifdef _WIN32
 #ifdef _WIN32
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+// NOTE: This code came up with the following stackoverflow post:
+// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
+inline bool load_system_certs_on_windows(X509_STORE *store) {
+  auto hStore = CertOpenSystemStore((HCRYPTPROV_LEGACY)NULL, L"ROOT");
+
+  if (!hStore) { return false; }
+
+  PCCERT_CONTEXT pContext = NULL;
+  while (pContext = CertEnumCertificatesInStore(hStore, pContext)) {
+    auto encoded_cert =
+        static_cast<const unsigned char *>(pContext->pbCertEncoded);
+
+    auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
+    if (x509) {
+      X509_STORE_add_cert(store, x509);
+      X509_free(x509);
+    }
+  }
+
+  CertFreeCertificateContext(pContext);
+  CertCloseStore(hStore, 0);
+
+  return true;
+}
+#endif
+
 class WSInit {
 class WSInit {
 public:
 public:
   WSInit() {
   WSInit() {
@@ -5544,23 +5575,44 @@ inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
   return true;
   return true;
 }
 }
 
 
+inline bool SSLClient::load_certs() {
+  bool ret = true;
+
+  std::call_once(initialize_cert_, [&]() {
+    std::lock_guard<std::mutex> guard(ctx_mutex_);
+    if (!ca_cert_file_path_.empty()) {
+      if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
+                                         nullptr)) {
+        ret = false;
+      }
+    } else if (!ca_cert_dir_path_.empty()) {
+      if (!SSL_CTX_load_verify_locations(ctx_, nullptr,
+                                         ca_cert_dir_path_.c_str())) {
+        ret = false;
+      }
+    } 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_);
+      }
+    } else {
+#ifdef _WIN32
+      detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
+#else
+      SSL_CTX_set_default_verify_paths(ctx_);
+#endif
+    }
+  });
+
+  return ret;
+}
+
 inline bool SSLClient::initialize_ssl(Socket &socket) {
 inline bool SSLClient::initialize_ssl(Socket &socket) {
   auto ssl = detail::ssl_new(
   auto ssl = detail::ssl_new(
       socket.sock, ctx_, ctx_mutex_,
       socket.sock, ctx_, ctx_mutex_,
       [&](SSL *ssl) {
       [&](SSL *ssl) {
-        if (ca_cert_file_path_.empty() && ca_cert_store_ == nullptr) {
-          SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr);
-        } 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 (server_certificate_verification_) {
+          if (!load_certs()) { return false; }
+          SSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr);
         }
         }
 
 
         if (SSL_connect(ssl) != 1) { return false; }
         if (SSL_connect(ssl) != 1) { return false; }
@@ -5750,3 +5802,5 @@ inline bool SSLClient::check_host_name(const char *pattern,
 } // namespace httplib
 } // namespace httplib
 
 
 #endif // CPPHTTPLIB_HTTPLIB_H
 #endif // CPPHTTPLIB_HTTPLIB_H
+
+

+ 19 - 3
test/test.cc

@@ -765,6 +765,9 @@ protected:
         svr_(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE)
         svr_(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE)
 #endif
 #endif
   {
   {
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+    cli_.enable_server_certificate_verification(false);
+#endif
   }
   }
 
 
   virtual void SetUp() {
   virtual void SetUp() {
@@ -2627,6 +2630,9 @@ protected:
         svr_(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE)
         svr_(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE)
 #endif
 #endif
   {
   {
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+    cli_.enable_server_certificate_verification(false);
+#endif
   }
   }
 
 
   virtual void SetUp() {
   virtual void SetUp() {
@@ -2704,6 +2710,9 @@ protected:
         svr_(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE)
         svr_(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE)
 #endif
 #endif
   {
   {
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+    cli_.enable_server_certificate_verification(false);
+#endif
   }
   }
 
 
   virtual void SetUp() {
   virtual void SetUp() {
@@ -2763,6 +2772,7 @@ TEST(SSLClientTest, ServerCertificateVerification1) {
 TEST(SSLClientTest, ServerCertificateVerification2) {
 TEST(SSLClientTest, ServerCertificateVerification2) {
   SSLClient cli("google.com");
   SSLClient cli("google.com");
   cli.enable_server_certificate_verification(true);
   cli.enable_server_certificate_verification(true);
+  cli.set_ca_cert_path("hello");
   auto res = cli.Get("/");
   auto res = cli.Get("/");
   ASSERT_TRUE(res == nullptr);
   ASSERT_TRUE(res == nullptr);
 }
 }
@@ -2819,8 +2829,10 @@ TEST(SSLClientServerTest, ClientCertPresent) {
   std::this_thread::sleep_for(std::chrono::milliseconds(1));
   std::this_thread::sleep_for(std::chrono::milliseconds(1));
 
 
   httplib::SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
   httplib::SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
-  auto res = cli.Get("/test");
+  cli.enable_server_certificate_verification(false);
   cli.set_connection_timeout(30);
   cli.set_connection_timeout(30);
+
+  auto res = cli.Get("/test");
   ASSERT_TRUE(res != nullptr);
   ASSERT_TRUE(res != nullptr);
   ASSERT_EQ(200, res->status);
   ASSERT_EQ(200, res->status);
 
 
@@ -2888,8 +2900,10 @@ TEST(SSLClientServerTest, MemoryClientCertPresent) {
   std::this_thread::sleep_for(std::chrono::milliseconds(1));
   std::this_thread::sleep_for(std::chrono::milliseconds(1));
 
 
   httplib::SSLClient cli(HOST, PORT, client_cert, client_private_key);
   httplib::SSLClient cli(HOST, PORT, client_cert, client_private_key);
-  auto res = cli.Get("/test");
+  cli.enable_server_certificate_verification(false);
   cli.set_connection_timeout(30);
   cli.set_connection_timeout(30);
+
+  auto res = cli.Get("/test");
   ASSERT_TRUE(res != nullptr);
   ASSERT_TRUE(res != nullptr);
   ASSERT_EQ(200, res->status);
   ASSERT_EQ(200, res->status);
 
 
@@ -2934,8 +2948,10 @@ TEST(SSLClientServerTest, TrustDirOptional) {
   std::this_thread::sleep_for(std::chrono::milliseconds(1));
   std::this_thread::sleep_for(std::chrono::milliseconds(1));
 
 
   httplib::SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
   httplib::SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
-  auto res = cli.Get("/test");
+  cli.enable_server_certificate_verification(false);
   cli.set_connection_timeout(30);
   cli.set_connection_timeout(30);
+
+  auto res = cli.Get("/test");
   ASSERT_TRUE(res != nullptr);
   ASSERT_TRUE(res != nullptr);
   ASSERT_EQ(200, res->status);
   ASSERT_EQ(200, res->status);