You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by ma...@apache.org on 2019/02/27 23:36:09 UTC

[trafficserver] branch master updated: Cleanup: Loading certs by SSLMultiCertConfigLoader

This is an automated email from the ASF dual-hosted git repository.

masaori pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new aecfd52  Cleanup: Loading certs by SSLMultiCertConfigLoader
aecfd52 is described below

commit aecfd526d784d3469cdb0437067ba0e09a5a26d0
Author: Masaori Koshiba <ma...@apache.org>
AuthorDate: Wed Feb 20 10:33:03 2019 +0900

    Cleanup: Loading certs by SSLMultiCertConfigLoader
---
 iocore/net/P_SSLCertLookup.h |   3 +-
 iocore/net/P_SSLConfig.h     |  10 +-
 iocore/net/P_SSLUtils.h      |  75 ++++++--
 iocore/net/SSLConfig.cc      |   3 +-
 iocore/net/SSLStats.cc       |   4 +-
 iocore/net/SSLUtils.cc       | 428 +++++++++++++++++++++++--------------------
 6 files changed, 301 insertions(+), 222 deletions(-)

diff --git a/iocore/net/P_SSLCertLookup.h b/iocore/net/P_SSLCertLookup.h
index 65d278a..475d05d 100644
--- a/iocore/net/P_SSLCertLookup.h
+++ b/iocore/net/P_SSLCertLookup.h
@@ -23,8 +23,9 @@
 
 #pragma once
 
+#include <openssl/ssl.h>
+
 #include "ProxyConfig.h"
-#include "P_SSLUtils.h"
 
 struct SSLConfigParams;
 struct SSLContextStorage;
diff --git a/iocore/net/P_SSLConfig.h b/iocore/net/P_SSLConfig.h
index 14b7b9f..39058b6 100644
--- a/iocore/net/P_SSLConfig.h
+++ b/iocore/net/P_SSLConfig.h
@@ -30,13 +30,15 @@
  ****************************************************************************/
 #pragma once
 
+#include <openssl/rand.h>
+
+#include "tscore/ink_inet.h"
+#include "tscore/IpMap.h"
+
 #include "ProxyConfig.h"
+
 #include "SSLSessionCache.h"
-#include "tscore/ink_inet.h"
-#include <openssl/rand.h>
-#include "P_SSLCertLookup.h"
 #include "YamlSNIConfig.h"
-#include <tscore/IpMap.h>
 
 struct SSLCertLookup;
 struct ssl_ticket_key_block;
diff --git a/iocore/net/P_SSLUtils.h b/iocore/net/P_SSLUtils.h
index a61d8bc..3e3de60 100644
--- a/iocore/net/P_SSLUtils.h
+++ b/iocore/net/P_SSLUtils.h
@@ -21,10 +21,6 @@
 
 #pragma once
 
-#include "tscore/ink_config.h"
-#include "tscore/Diags.h"
-#include "P_SSLClientUtils.h"
-
 #define OPENSSL_THREAD_DEFINES
 
 // BoringSSL does not have this include file
@@ -33,37 +29,88 @@
 #endif
 #include <openssl/ssl.h>
 
+#include "tscore/ink_config.h"
+#include "tscore/Diags.h"
+
+#include "P_SSLClientUtils.h"
+#include "P_SSLCertLookup.h"
+
 struct SSLConfigParams;
-struct SSLCertLookup;
 class SSLNetVConnection;
 
 typedef int ssl_error_t;
 
