You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by mo...@apache.org on 2024/04/09 21:59:15 UTC

(trafficserver) branch master updated: Restore private key passphrase behavior (#11201)

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

mochen 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 87d88ded28 Restore private key passphrase behavior (#11201)
87d88ded28 is described below

commit 87d88ded2884a8b4a36a2f440bd4ddb75c481192
Author: Mo Chen <mo...@apache.org>
AuthorDate: Tue Apr 9 16:59:08 2024 -0500

    Restore private key passphrase behavior (#11201)
    
    Fix private key passphrase support in ssl_multicert.  Add autest.
---
 src/iocore/net/SSLConfig.cc                 |  4 +-
 src/iocore/net/SSLUtils.cc                  | 57 ++++++++----------
 tests/gold_tests/tls/ssl/passphrase.key     | 30 ++++++++++
 tests/gold_tests/tls/ssl/passphrase.pem     | 27 +++++++++
 tests/gold_tests/tls/ssl/passphrase2.key    | 30 ++++++++++
 tests/gold_tests/tls/ssl/passphrase2.pem    | 27 +++++++++
 tests/gold_tests/tls/ssl_key_dialog.test.py | 91 +++++++++++++++++++++++++++++
 7 files changed, 231 insertions(+), 35 deletions(-)

diff --git a/src/iocore/net/SSLConfig.cc b/src/iocore/net/SSLConfig.cc
index f275d1b051..e9a9fcef67 100644
--- a/src/iocore/net/SSLConfig.cc
+++ b/src/iocore/net/SSLConfig.cc
@@ -983,7 +983,9 @@ SSLConfigParams::getCTX(const std::string &client_cert, const std::string &key_f
         biop = BIO_new_mem_buf(secret_data.data(), secret_data.size());
       }
 
-      key = PEM_read_bio_PrivateKey(biop, nullptr, nullptr, nullptr);
+      pem_password_cb *password_cb = SSL_CTX_get_default_passwd_cb(client_ctx.get());
+      void *u                      = SSL_CTX_get_default_passwd_cb_userdata(client_ctx.get());
+      key                          = PEM_read_bio_PrivateKey(biop, nullptr, password_cb, u);
       if (!key) {
         SSLError("failed to load client private key file from %s", key_file_name.c_str());
         goto fail;
diff --git a/src/iocore/net/SSLUtils.cc b/src/iocore/net/SSLUtils.cc
index 4007741f4d..d7fbaf6fa4 100644
--- a/src/iocore/net/SSLUtils.cc
+++ b/src/iocore/net/SSLUtils.cc
@@ -654,18 +654,6 @@ ssl_context_enable_tickets(SSL_CTX *ctx, const char *ticket_key_path)
 #endif /* TS_HAS_TLS_SESSION_TICKET */
 }
 
-struct passphrase_cb_userdata {
-  const SSLConfigParams *_configParams;
-  const char *_serverDialog;
-  const char *_serverCert;
-  const char *_serverKey;
-
-  passphrase_cb_userdata(const SSLConfigParams *params, const char *dialog, const char *cert, const char *key)
-    : _configParams(params), _serverDialog(dialog), _serverCert(cert), _serverKey(key)
-  {
-  }
-};
-
 // RAII implementation for struct termios
 struct ssl_termios : public termios {
   ssl_termios(int fd)
@@ -741,15 +729,17 @@ ssl_private_key_passphrase_callback_exec(char *buf, int size, int rwflag, void *
     return 0;
   }
 
-  *buf                       = 0;
-  passphrase_cb_userdata *ud = static_cast<passphrase_cb_userdata *>(userdata);
+  *buf                                                = 0;
+  const SSLMultiCertConfigParams *sslMultCertSettings = static_cast<SSLMultiCertConfigParams *>(userdata);
 
-  Dbg(dbg_ctl_ssl_load, "ssl_private_key_passphrase_callback_exec rwflag=%d serverDialog=%s", rwflag, ud->_serverDialog);
+  Dbg(dbg_ctl_ssl_load, "ssl_private_key_passphrase_callback_exec rwflag=%d dialog=%s", rwflag, sslMultCertSettings->dialog.get());
 
   // only respond to reading private keys, not writing them (does ats even do that?)
   if (0 == rwflag) {
     // execute the dialog program and use the first line output as the passphrase
-    FILE *f = popen(ud->_serverDialog, "r");
+    ink_assert(strncmp(sslMultCertSettings->dialog, "exec:", 5) == 0);
+    const char *serverDialog = &sslMultCertSettings->dialog[5];
+    FILE *f                  = popen(serverDialog, "r");
     if (f) {
       if (fgets(buf, size, f)) {
         // remove any ending CR or LF
@@ -762,7 +752,7 @@ ssl_private_key_passphrase_callback_exec(char *buf, int size, int rwflag, void *
       }
       pclose(f);
     } else { // popen failed
-      Error("could not open dialog '%s' - %s", ud->_serverDialog, strerror(errno));
+      Error("could not open dialog '%s' - %s", serverDialog, strerror(errno));
     }
   }
   return strlen(buf);
@@ -775,19 +765,19 @@ ssl_private_key_passphrase_callback_builtin(char *buf, int size, int rwflag, voi
     return 0;
   }
 
-  *buf                       = 0;
-  passphrase_cb_userdata *ud = static_cast<passphrase_cb_userdata *>(userdata);
+  *buf                                                = 0;
+  const SSLMultiCertConfigParams *sslMultCertSettings = static_cast<SSLMultiCertConfigParams *>(userdata);
 
-  Dbg(dbg_ctl_ssl_load, "ssl_private_key_passphrase_callback rwflag=%d serverDialog=%s", rwflag, ud->_serverDialog);
+  Dbg(dbg_ctl_ssl_load, "ssl_private_key_passphrase_callback rwflag=%d dialog=%s", rwflag, sslMultCertSettings->dialog.get());
 
   // only respond to reading private keys, not writing them (does ats even do that?)
   if (0 == rwflag) {
     // output request
     fprintf(stdout, "Some of your private key files are encrypted for security reasons.\n");
     fprintf(stdout, "In order to read them you have to provide the pass phrases.\n");
-    fprintf(stdout, "ssl_cert_name=%s", ud->_serverCert);
-    if (ud->_serverKey) { // output ssl_key_name if provided
-      fprintf(stdout, " ssl_key_name=%s", ud->_serverKey);
+    fprintf(stdout, "ssl_cert_name=%s", sslMultCertSettings->cert.get());
+    if (sslMultCertSettings->key.get()) { // output ssl_key_name if provided
+      fprintf(stdout, " ssl_key_name=%s", sslMultCertSettings->key.get());
     }
     fprintf(stdout, "\n");
     // get passphrase
@@ -975,7 +965,10 @@ SSLPrivateKeyHandler(SSL_CTX *ctx, const SSLConfigParams *params, const char *ke
 #endif
   if (pkey == nullptr) {
     scoped_BIO bio(BIO_new_mem_buf(secret_data, secret_data_len));
-    pkey = PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr);
+
+    pem_password_cb *password_cb = SSL_CTX_get_default_passwd_cb(ctx);
+    void *u                      = SSL_CTX_get_default_passwd_cb_userdata(ctx);
+    pkey                         = PEM_read_bio_PrivateKey(bio.get(), nullptr, password_cb, u);
     if (nullptr == pkey) {
       Dbg(dbg_ctl_ssl_load, "failed to load server private key (%.*s) from %s", secret_data_len < 50 ? secret_data_len : 50,
           secret_data, (!keyPath || keyPath[0] == '\0') ? "[empty key path]" : keyPath);
@@ -1431,16 +1424,14 @@ bool
 SSLMultiCertConfigLoader::_setup_dialog(SSL_CTX *ctx, const SSLMultiCertConfigParams *sslMultCertSettings)
 {
   if (sslMultCertSettings->dialog) {
-    passphrase_cb_userdata ud(this->_params, sslMultCertSettings->dialog, sslMultCertSettings->first_cert,
-                              sslMultCertSettings->key);
     // pass phrase dialog configuration
     pem_password_cb *passwd_cb = nullptr;
     if (strncmp(sslMultCertSettings->dialog, "exec:", 5) == 0) {
-      ud._serverDialog = &sslMultCertSettings->dialog[5];
+      const char *serverDialog = &sslMultCertSettings->dialog[5];
+      Dbg(dbg_ctl_ssl_load, "exec:%s", serverDialog);
       // validate the exec program
-      if (!ssl_private_key_validate_exec(ud._serverDialog)) {
-        SSLError("failed to access '%s' pass phrase program: %s", (const char *)ud._serverDialog, strerror(errno));
-        memset(static_cast<void *>(&ud), 0, sizeof(ud));
+      if (!ssl_private_key_validate_exec(serverDialog)) {
+        SSLError("failed to access '%s' pass phrase program: %s", serverDialog, strerror(errno));
         return false;
       }
       passwd_cb = ssl_private_key_passphrase_callback_exec;
@@ -1448,13 +1439,10 @@ SSLMultiCertConfigLoader::_setup_dialog(SSL_CTX *ctx, const SSLMultiCertConfigPa
       passwd_cb = ssl_private_key_passphrase_callback_builtin;
     } else { // unknown config
       SSLError("unknown %s configuration value '%s'", SSL_KEY_DIALOG.data(), (const char *)sslMultCertSettings->dialog);
-      memset(static_cast<void *>(&ud), 0, sizeof(ud));
       return false;
     }
     SSL_CTX_set_default_passwd_cb(ctx, passwd_cb);
-    SSL_CTX_set_default_passwd_cb_userdata(ctx, &ud);
-    // Clear any password info lingering in the UD data structure
-    memset(static_cast<void *>(&ud), 0, sizeof(ud));
+    SSL_CTX_set_default_passwd_cb_userdata(ctx, const_cast<SSLMultiCertConfigParams *>(sslMultCertSettings));
   }
   return true;
 }
@@ -2556,6 +2544,7 @@ SSLMultiCertConfigLoader::_dbg_ctl() const
 void
 SSLMultiCertConfigLoader::clear_pw_references(SSL_CTX *ssl_ctx)
 {
+  Dbg(dbg_ctl_ssl_load, "clearing pw preferences");
   SSL_CTX_set_default_passwd_cb(ssl_ctx, nullptr);
   SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, nullptr);
 }
diff --git a/tests/gold_tests/tls/ssl/passphrase.key b/tests/gold_tests/tls/ssl/passphrase.key
new file mode 100644
index 0000000000..152197bf99
--- /dev/null
+++ b/tests/gold_tests/tls/ssl/passphrase.key
@@ -0,0 +1,30 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFJDBWBgkqhkiG9w0BBQ0wSTAxBgkqhkiG9w0BBQwwJAQQBZQrPgU+cHs3Ls4O
+gfcPBwICCAAwDAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIBRDrt5DRnIoEggTI
+S5O31tiNE8pgNMZ9MAoiNfGGJHcnvQcOnP/bosrxfXRVrumLzxLhpXhPREtBiIUk
+ksuPmvobWS7AcZIPD72RpCN8NZw3lTANHnN3N7HP7qgUDbXsYhT2ssuT+BQsNRhA
+y8uh0tKysVCbVNClYJvgrM2xzU7JFghaeHkJkabYJ2FKnHVRIZwkwbJWDeW0Ce/V
+r858mzdmbJGack2Rg3FT5xDAqmzUc6PWSZgBXkrOwbmHJehoWTzfMSl4uBsrFKnd
+KGHQJ6m24DP4Am79BOce6YoM1P36i56Rwo/q7LHI5AAadVSTLMaoD8gey6Ny9ZO9
+eflyKDW80wqmJxDevEhCKqOQbunogBuVfyo4dc2G4XPfwBhN4Vp+dVcfx9TBTdGo
+gxNEd8+Nr8Ihzoff6brst9iJrBx/uWkEblSQXlp1cI9qfv8w/ji4PeLJilRaHfwV
+VSr2BupIseW2GDjOOiziFvgl7Crj2kOevThunNl/IdNf8LmidUCIRJimwh38KsLx
+VyG8+VFpISnkgPd89S9UEUpmUJi5hshmqpWZXLSuYUrYPucqJSKJHpqPdzcTBWg3
+kH/82T5VCYWJ/lfsl7HcfZHxfeng8636ZYZsXMwUpX3mHEGA08fuUROuekN9konO
+xXxvmtmWZcyJz+eVHCL+gqyP4ecfOmkvIVin2mXSneA420lmxyzY36pox4iZbqm3
+a8NpmUd+envWlJjq7dCHIsGjBfFr3hGyjDsE3ZRuoMnGGVbwM98Nrkw5bsKWUfXQ
+N3PZFZKQT/cXxpUIW/g7TLXFmP4I7o2NycV/84/vfX2tlh6nFk67JdgX54m2Et9n
+M8RMNb6gU8AWfZY2kDaDd44Qx/i/7huNsEmJ5u4ze0+GoN3R+Bj6fbEKqar/SP2v
+9or8yK64+A1ZHXUf6W12n9VBFsALqDIbHKxRfWiYfLv2eTnpGtxzicGp1OZMBWD4
+b1wpzGz5QjkQ3OC8zNRqSmynIJcnTSviJbAlHBSogUmOK/NAeSe8OIznpAUCHzGU
+zZylmM/2O1Fpf75iuqOL6flPGbXoT5IRirmP5KevJWbadBX3O5DPP2irjO+RiGf9
+0NdbLNvZh5WWUXycjUbnS36uzPiBKD+ZclgqZBH1UYPg26/p4hR1m3cCswg1AbsL
+lwjF+8w73ULHSF5VsSAa9U7QZ1DiJXFzaOyCDuCBlzFYc9v1A+kYrQVpZ1fsSubd
+k14BbrDlOuSfYWL8jpRg3RBBh41zCXa297fFG0GtqVVYG90Ij2za5od+fy/Q/W1I
+SJYuZndyIpf1hvGfVwRCPKcXIr7mf+//SPHBKJe5aHrpVDznEpswG3Tjcz+qM7OZ
+qZG4mfNUNUa6xWcVFSdZfXRszXzJdY0N3P2JFolpmJO3oDkbZoa63ILtTf355S9Z
+UVMTwX1QxGsKFG0jz92s474R2Z58A7TyRiZLQw+JxiaIDCVxaW73tr+/pCjqQwkW
+RT3iHNd/BeQnbl3RV8o+jE6wKMz10mulml7yq38vmyjHtBMSwPZz668T6lb161ti
+vMh/B4/bQu22DRpdtQS3B5h3XATJ7bkoG+hQUHzowz0jiK93ICZumpCYBu5V58nr
+QsIKSU9+bBOlU8y28KJgNuALBZgSZ/fs
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/tests/gold_tests/tls/ssl/passphrase.pem b/tests/gold_tests/tls/ssl/passphrase.pem
new file mode 100644
index 0000000000..8d9ea6b746
--- /dev/null
+++ b/tests/gold_tests/tls/ssl/passphrase.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEfjCCAmagAwIBAgIUWqf2OgjUkTeH4qDkqxeaeOA9EVIwDQYJKoZIhvcNAQEL
+BQAwVDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAklMMQ4wDAYDVQQKDAVZYWhvbzEN
+MAsGA1UECwwERWRnZTEZMBcGA1UEAwwQc2lnbmVyLnlhaG9vLmNvbTAeFw0yNDA0
+MDIyMTU5MzNaFw0yNTA0MDIyMTU5MzNaMFoxCzAJBgNVBAYTAkFVMRMwEQYDVQQI
+DApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQx
+EzARBgNVBAMMCnBhc3NwaHJhc2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQDRSJlZF7fVu/8etpbSYDbPL0m2Q8DHKfwinK6nqbbF7OWbQoCeHpaEppyk
+9iQ/eIPCtnDD0rdA1cC9DwdOfaeaBhjXy2OaLrJGaeTUhhNcLhSwbfX26dKn5EEi
+B5bRaEruKsh/MmmSe691TT6kEfpFQCK4WO9B9UXejEtI7NZ4wskaO38UjVEYLguW
+wJ25mnzWQpjhYWR2G1+2Q3SCtZ82cXKJTQWuOPJHmJ5FjQQpldFJg0PKf6zAY9Vr
+vkggeHiLwNKTk5R+975EB9fLoV3R69zkqf2NoMVDTvG9jd974haT/iRO9fy2pGXt
+ACaxoYoVEyUbbZd9i9aR3gmczqEFAgMBAAGjQjBAMB0GA1UdDgQWBBRRijjaYGCs
+ZE5bO2i7l5JA+nlGtDAfBgNVHSMEGDAWgBS8vY4PSrVikT05kO3dCxqr9MMboTAN
+BgkqhkiG9w0BAQsFAAOCAgEAX2O9IBogwv+338jsvXb+ZVwHLUHFkXugqXNEI8hm
+A9JFcqHwsVfOJmYGuAEjGReyjzfZou5YuzPT14VvF+wnJPA6/PbWrAKxoioq4pho
+2/LRQSqe4O+T+QcSX32Nsu8h5bnv6i9MSAAWltErhOTmQkWEqYIzCh86YuHloBll
+txVJ/KvWu4xetHREo1OILV5w2a5jZ7AhTqe/Yx9NmRSvQIt4MRp4DBPS8nNaUwak
+3rgJV8QCIFk4+S1FqSb+4t5O7VQh4aPSmsEKHkP1Km4qFtUa4BKpDTWQTjtC/c+2
+V/ja6vFJFApniTHLpsRuu7Ma8xVulHiGuNwcvlgBtDcRAzdLcCPHCrhHpWTkrG6p
+OnqznwwlBFmZHa8yiu8EHZbxH8hrPdf8VguNOc2sPEaskllmebl+XJlD0d3ulwQj
+g1Gv3em0/eRWBs0sTcjTX6s852QLHPeTgsn5Z+/VVOnFQ4rY/obrrmd/44QHDN2l
+SoEHGWT6KWK+bUUytkm+uGdJjElsPYj2dXftTLc0n4AraRCEKATMHrH/dx3HuNez
+YjR8oYsSQp/OpzsaYFPoid/03n66rbtLm7DSeS4SdLdnrs8f7CbEhyyZPtG8VyoI
+/YMSkidOMhgfiOdGl2JcC4JDhJCOlH6cQwxU+jxLFBomqTkX03kZNzbXdiCb3SiG
+sk8=
+-----END CERTIFICATE-----
diff --git a/tests/gold_tests/tls/ssl/passphrase2.key b/tests/gold_tests/tls/ssl/passphrase2.key
new file mode 100644
index 0000000000..152197bf99
--- /dev/null
+++ b/tests/gold_tests/tls/ssl/passphrase2.key
@@ -0,0 +1,30 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFJDBWBgkqhkiG9w0BBQ0wSTAxBgkqhkiG9w0BBQwwJAQQBZQrPgU+cHs3Ls4O
+gfcPBwICCAAwDAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIBRDrt5DRnIoEggTI
+S5O31tiNE8pgNMZ9MAoiNfGGJHcnvQcOnP/bosrxfXRVrumLzxLhpXhPREtBiIUk
+ksuPmvobWS7AcZIPD72RpCN8NZw3lTANHnN3N7HP7qgUDbXsYhT2ssuT+BQsNRhA
+y8uh0tKysVCbVNClYJvgrM2xzU7JFghaeHkJkabYJ2FKnHVRIZwkwbJWDeW0Ce/V
+r858mzdmbJGack2Rg3FT5xDAqmzUc6PWSZgBXkrOwbmHJehoWTzfMSl4uBsrFKnd
+KGHQJ6m24DP4Am79BOce6YoM1P36i56Rwo/q7LHI5AAadVSTLMaoD8gey6Ny9ZO9
+eflyKDW80wqmJxDevEhCKqOQbunogBuVfyo4dc2G4XPfwBhN4Vp+dVcfx9TBTdGo
+gxNEd8+Nr8Ihzoff6brst9iJrBx/uWkEblSQXlp1cI9qfv8w/ji4PeLJilRaHfwV
+VSr2BupIseW2GDjOOiziFvgl7Crj2kOevThunNl/IdNf8LmidUCIRJimwh38KsLx
+VyG8+VFpISnkgPd89S9UEUpmUJi5hshmqpWZXLSuYUrYPucqJSKJHpqPdzcTBWg3
+kH/82T5VCYWJ/lfsl7HcfZHxfeng8636ZYZsXMwUpX3mHEGA08fuUROuekN9konO
+xXxvmtmWZcyJz+eVHCL+gqyP4ecfOmkvIVin2mXSneA420lmxyzY36pox4iZbqm3
+a8NpmUd+envWlJjq7dCHIsGjBfFr3hGyjDsE3ZRuoMnGGVbwM98Nrkw5bsKWUfXQ
+N3PZFZKQT/cXxpUIW/g7TLXFmP4I7o2NycV/84/vfX2tlh6nFk67JdgX54m2Et9n
+M8RMNb6gU8AWfZY2kDaDd44Qx/i/7huNsEmJ5u4ze0+GoN3R+Bj6fbEKqar/SP2v
+9or8yK64+A1ZHXUf6W12n9VBFsALqDIbHKxRfWiYfLv2eTnpGtxzicGp1OZMBWD4
+b1wpzGz5QjkQ3OC8zNRqSmynIJcnTSviJbAlHBSogUmOK/NAeSe8OIznpAUCHzGU
+zZylmM/2O1Fpf75iuqOL6flPGbXoT5IRirmP5KevJWbadBX3O5DPP2irjO+RiGf9
+0NdbLNvZh5WWUXycjUbnS36uzPiBKD+ZclgqZBH1UYPg26/p4hR1m3cCswg1AbsL
+lwjF+8w73ULHSF5VsSAa9U7QZ1DiJXFzaOyCDuCBlzFYc9v1A+kYrQVpZ1fsSubd
+k14BbrDlOuSfYWL8jpRg3RBBh41zCXa297fFG0GtqVVYG90Ij2za5od+fy/Q/W1I
+SJYuZndyIpf1hvGfVwRCPKcXIr7mf+//SPHBKJe5aHrpVDznEpswG3Tjcz+qM7OZ
+qZG4mfNUNUa6xWcVFSdZfXRszXzJdY0N3P2JFolpmJO3oDkbZoa63ILtTf355S9Z
+UVMTwX1QxGsKFG0jz92s474R2Z58A7TyRiZLQw+JxiaIDCVxaW73tr+/pCjqQwkW
+RT3iHNd/BeQnbl3RV8o+jE6wKMz10mulml7yq38vmyjHtBMSwPZz668T6lb161ti
+vMh/B4/bQu22DRpdtQS3B5h3XATJ7bkoG+hQUHzowz0jiK93ICZumpCYBu5V58nr
+QsIKSU9+bBOlU8y28KJgNuALBZgSZ/fs
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/tests/gold_tests/tls/ssl/passphrase2.pem b/tests/gold_tests/tls/ssl/passphrase2.pem
new file mode 100644
index 0000000000..8d9ea6b746
--- /dev/null
+++ b/tests/gold_tests/tls/ssl/passphrase2.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEfjCCAmagAwIBAgIUWqf2OgjUkTeH4qDkqxeaeOA9EVIwDQYJKoZIhvcNAQEL
+BQAwVDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAklMMQ4wDAYDVQQKDAVZYWhvbzEN
+MAsGA1UECwwERWRnZTEZMBcGA1UEAwwQc2lnbmVyLnlhaG9vLmNvbTAeFw0yNDA0
+MDIyMTU5MzNaFw0yNTA0MDIyMTU5MzNaMFoxCzAJBgNVBAYTAkFVMRMwEQYDVQQI
+DApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQx
+EzARBgNVBAMMCnBhc3NwaHJhc2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQDRSJlZF7fVu/8etpbSYDbPL0m2Q8DHKfwinK6nqbbF7OWbQoCeHpaEppyk
+9iQ/eIPCtnDD0rdA1cC9DwdOfaeaBhjXy2OaLrJGaeTUhhNcLhSwbfX26dKn5EEi
+B5bRaEruKsh/MmmSe691TT6kEfpFQCK4WO9B9UXejEtI7NZ4wskaO38UjVEYLguW
+wJ25mnzWQpjhYWR2G1+2Q3SCtZ82cXKJTQWuOPJHmJ5FjQQpldFJg0PKf6zAY9Vr
+vkggeHiLwNKTk5R+975EB9fLoV3R69zkqf2NoMVDTvG9jd974haT/iRO9fy2pGXt
+ACaxoYoVEyUbbZd9i9aR3gmczqEFAgMBAAGjQjBAMB0GA1UdDgQWBBRRijjaYGCs
+ZE5bO2i7l5JA+nlGtDAfBgNVHSMEGDAWgBS8vY4PSrVikT05kO3dCxqr9MMboTAN
+BgkqhkiG9w0BAQsFAAOCAgEAX2O9IBogwv+338jsvXb+ZVwHLUHFkXugqXNEI8hm
+A9JFcqHwsVfOJmYGuAEjGReyjzfZou5YuzPT14VvF+wnJPA6/PbWrAKxoioq4pho
+2/LRQSqe4O+T+QcSX32Nsu8h5bnv6i9MSAAWltErhOTmQkWEqYIzCh86YuHloBll
+txVJ/KvWu4xetHREo1OILV5w2a5jZ7AhTqe/Yx9NmRSvQIt4MRp4DBPS8nNaUwak
+3rgJV8QCIFk4+S1FqSb+4t5O7VQh4aPSmsEKHkP1Km4qFtUa4BKpDTWQTjtC/c+2
+V/ja6vFJFApniTHLpsRuu7Ma8xVulHiGuNwcvlgBtDcRAzdLcCPHCrhHpWTkrG6p
+OnqznwwlBFmZHa8yiu8EHZbxH8hrPdf8VguNOc2sPEaskllmebl+XJlD0d3ulwQj
+g1Gv3em0/eRWBs0sTcjTX6s852QLHPeTgsn5Z+/VVOnFQ4rY/obrrmd/44QHDN2l
+SoEHGWT6KWK+bUUytkm+uGdJjElsPYj2dXftTLc0n4AraRCEKATMHrH/dx3HuNez
+YjR8oYsSQp/OpzsaYFPoid/03n66rbtLm7DSeS4SdLdnrs8f7CbEhyyZPtG8VyoI
+/YMSkidOMhgfiOdGl2JcC4JDhJCOlH6cQwxU+jxLFBomqTkX03kZNzbXdiCb3SiG
+sk8=
+-----END CERTIFICATE-----
diff --git a/tests/gold_tests/tls/ssl_key_dialog.test.py b/tests/gold_tests/tls/ssl_key_dialog.test.py
new file mode 100644
index 0000000000..70bb365c70
--- /dev/null
+++ b/tests/gold_tests/tls/ssl_key_dialog.test.py
@@ -0,0 +1,91 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding right ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+Test.Summary = '''
+Test that Trafficserver starts with default configurations.
+'''
+
+ts = Test.MakeATSProcess("ts", enable_tls=True)
+server = Test.MakeOriginServer("server")
+
+ts.addSSLfile("ssl/passphrase.pem")
+ts.addSSLfile("ssl/passphrase.key")
+
+ts.addSSLfile("ssl/passphrase2.pem")
+ts.addSSLfile("ssl/passphrase2.key")
+
+ts.Disk.remap_config.AddLine(f"map https://passphrase:{ts.Variables.ssl_port}/ http://127.0.0.1:{server.Variables.Port}")
+ts.Disk.records_config.update(
+    {
+        'proxy.config.diags.debug.enabled': 1,
+        'proxy.config.diags.debug.tags': 'ssl_load|http',
+        'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
+        'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir),
+    })
+
+ts.Disk.ssl_multicert_config.AddLines(
+    [
+        'dest_ip=* ssl_cert_name=passphrase.pem ssl_key_name=passphrase.key ssl_key_dialog="exec:/bin/bash -c \'echo -n passphrase\'"',
+    ])
+
+request_header = {"headers": "GET / HTTP/1.1\r\nHost: bogus\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": "success!"}
+server.addResponse("sessionlog.json", request_header, response_header)
+
+tr = Test.AddTestRun("use a key with passphrase")
+tr.Setup.Copy("ssl/signer.pem")
+tr.Processes.Default.Command = f"curl -v --cacert ./signer.pem  --resolve 'passphrase:{ts.Variables.ssl_port}:127.0.0.1' https://passphrase:{ts.Variables.ssl_port}/"
+tr.ReturnCode = 0
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(Test.Processes.ts)
+tr.Processes.Default.Streams.stderr.Content = Testers.ContainsExpression("200", "expected 200 OK response")
+tr.Processes.Default.Streams.stdout.Content = Testers.ContainsExpression("success!", "expected success")
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+
+tr2 = Test.AddTestRun("Update config files")
+# Update the multicert config
+sslcertpath = ts.Disk.ssl_multicert_config.AbsPath
+tr2.Disk.File(sslcertpath, id="ssl_multicert_config", typename="ats:config"),
+tr2.Disk.ssl_multicert_config.AddLines(
+    [
+        'dest_ip=* ssl_cert_name=passphrase2.pem ssl_key_name=passphrase2.key ssl_key_dialog="exec:/bin/bash -c \'echo -n passphrase\'"',
+    ])
+tr2.StillRunningAfter = ts
+tr2.StillRunningAfter = server
+tr2.Processes.Default.Command = 'echo Updated configs'
+# Need to copy over the environment so traffic_ctl knows where to find the unix domain socket
+tr2.Processes.Default.Env = ts.Env
+tr2.Processes.Default.ReturnCode = 0
+
+tr2reload = Test.AddTestRun("Reload config")
+tr2reload.StillRunningAfter = ts
+tr2reload.StillRunningAfter = server
+tr2reload.Processes.Default.Command = 'traffic_ctl config reload'
+# Need to copy over the environment so traffic_ctl knows where to find the unix domain socket
+tr2reload.Processes.Default.Env = ts.Env
+tr2reload.Processes.Default.ReturnCode = 0
+
+tr3 = Test.AddTestRun("use a key with passphrase")
+tr3.Setup.Copy("ssl/signer.pem")
+tr3.Processes.Default.Command = f"curl -v --cacert ./signer.pem  --resolve 'passphrase:{ts.Variables.ssl_port}:127.0.0.1' https://passphrase:{ts.Variables.ssl_port}/"
+tr3.ReturnCode = 0
+tr3.Processes.Default.Streams.stderr.Content = Testers.ContainsExpression("200", "expected 200 OK response")
+tr3.Processes.Default.Streams.stdout.Content = Testers.ContainsExpression("success!", "expected success")
+tr3.StillRunningAfter = server
+tr3.StillRunningAfter = ts