-// Create a default SSL server context.
-SSL_CTX *SSLDefaultServerContext();
+/**
+   @brief Gather user provided settings from ssl_multicert.config in to this single struct
+ */
+struct SSLMultiCertConfigParams {
+  SSLMultiCertConfigParams() : opt(SSLCertContext::OPT_NONE)
+  {
+    REC_ReadConfigInt32(session_ticket_enabled, "proxy.config.ssl.server.session_ticket.enable");
+  }
+
+  int session_ticket_enabled; ///< session ticket enabled
+  ats_scoped_str addr;        ///< IPv[64] address to match
+  ats_scoped_str cert;        ///< certificate
+  ats_scoped_str first_cert;  ///< the first certificate name when multiple cert files are in 'ssl_cert_name'
+  ats_scoped_str ca;          ///< CA public certificate
+  ats_scoped_str key;         ///< Private key
+  ats_scoped_str dialog;      ///< Private key dialog
+  ats_scoped_str servername;  ///< Destination server
+  SSLCertContext::Option opt; ///< SSLCertContext special handling option
+};
+
+/**
+    @brief Load SSL certificates from ssl_multicert.config and setup SSLCertLookup for SSLCertificateConfig
+ */
+class SSLMultiCertConfigLoader
+{
+public:
+  SSLMultiCertConfigLoader(const SSLConfigParams *p) : _params(p) {}
+
+  bool load(SSLCertLookup *lookup);
+
+  virtual SSL_CTX *default_server_ssl_ctx();
+  virtual SSL_CTX *init_server_ssl_ctx(std::vector<X509 *> &certList, const SSLMultiCertConfigParams *sslMultCertSettings);
+
+  static bool load_certs(SSL_CTX *ctx, std::vector<X509 *> &certList, const SSLConfigParams *params,
+                         const SSLMultiCertConfigParams *ssl_multi_cert_params);
+  static bool set_session_id_context(SSL_CTX *ctx, const SSLConfigParams *params,
+                                     const SSLMultiCertConfigParams *sslMultCertSettings);
+
+  static bool index_certificate(SSLCertLookup *lookup, SSLCertContext const &cc, X509 *cert, const char *certname);
+  static int check_server_cert_now(X509 *cert, const char *certname);
+  static void clear_pw_references(SSL_CTX *ssl_ctx);
+
+protected:
+  const SSLConfigParams *_params;
+
+private:
+  virtual SSL_CTX *_store_ssl_ctx(SSLCertLookup *lookup, const SSLMultiCertConfigParams *ssl_multi_cert_params);
+  virtual void _set_handshake_callbacks(SSL_CTX *ctx);
+};
 
 // Create a new SSL server context fully configured.
+// Used by TS API (TSSslServerContextCreate)
 SSL_CTX *SSLCreateServerContext(const SSLConfigParams *params);
 
+// Release SSL_CTX and the associated data. This works for both
+// client and server contexts and gracefully accepts nullptr.
+// Used by TS API (TSSslContextDestroy)
+void SSLReleaseContext(SSL_CTX *ctx);
+
 // Initialize the SSL library.
 void SSLInitializeLibrary();
 
 // Initialize SSL library based on configuration settings
 void SSLPostConfigInitialize();
 
-// Release SSL_CTX and the associated data. This works for both
-// client and server contexts and gracefully accepts nullptr.
-void SSLReleaseContext(SSL_CTX *ctx);
-
 // Wrapper functions to SSL I/O routines
 ssl_error_t SSLWriteBuffer(SSL *ssl, const void *buf, int64_t nbytes, int64_t &nwritten);
 ssl_error_t SSLReadBuffer(SSL *ssl, void *buf, int64_t nbytes, int64_t &nread);
 ssl_error_t SSLAccept(SSL *ssl);
 ssl_error_t SSLConnect(SSL *ssl);
 
-// Load the SSL certificate configuration.
-bool SSLParseCertificateConfiguration(const SSLConfigParams *params, SSLCertLookup *lookup);
-
 // Attach a SSL NetVC back pointer to a SSL session.
 void SSLNetVCAttach(SSL *ssl, SSLNetVConnection *vc);
 
diff --git a/iocore/net/SSLConfig.cc b/iocore/net/SSLConfig.cc
index d822ce4..51855b4 100644
--- a/iocore/net/SSLConfig.cc
+++ b/iocore/net/SSLConfig.cc
@@ -518,7 +518,8 @@ SSLCertificateConfig::reconfigure()
     ink_hrtime_sleep(HRTIME_SECONDS(secs));
   }
 
-  SSLParseCertificateConfiguration(params, lookup);
+  SSLMultiCertConfigLoader loader(params);
+  loader.load(lookup);
 
   if (!lookup->is_valid) {
     retStatus = false;
diff --git a/iocore/net/SSLStats.cc b/iocore/net/SSLStats.cc
index c748b94..b15e40d 100644
--- a/iocore/net/SSLStats.cc
+++ b/iocore/net/SSLStats.cc
@@ -26,6 +26,7 @@
 #include <openssl/err.h>
 
 #include "P_SSLConfig.h"
+#include "P_SSLUtils.h"
 
 RecRawStatBlock *ssl_rsb = nullptr;
 std::unordered_map<std::string, intptr_t> cipher_map;
@@ -206,7 +207,8 @@ SSLInitializeStatistics()
   // filtered by proxy.config.ssl.server.cipher_suite. This keeps the set of cipher suites stable across
   // configuration reloads and works for the case where we honor the client cipher preference.
 
-  ctx     = SSLDefaultServerContext();
+  SSLMultiCertConfigLoader loader(nullptr);
+  ctx     = loader.default_server_ssl_ctx();
   ssl     = SSL_new(ctx);
   ciphers = SSL_get_ciphers(ssl);
 
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index 1e48bf2..20bd0ea 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -95,36 +95,6 @@ static constexpr char SSL_CERT_SEPARATE_DELIM = ',';
 #endif
 #endif
 
-/*
- * struct ssl_user_config: gather user provided settings from ssl_multicert.config in to this single struct
- * ssl_ticket_enabled - session ticket enabled
- * ssl_cert_name - certificate
- * dest_ip - IPv[64] address to match
- * ssl_cert_name - certificate
- * first_cert - the first certificate name when multiple cert files are in 'ssl_cert_name'
- * ssl_ca_name - CA public certificate
- * ssl_key_name - Private key
- * ticket_key_name - session key file. [key_name (16Byte) + HMAC_secret (16Byte) + AES_key (16Byte)]
- * ssl_key_dialog - Private key dialog
- * servername - Destination server
- */
-struct ssl_user_config {
-  ssl_user_config() : opt(SSLCertContext::OPT_NONE)
-  {
-    REC_ReadConfigInt32(session_ticket_enabled, "proxy.config.ssl.server.session_ticket.enable");
-  }
-
-  int session_ticket_enabled;
-  ats_scoped_str addr;
-  ats_scoped_str cert;
-  ats_scoped_str first_cert;
-  ats_scoped_str ca;
-  ats_scoped_str key;
-  ats_scoped_str dialog;
-  ats_scoped_str servername;
-  SSLCertContext::Option opt;
-};
-
 SSLSessionCache *session_cache; // declared extern in P_SSLConfig.h
 
 #if TS_HAVE_OPENSSL_SESSION_TICKETS
@@ -1022,7 +992,7 @@ SSLInitializeLibrary()
 }
 
 SSL_CTX *
-SSLDefaultServerContext()
+SSLMultiCertConfigLoader::default_server_ssl_ctx()
 {
   return SSL_CTX_new(SSLv23_server_method());
 }
@@ -1064,19 +1034,22 @@ SSLPrivateKeyHandler(SSL_CTX *ctx, const SSLConfigParams *params, const std::str
   return true;
 }
 
-static int
-SSLCheckServerCertNow(X509 *cert, const char *certname)
+/**
+   returns 0 on OK or negative value on failure and update log as appropriate.
+
+   Will check:
+   - if file exists, and has read permissions
+   - for truncation or other PEM read fail
+   - current time is between notBefore and notAfter dates of certificate
+   if anything is not kosher, a negative value is returned and appropriate error logged.
+
+   @static
+ */
+int
+SSLMultiCertConfigLoader::check_server_cert_now(X509 *cert, const char *certname)
 {
   int timeCmpValue;
 
-  // SSLCheckServerCertNow() -  returns 0 on OK or negative value on failure
-  // and update log as appropriate.
-  // Will check:
-  // - if file exists, and has read permissions
-  // - for truncation or other PEM read fail
-  // - current time is between notBefore and notAfter dates of certificate
-  // if anything is not kosher, a negative value is returned and appropriate error logged.
-
   if (!cert) {
     // a truncated certificate would fall into here
     Error("invalid certificate %s: file is truncated or corrupted", certname);
@@ -1122,11 +1095,14 @@ asn1_strdup(ASN1_STRING *s)
   return ats_strndup((const char *)ASN1_STRING_get0_data(s), ASN1_STRING_length(s));
 }
 
-// Given a certificate and it's corresponding SSL_CTX context, insert hash
-// table aliases for subject CN and subjectAltNames DNS without wildcard,
-// insert trie aliases for those with wildcard.
-static bool
-ssl_index_certificate(SSLCertLookup *lookup, SSLCertContext const &cc, X509 *cert, const char *certname)
+/**
+   Given a certificate and it's corresponding SSL_CTX context, insert hash
+   table aliases for subject CN and subjectAltNames DNS without wildcard,
+   insert trie aliases for those with wildcard.
+   @static
+*/
+bool
+SSLMultiCertConfigLoader::index_certificate(SSLCertLookup *lookup, SSLCertContext const &cc, X509 *cert, const char *certname)
 {
   X509_NAME *subject = nullptr;
   bool inserted      = false;
@@ -1232,8 +1208,8 @@ ssl_callback_info(const SSL *ssl, int where, int ret)
   }
 }
 
-static void
-ssl_set_handshake_callbacks(SSL_CTX *ctx)
+void
+SSLMultiCertConfigLoader::_set_handshake_callbacks(SSL_CTX *ctx)
 {
 // Make sure the callbacks are set
 #if TS_USE_CERT_CB
@@ -1275,16 +1251,17 @@ setClientCertLevel(SSL *ssl, uint8_t certLevel)
   SSL_set_verify_depth(ssl, params->verify_depth); // might want to make configurable at some point.
 }
 
+/**
+   Initialize SSL_CTX for server
+   This is public function because of used by SSLCreateServerContext.
+ */
 SSL_CTX *
-SSLInitServerContext(const SSLConfigParams *params, const ssl_user_config *sslMultCertSettings, std::vector<X509 *> &cert_list)
+SSLMultiCertConfigLoader::init_server_ssl_ctx(std::vector<X509 *> &cert_list, const SSLMultiCertConfigParams *sslMultCertSettings)
 {
+  const SSLConfigParams *params = this->_params;
+
   int server_verify_client;
-  SSL_CTX *ctx                 = SSLDefaultServerContext();
-  EVP_MD_CTX *digest           = EVP_MD_CTX_new();
-  STACK_OF(X509_NAME) *ca_list = nullptr;
-  unsigned char hash_buf[EVP_MAX_MD_SIZE];
-  unsigned int hash_len    = 0;
-  const char *setting_cert = sslMultCertSettings ? sslMultCertSettings->cert.get() : nullptr;
+  SSL_CTX *ctx = this->default_server_ssl_ctx();
 
   // disable selected protocols
   SSL_CTX_set_options(ctx, params->ssl_ctx_options);
@@ -1366,84 +1343,9 @@ SSLInitServerContext(const SSLConfigParams *params, const ssl_user_config *sslMu
     }
 
     if (sslMultCertSettings->cert) {
-      SimpleTokenizer cert_tok((const char *)sslMultCertSettings->cert, SSL_CERT_SEPARATE_DELIM);
-      SimpleTokenizer key_tok((sslMultCertSettings->key ? (const char *)sslMultCertSettings->key : ""), SSL_CERT_SEPARATE_DELIM);
-
-      if (sslMultCertSettings->key && cert_tok.getNumTokensRemaining() != key_tok.getNumTokensRemaining()) {
-        Error("the number of certificates in ssl_cert_name and ssl_key_name doesn't match");
+      if (!SSLMultiCertConfigLoader::load_certs(ctx, cert_list, params, sslMultCertSettings)) {
         goto fail;
       }
-      SimpleTokenizer ca_tok("", SSL_CERT_SEPARATE_DELIM);
-      if (sslMultCertSettings->ca) {
-        ca_tok.setString(sslMultCertSettings->ca);
-        if (cert_tok.getNumTokensRemaining() != ca_tok.getNumTokensRemaining()) {
-          Error("the number of certificates in ssl_cert_name and ssl_ca_name doesn't match");
-          goto fail;
-        }
-      }
-
-      for (const char *certname = cert_tok.getNext(); certname; certname = cert_tok.getNext()) {
-        std::string completeServerCertPath = Layout::relative_to(params->serverCertPathOnly, certname);
-        scoped_BIO bio(BIO_new_file(completeServerCertPath.c_str(), "r"));
-        X509 *cert = nullptr;
-        if (bio) {
-          cert = PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr);
-        }
-        if (!bio || !cert) {
-          SSLError("failed to load certificate chain from %s", completeServerCertPath.c_str());
-          goto fail;
-        }
-        if (!SSL_CTX_use_certificate(ctx, cert)) {
-          SSLError("Failed to assign cert from %s to SSL_CTX", completeServerCertPath.c_str());
-          X509_free(cert);
-          goto fail;
-        }
-
-        // Load up any additional chain certificates
-        SSL_CTX_add_extra_chain_cert_bio(ctx, bio);
-
-        const char *keyPath = key_tok.getNext();
-        if (!SSLPrivateKeyHandler(ctx, params, completeServerCertPath, keyPath)) {
-          goto fail;
-        }
-
-        cert_list.push_back(cert);
-        if (SSLConfigParams::load_ssl_file_cb) {
-          SSLConfigParams::load_ssl_file_cb(completeServerCertPath.c_str(), CONFIG_FLAG_UNVERSIONED);
-        }
-
-        // Must load all the intermediate certificates before starting the next chain
-
-        // First, load any CA chains from the global chain file.  This should probably
-        // eventually be a comma separated list too.  For now we will load it in all chains even
-        // though it only makes sense in one chain
-        if (params->serverCertChainFilename) {
-          ats_scoped_str completeServerCertChainPath(
-            Layout::relative_to(params->serverCertPathOnly, params->serverCertChainFilename));
-          if (!SSL_CTX_add_extra_chain_cert_file(ctx, completeServerCertChainPath)) {
-            SSLError("failed to load global certificate chain from %s", (const char *)completeServerCertChainPath);
-            goto fail;
-          }
-          if (SSLConfigParams::load_ssl_file_cb) {
-            SSLConfigParams::load_ssl_file_cb(completeServerCertChainPath, CONFIG_FLAG_UNVERSIONED);
-          }
-        }
-
-        // Now, load any additional certificate chains specified in this entry.
-        if (sslMultCertSettings->ca) {
-          const char *ca_name = ca_tok.getNext();
-          if (ca_name != nullptr) {
-            ats_scoped_str completeServerCertChainPath(Layout::relative_to(params->serverCertPathOnly, ca_name));
-            if (!SSL_CTX_add_extra_chain_cert_file(ctx, completeServerCertChainPath)) {
-              SSLError("failed to load certificate chain from %s", (const char *)completeServerCertChainPath);
-              goto fail;
-            }
-            if (SSLConfigParams::load_ssl_file_cb) {
-              SSLConfigParams::load_ssl_file_cb(completeServerCertChainPath, CONFIG_FLAG_UNVERSIONED);
-            }
-          }
-        }
-      }
     }
 
     // SSL_CTX_load_verify_locations() builds the cert chain from the
@@ -1497,50 +1399,7 @@ SSLInitServerContext(const SSLConfigParams *params, const ssl_user_config *sslMu
     SSL_CTX_set_verify_depth(ctx, params->verify_depth); // might want to make configurable at some point.
   }
 
-  // Set the list of CA's to send to client if we ask for a client
-  // certificate
-  if (params->serverCACertFilename) {
-    ca_list = SSL_load_client_CA_file(params->serverCACertFilename);
-    if (ca_list) {
-      SSL_CTX_set_client_CA_list(ctx, ca_list);
-    }
-  }
-
-  if (EVP_DigestInit_ex(digest, evp_md_func, nullptr) == 0) {
-    SSLError("EVP_DigestInit_ex failed");
-    goto fail;
-  }
-
-  if (nullptr != setting_cert) {
-    Debug("ssl", "Using '%s' in hash for session id context", sslMultCertSettings->cert.get());
-    if (EVP_DigestUpdate(digest, sslMultCertSettings->cert, strlen(setting_cert)) == 0) {
-      SSLError("EVP_DigestUpdate failed");
-      goto fail;
-    }
-  }
-
-  if (ca_list != nullptr) {
-    size_t num_certs = sk_X509_NAME_num(ca_list);
-
-    for (size_t i = 0; i < num_certs; i++) {
-      X509_NAME *name = sk_X509_NAME_value(ca_list, i);
-      if (X509_NAME_digest(name, evp_md_func, hash_buf /* borrow our final hash buffer. */, &hash_len) == 0 ||
-          EVP_DigestUpdate(digest, hash_buf, hash_len) == 0) {
-        SSLError("Adding X509 name to digest failed");
-        goto fail;
-      }
-    }
-  }
-
-  if (EVP_DigestFinal_ex(digest, hash_buf, &hash_len) == 0) {
-    SSLError("EVP_DigestFinal_ex failed");
-    goto fail;
-  }
-  EVP_MD_CTX_free(digest);
-  digest = nullptr;
-
-  if (SSL_CTX_set_session_id_context(ctx, hash_buf, hash_len) == 0) {
-    SSLError("SSL_CTX_set_session_id_context failed");
+  if (!SSLMultiCertConfigLoader::set_session_id_context(ctx, params, sslMultCertSettings)) {
     goto fail;
   }
 
@@ -1574,13 +1433,9 @@ SSLInitServerContext(const SSLConfigParams *params, const ssl_user_config *sslMu
   }
 
   ssl_context_enable_ecdh(ctx);
-#define SSL_CLEAR_PW_REFERENCES(CTX)                      \
-  {                                                       \
-    SSL_CTX_set_default_passwd_cb(CTX, nullptr);          \
-    SSL_CTX_set_default_passwd_cb_userdata(CTX, nullptr); \
-  }
+
   if (sslMultCertSettings && sslMultCertSettings->dialog) {
-    SSL_CLEAR_PW_REFERENCES(ctx);
+    SSLMultiCertConfigLoader::clear_pw_references(ctx);
   }
   SSL_CTX_set_info_callback(ctx, ssl_callback_info);
 
@@ -1596,10 +1451,11 @@ SSLInitServerContext(const SSLConfigParams *params, const ssl_user_config *sslMu
   if (SSLConfigParams::ssl_ocsp_enabled) {
     Debug("ssl", "SSL OCSP Stapling is enabled");
     SSL_CTX_set_tlsext_status_cb(ctx, ssl_callback_ocsp_stapling);
+    const char *cert_name = sslMultCertSettings ? sslMultCertSettings->cert.get() : nullptr;
 
     for (auto cert : cert_list) {
-      if (!ssl_stapling_init_cert(ctx, cert, setting_cert)) {
-        Warning("failed to configure SSL_CTX for OCSP Stapling info for certificate at %s", setting_cert);
+      if (!ssl_stapling_init_cert(ctx, cert, cert_name)) {
+        Warning("failed to configure SSL_CTX for OCSP Stapling info for certificate at %s", cert_name);
       }
     }
   } else {
@@ -1614,13 +1470,11 @@ SSLInitServerContext(const SSLConfigParams *params, const ssl_user_config *sslMu
   if (SSLConfigParams::init_ssl_ctx_cb) {
     SSLConfigParams::init_ssl_ctx_cb(ctx, true);
   }
+
   return ctx;
 
 fail:
-  if (digest) {
-    EVP_MD_CTX_free(digest);
-  }
-  SSL_CLEAR_PW_REFERENCES(ctx)
+  SSLMultiCertConfigLoader::clear_pw_references(ctx);
   SSLReleaseContext(ctx);
   for (auto cert : cert_list) {
     X509_free(cert);
@@ -1632,21 +1486,24 @@ fail:
 SSL_CTX *
 SSLCreateServerContext(const SSLConfigParams *params)
 {
+  SSLMultiCertConfigLoader loader(params);
   std::vector<X509 *> cert_list;
-  SSL_CTX *ctx = SSLInitServerContext(params, nullptr, cert_list);
+
+  SSL_CTX *ctx = loader.init_server_ssl_ctx(cert_list, nullptr);
   ink_assert(cert_list.empty());
+
   return ctx;
 }
 
 /**
    Insert SSLCertContext (SSL_CTX ans options) into SSLCertLookup with key.
-   Do NOT call SSL_CTX_set_* functions from here. SSL_CTX should be set up by SSLInitServerContext().
+   Do NOT call SSL_CTX_set_* functions from here. SSL_CTX should be set up by SSLMultiCertConfigLoader::init_server_ssl_ctx().
  */
-static SSL_CTX *
-ssl_store_ssl_context(const SSLConfigParams *params, SSLCertLookup *lookup, const ssl_user_config *sslMultCertSettings)
+SSL_CTX *
+SSLMultiCertConfigLoader::_store_ssl_ctx(SSLCertLookup *lookup, const SSLMultiCertConfigParams *sslMultCertSettings)
 {
   std::vector<X509 *> cert_list;
-  SSL_CTX *ctx                   = SSLInitServerContext(params, sslMultCertSettings, cert_list);
+  SSL_CTX *ctx                   = this->init_server_ssl_ctx(cert_list, sslMultCertSettings);
   ssl_ticket_key_block *keyblock = nullptr;
   bool inserted                  = false;
 
@@ -1657,7 +1514,7 @@ ssl_store_ssl_context(const SSLConfigParams *params, SSLCertLookup *lookup, cons
 
   const char *certname = sslMultCertSettings->cert.get();
   for (auto cert : cert_list) {
-    if (0 > SSLCheckServerCertNow(cert, certname)) {
+    if (0 > SSLMultiCertConfigLoader::check_server_cert_now(cert, certname)) {
       /* At this point, we know cert is bad, and we've already printed a
          descriptive reason as to why cert is bad to the log file */
       Debug("ssl", "Marking certificate as NOT VALID: %s", certname);
@@ -1676,7 +1533,7 @@ ssl_store_ssl_context(const SSLConfigParams *params, SSLCertLookup *lookup, cons
       if (lookup->insert(sslMultCertSettings->addr, SSLCertContext(ctx, sslMultCertSettings->opt, keyblock)) >= 0) {
         inserted            = true;
         lookup->ssl_default = ctx;
-        ssl_set_handshake_callbacks(ctx);
+        this->_set_handshake_callbacks(ctx);
       }
     } else {
       IpEndpoint ep;
@@ -1705,7 +1562,7 @@ ssl_store_ssl_context(const SSLConfigParams *params, SSLCertLookup *lookup, cons
   // refcounting or alternate way of avoiding double frees.
   Debug("ssl", "importing SNI names from %s", (const char *)certname);
   for (auto cert : cert_list) {
-    if (ssl_index_certificate(lookup, SSLCertContext(ctx, sslMultCertSettings->opt), cert, certname)) {
+    if (SSLMultiCertConfigLoader::index_certificate(lookup, SSLCertContext(ctx, sslMultCertSettings->opt), cert, certname)) {
       inserted = true;
     }
   }
@@ -1729,7 +1586,7 @@ ssl_store_ssl_context(const SSLConfigParams *params, SSLCertLookup *lookup, cons
 }
 
 static bool
-ssl_extract_certificate(const matcher_line *line_info, ssl_user_config &sslMultCertSettings)
+ssl_extract_certificate(const matcher_line *line_info, SSLMultiCertConfigParams &sslMultCertSettings)
 {
   for (int i = 0; i < MATCHER_MAX_TOKENS; ++i) {
     const char *label;
@@ -1792,8 +1649,10 @@ ssl_extract_certificate(const matcher_line *line_info, ssl_user_config &sslMultC
 }
 
 bool
-SSLParseCertificateConfiguration(const SSLConfigParams *params, SSLCertLookup *lookup)
+SSLMultiCertConfigLoader::load(SSLCertLookup *lookup)
 {
+  const SSLConfigParams *params = this->_params;
+
   char *tok_state = nullptr;
   char *line      = nullptr;
   ats_scoped_str file_buf;
@@ -1829,7 +1688,7 @@ SSLParseCertificateConfiguration(const SSLConfigParams *params, SSLCertLookup *l
     }
 
     if (*line != '\0' && *line != '#') {
-      ssl_user_config sslMultiCertSettings;
+      SSLMultiCertConfigParams sslMultiCertSettings;
       const char *errPtr;
 
       errPtr = parseConfigLine(line, &line_info, &sslCertTags);
@@ -1841,7 +1700,7 @@ SSLParseCertificateConfiguration(const SSLConfigParams *params, SSLCertLookup *l
         if (ssl_extract_certificate(&line_info, sslMultiCertSettings)) {
           // There must be a certificate specified unless the tunnel action is set
           if (sslMultiCertSettings.cert || sslMultiCertSettings.opt != SSLCertContext::OPT_TUNNEL) {
-            ssl_store_ssl_context(params, lookup, &sslMultiCertSettings);
+            this->_store_ssl_ctx(lookup, &sslMultiCertSettings);
           } else {
             Warning("No ssl_cert_name specified and no tunnel action set");
           }
@@ -1856,9 +1715,9 @@ SSLParseCertificateConfiguration(const SSLConfigParams *params, SSLCertLookup *l
   // bootstrap the SSL handshake so that we can subsequently do the SNI lookup to switch to the real
   // context.
   if (lookup->ssl_default == nullptr) {
-    ssl_user_config sslMultiCertSettings;
+    SSLMultiCertConfigParams sslMultiCertSettings;
     sslMultiCertSettings.addr = ats_strdup("*");
-    if (ssl_store_ssl_context(params, lookup, &sslMultiCertSettings) == nullptr) {
+    if (this->_store_ssl_ctx(lookup, &sslMultiCertSettings) == nullptr) {
       Error("failed set default context");
       return false;
     }
@@ -1988,3 +1847,170 @@ SSLConnect(SSL *ssl)
 
   return ssl_error;
 }
+
+/**
+   Load certificats to SSL_CTX
+   @static
+ */
+bool
+SSLMultiCertConfigLoader::load_certs(SSL_CTX *ctx, std::vector<X509 *> &cert_list, const SSLConfigParams *params,
+                                     const SSLMultiCertConfigParams *sslMultCertSettings)
+{
+  SimpleTokenizer cert_tok((const char *)sslMultCertSettings->cert, SSL_CERT_SEPARATE_DELIM);
+  SimpleTokenizer key_tok((sslMultCertSettings->key ? (const char *)sslMultCertSettings->key : ""), SSL_CERT_SEPARATE_DELIM);
+
+  if (sslMultCertSettings->key && cert_tok.getNumTokensRemaining() != key_tok.getNumTokensRemaining()) {
+    Error("the number of certificates in ssl_cert_name and ssl_key_name doesn't match");
+    return false;
+  }
+  SimpleTokenizer ca_tok("", SSL_CERT_SEPARATE_DELIM);
+  if (sslMultCertSettings->ca) {
+    ca_tok.setString(sslMultCertSettings->ca);
+    if (cert_tok.getNumTokensRemaining() != ca_tok.getNumTokensRemaining()) {
+      Error("the number of certificates in ssl_cert_name and ssl_ca_name doesn't match");
+      return false;
+    }
+  }
+
+  for (const char *certname = cert_tok.getNext(); certname; certname = cert_tok.getNext()) {
+    std::string completeServerCertPath = Layout::relative_to(params->serverCertPathOnly, certname);
+    scoped_BIO bio(BIO_new_file(completeServerCertPath.c_str(), "r"));
+    X509 *cert = nullptr;
+    if (bio) {
+      cert = PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr);
+    }
+    if (!bio || !cert) {
+      SSLError("failed to load certificate chain from %s", completeServerCertPath.c_str());
+      return false;
+    }
+    if (!SSL_CTX_use_certificate(ctx, cert)) {
+      SSLError("Failed to assign cert from %s to SSL_CTX", completeServerCertPath.c_str());
+      X509_free(cert);
+      return false;
+    }
+
+    // Load up any additional chain certificates
+    SSL_CTX_add_extra_chain_cert_bio(ctx, bio);
+
+    const char *keyPath = key_tok.getNext();
+    if (!SSLPrivateKeyHandler(ctx, params, completeServerCertPath, keyPath)) {
+      return false;
+    }
+
+    cert_list.push_back(cert);
+    if (SSLConfigParams::load_ssl_file_cb) {
+      SSLConfigParams::load_ssl_file_cb(completeServerCertPath.c_str(), CONFIG_FLAG_UNVERSIONED);
+    }
+
+    // Must load all the intermediate certificates before starting the next chain
+
+    // First, load any CA chains from the global chain file.  This should probably
+    // eventually be a comma separated list too.  For now we will load it in all chains even
+    // though it only makes sense in one chain
+    if (params->serverCertChainFilename) {
+      ats_scoped_str completeServerCertChainPath(Layout::relative_to(params->serverCertPathOnly, params->serverCertChainFilename));
+      if (!SSL_CTX_add_extra_chain_cert_file(ctx, completeServerCertChainPath)) {
+        SSLError("failed to load global certificate chain from %s", (const char *)completeServerCertChainPath);
+        return false;
+      }
+      if (SSLConfigParams::load_ssl_file_cb) {
+        SSLConfigParams::load_ssl_file_cb(completeServerCertChainPath, CONFIG_FLAG_UNVERSIONED);
+      }
+    }
+
+    // Now, load any additional certificate chains specified in this entry.
+    if (sslMultCertSettings->ca) {
+      const char *ca_name = ca_tok.getNext();
+      if (ca_name != nullptr) {
+        ats_scoped_str completeServerCertChainPath(Layout::relative_to(params->serverCertPathOnly, ca_name));
+        if (!SSL_CTX_add_extra_chain_cert_file(ctx, completeServerCertChainPath)) {
+          SSLError("failed to load certificate chain from %s", (const char *)completeServerCertChainPath);
+          return false;
+        }
+        if (SSLConfigParams::load_ssl_file_cb) {
+          SSLConfigParams::load_ssl_file_cb(completeServerCertChainPath, CONFIG_FLAG_UNVERSIONED);
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
+/**
+    Set session_id context for session reuse
+    @static
+ */
+bool
+SSLMultiCertConfigLoader::set_session_id_context(SSL_CTX *ctx, const SSLConfigParams *params,
+                                                 const SSLMultiCertConfigParams *sslMultCertSettings)
+{
+  EVP_MD_CTX *digest           = EVP_MD_CTX_new();
+  STACK_OF(X509_NAME) *ca_list = nullptr;
+  unsigned char hash_buf[EVP_MAX_MD_SIZE];
+  unsigned int hash_len    = 0;
+  const char *setting_cert = sslMultCertSettings ? sslMultCertSettings->cert.get() : nullptr;
+  bool result              = false;
+
+  // Set the list of CA's to send to client if we ask for a client certificate
+  if (params->serverCACertFilename) {
+    ca_list = SSL_load_client_CA_file(params->serverCACertFilename);
+    if (ca_list) {
+      SSL_CTX_set_client_CA_list(ctx, ca_list);
+    }
+  }
+
+  if (EVP_DigestInit_ex(digest, evp_md_func, nullptr) == 0) {
+    SSLError("EVP_DigestInit_ex failed");
+    goto fail;
+  }
+
+  if (nullptr != setting_cert) {
+    Debug("ssl", "Using '%s' in hash for session id context", sslMultCertSettings->cert.get());
+    if (EVP_DigestUpdate(digest, sslMultCertSettings->cert, strlen(setting_cert)) == 0) {
+      SSLError("EVP_DigestUpdate failed");
+      goto fail;
+    }
+  }
+
+  if (ca_list != nullptr) {
+    size_t num_certs = sk_X509_NAME_num(ca_list);
+
+    for (size_t i = 0; i < num_certs; i++) {
+      X509_NAME *name = sk_X509_NAME_value(ca_list, i);
+      if (X509_NAME_digest(name, evp_md_func, hash_buf /* borrow our final hash buffer. */, &hash_len) == 0 ||
+          EVP_DigestUpdate(digest, hash_buf, hash_len) == 0) {
+        SSLError("Adding X509 name to digest failed");
+        goto fail;
+      }
+    }
+  }
+
+  if (EVP_DigestFinal_ex(digest, hash_buf, &hash_len) == 0) {
+    SSLError("EVP_DigestFinal_ex failed");
+    goto fail;
+  }
+
+  if (SSL_CTX_set_session_id_context(ctx, hash_buf, hash_len) == 0) {
+    SSLError("SSL_CTX_set_session_id_context failed");
+    goto fail;
+  }
+
+  result = true;
+
+fail:
+  EVP_MD_CTX_free(digest);
+
+  return result;
+}
+
+/**
+   Clear password in SSL_CTX
+   @static
+ */
+void
+SSLMultiCertConfigLoader::clear_pw_references(SSL_CTX *ssl_ctx)
+{
+  SSL_CTX_set_default_passwd_cb(ssl_ctx, nullptr);
+  SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, nullptr);
+}