Blob Blame History Raw
diff --git a/contrib/vim/syntax/nginx.vim b/contrib/vim/syntax/nginx.vim
index 7d587fc..15b21e2 100644
--- a/contrib/vim/syntax/nginx.vim
+++ b/contrib/vim/syntax/nginx.vim
@@ -617,6 +617,7 @@ syn keyword ngxDirective contained ssl_ocsp
 syn keyword ngxDirective contained ssl_ocsp_cache
 syn keyword ngxDirective contained ssl_ocsp_responder
 syn keyword ngxDirective contained ssl_password_file
+syn keyword ngxDirective contained ssl_pass_phrase_dialog
 syn keyword ngxDirective contained ssl_prefer_server_ciphers
 syn keyword ngxDirective contained ssl_preread
 syn keyword ngxDirective contained ssl_protocols
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index 104e8da..8cf777e 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -9,9 +9,8 @@
 #include <ngx_core.h>
 #include <ngx_event.h>
 
-
 #define NGX_SSL_PASSWORD_BUFFER_SIZE  4096
-
+#define NGX_PASS_PHRASE_ARG_MAX_LEN  255
 
 typedef struct {
     ngx_uint_t  engine;   /* unsigned  engine:1; */
@@ -20,8 +19,8 @@ typedef struct {
 
 static X509 *ngx_ssl_load_certificate(ngx_pool_t *pool, char **err,
     ngx_str_t *cert, STACK_OF(X509) **chain);
-static EVP_PKEY *ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,
-    ngx_str_t *key, ngx_array_t *passwords);
+static EVP_PKEY *ngx_ssl_load_certificate_key(ngx_pool_t *pool,
+    char **err, ngx_str_t *key, ngx_array_t *passwords, ngx_ssl_ppdialog_conf_t *dlg);
 static int ngx_ssl_password_callback(char *buf, int size, int rwflag,
     void *userdata);
 static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);
@@ -88,6 +87,12 @@ static time_t ngx_ssl_parse_time(
 #endif
     ASN1_TIME *asn1time, ngx_log_t *log);
 
+static int ngx_ssl_read_pstream(const char *cmd, char *buf,
+    ngx_int_t bufsize);
+
+static int ngx_ssl_pass_phrase_callback(char *buf, int bufsize,
+    int rwflag, void *u);
+
 static void *ngx_openssl_create_conf(ngx_cycle_t *cycle);
 static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static void ngx_openssl_exit(ngx_cycle_t *cycle);
@@ -398,7 +403,7 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data)
 
 ngx_int_t
 ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *certs,
-    ngx_array_t *keys, ngx_array_t *passwords)
+    ngx_array_t *keys, ngx_array_t *passwords, ngx_ssl_ppdialog_conf_t *dlg)
 {
     ngx_str_t   *cert, *key;
     ngx_uint_t   i;
@@ -408,7 +413,7 @@ ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *certs,
 
     for (i = 0; i < certs->nelts; i++) {
 
-        if (ngx_ssl_certificate(cf, ssl, &cert[i], &key[i], passwords)
+        if (ngx_ssl_certificate(cf, ssl, &cert[i], &key[i], passwords, dlg)
             != NGX_OK)
         {
             return NGX_ERROR;
@@ -421,12 +426,13 @@ ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *certs,
 
 ngx_int_t
 ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
-    ngx_str_t *key, ngx_array_t *passwords)
+    ngx_str_t *key, ngx_array_t *passwords, ngx_ssl_ppdialog_conf_t *dlg)
 {
     char            *err;
     X509            *x509;
     EVP_PKEY        *pkey;
     STACK_OF(X509)  *chain;
+    EVP_PKEY        *pubkey;
 
     x509 = ngx_ssl_load_certificate(cf->pool, &err, cert, &chain);
     if (x509 == NULL) {
@@ -516,8 +522,19 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
     }
 #endif
 
-    pkey = ngx_ssl_load_certificate_key(cf->pool, &err, key, passwords);
-    if (pkey == NULL) {
+    pubkey = X509_get_pubkey(x509);
+    if (!pubkey){
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                          "X509_get_pubkey() failed");
+        return NGX_ERROR;
+    }
+    dlg->cryptosystem = EVP_PKEY_get_base_id(pubkey);
+    EVP_PKEY_free(pubkey);
+
+    pkey = ngx_ssl_load_certificate_key(cf->pool, &err, key, passwords, dlg);
+    if (ngx_test_config){
+        return NGX_OK;
+    } else if (pkey == NULL) {
         if (err != NULL) {
             ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
                           "cannot load certificate key \"%s\": %s",
@@ -587,7 +604,7 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
 
 #endif
 
-    pkey = ngx_ssl_load_certificate_key(pool, &err, key, passwords);
+    pkey = ngx_ssl_load_certificate_key(pool, &err, key, passwords, NULL);
     if (pkey == NULL) {
         if (err != NULL) {
             ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
@@ -700,10 +717,81 @@ ngx_ssl_load_certificate(ngx_pool_t *pool, char **err, ngx_str_t *cert,
     return x509;
 }
 
+static int
+ngx_ssl_read_pstream(const char *cmd, char *buf, ngx_int_t bufsize)
+{
+    FILE       *fp;
+    ngx_int_t i;
+    char c;
+
+    fp = popen(cmd, "r");
+    if (fp == NULL) {
+        return -1;
+    }
+
+    for (i = 0; (c = fgetc(fp)) != EOF &&
+            (i < bufsize - 1); i++) {
+
+        if (c == '\n' || c == '\r'){
+            break;
+        }
+
+        buf[i] = c;
+    }
+    buf[i] = '\0';
+
+    pclose(fp);
+
+    return 0;
+}
+
+static int
+ngx_ssl_pass_phrase_callback(char *buf, int bufsize, int rwflag, void *u)
+{
+    u_char cmd[NGX_PASS_PHRASE_ARG_MAX_LEN + 1] = {0};
+    u_char *cmd_end;
+    ngx_ssl_ppdialog_conf_t *dlg              = (ngx_ssl_ppdialog_conf_t *)u;
+    ngx_str_t *pass_phrase_dialog             = dlg->data;
+    char cryptosystem[4] = {0};
+    int ret;
+
+    /* remove exec: str from pass_phrase_dialog */
+    pass_phrase_dialog->data = pass_phrase_dialog->data + 5;
+    pass_phrase_dialog->len  = pass_phrase_dialog->len - 5;
+
+    switch (dlg->cryptosystem){
+        case EVP_PKEY_RSA:
+            strncpy(cryptosystem, "RSA", 4);
+            break;
+        case EVP_PKEY_DSA:
+            strncpy(cryptosystem, "DSA", 4);
+            break;
+        case EVP_PKEY_EC:
+            strncpy(cryptosystem, "EC", 3);
+            break;
+        case EVP_PKEY_DH:
+            strncpy(cryptosystem, "DH", 3);
+            break;
+        default:
+            strncpy(cryptosystem, "UNK", 4);
+            break;
+    }
+
+    cmd_end = ngx_snprintf(cmd, NGX_PASS_PHRASE_ARG_MAX_LEN, "%V %V %s",
+                         pass_phrase_dialog, dlg->server, cryptosystem);
+    *cmd_end = '\0';
+
+    ngx_log_stderr(0, "Executing external script: %s\n", cmd);
+
+    if ((ret = ngx_ssl_read_pstream((char *)cmd, buf, bufsize)) != 0){
+        return -1;
+    }
+
+    return strlen(buf);
+}
 
 static EVP_PKEY *
-ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,
-    ngx_str_t *key, ngx_array_t *passwords)
+ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err, ngx_str_t *key, ngx_array_t *passwords, ngx_ssl_ppdialog_conf_t *dlg)
 {
     BIO              *bio;
     EVP_PKEY         *pkey;
@@ -791,11 +879,26 @@ ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,
         tries = 1;
         pwd = NULL;
         cb = NULL;
+
+        /** directive format: ssl_pass_phrase_dialog buildin|exec:filepath */
+        if (dlg && ngx_strncasecmp(dlg->data->data, (u_char *)"exec:", 5) == 0){
+           pwd = (void *)dlg;
+           cb = ngx_ssl_pass_phrase_callback;
+        } else {
+           pwd = NULL;
+           cb = NULL;
+        }
     }
 
-    for ( ;; ) {
+    /* skip decrypting private keys in config test phase to avoid 
+       asking for pass phase twice */
+    if (ngx_test_config){
+        return NULL;
+    }
 
+    for ( ;; ) {
         pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, pwd);
+
         if (pkey != NULL) {
             break;
         }
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
index 860ea26..41f4501 100644
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -74,9 +74,19 @@
 #define ERR_peek_error_data(d, f)    ERR_peek_error_line_data(NULL, NULL, d, f)
 #endif
 
+#define NGX_SSL_PASS_PHRASE_ARG_MAX_LEN  255
+#define NGX_SSL_PASS_PHRASE_DEFAULT_VAL "builtin"
+#define NGX_SSL_SERVER_NULL             "undefined"
 
 typedef struct ngx_ssl_ocsp_s  ngx_ssl_ocsp_t;
 
+typedef struct ngx_ssl_ppdialog_conf_s ngx_ssl_ppdialog_conf_t;
+
+struct ngx_ssl_ppdialog_conf_s {
+    ngx_str_t                  *data;
+    ngx_str_t                  *server;
+    ngx_int_t                  cryptosystem;
+};
 
 struct ngx_ssl_s {
     SSL_CTX                    *ctx;
@@ -84,7 +94,6 @@ struct ngx_ssl_s {
     size_t                      buffer_size;
 };
 
-
 struct ngx_ssl_connection_s {
     ngx_ssl_conn_t             *connection;
     SSL_CTX                    *session_ctx;
@@ -184,9 +193,9 @@ ngx_int_t ngx_ssl_init(ngx_log_t *log);
 ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data);
 
 ngx_int_t ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl,
-    ngx_array_t *certs, ngx_array_t *keys, ngx_array_t *passwords);
+    ngx_array_t *certs, ngx_array_t *keys, ngx_array_t *passwords, ngx_ssl_ppdialog_conf_t *dlg);
 ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
-    ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);
+    ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords, ngx_ssl_ppdialog_conf_t *dlg);
 ngx_int_t ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
     ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);
 
diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c
index dfe49c5..904263d 100644
--- a/src/http/modules/ngx_http_grpc_module.c
+++ b/src/http/modules/ngx_http_grpc_module.c
@@ -4983,7 +4983,7 @@ ngx_http_grpc_set_ssl(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *glcf)
             if (ngx_ssl_certificate(cf, glcf->upstream.ssl,
                                     &glcf->upstream.ssl_certificate->value,
                                     &glcf->upstream.ssl_certificate_key->value,
-                                    glcf->upstream.ssl_passwords)
+                                    glcf->upstream.ssl_passwords, NULL)
                 != NGX_OK)
             {
                 return NGX_ERROR;
diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c
index 9cc202c..2c938d7 100644
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -5032,7 +5032,7 @@ ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf)
             if (ngx_ssl_certificate(cf, plcf->upstream.ssl,
                                     &plcf->upstream.ssl_certificate->value,
                                     &plcf->upstream.ssl_certificate_key->value,
-                                    plcf->upstream.ssl_passwords)
+                                    plcf->upstream.ssl_passwords, NULL)
                 != NGX_OK)
             {
                 return NGX_ERROR;
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
index 4c4a598..a147054 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -17,8 +17,9 @@ typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,
 #define NGX_DEFAULT_CIPHERS     "HIGH:!aNULL:!MD5"
 #define NGX_DEFAULT_ECDH_CURVE  "auto"
 
-#define NGX_HTTP_ALPN_PROTOS    "\x08http/1.1\x08http/1.0\x08http/0.9"
+static ngx_str_t ngx_ssl_server_null = ngx_string(NGX_SSL_SERVER_NULL);
 
+#define NGX_HTTP_ALPN_PROTOS    "\x08http/1.1\x08http/1.0\x08http/0.9"
 
 #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
 static int ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn,
@@ -53,6 +54,9 @@ static char *ngx_http_ssl_conf_command_check(ngx_conf_t *cf, void *post,
 
 static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf);
 
+static char *ngx_conf_set_pass_phrase_dialog(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+
 
 static ngx_conf_bitmask_t  ngx_http_ssl_protocols[] = {
     { ngx_string("SSLv2"), NGX_SSL_SSLv2 },
@@ -296,6 +300,13 @@ static ngx_command_t  ngx_http_ssl_commands[] = {
       offsetof(ngx_http_ssl_srv_conf_t, reject_handshake),
       NULL },
 
+    { ngx_string("ssl_pass_phrase_dialog"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_pass_phrase_dialog,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      offsetof(ngx_http_ssl_srv_conf_t, pass_phrase_dialog),
+      NULL },
+
       ngx_null_command
 };
 
@@ -555,7 +566,7 @@ ngx_http_ssl_add_variables(ngx_conf_t *cf)
 static void *
 ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
 {
-    ngx_http_ssl_srv_conf_t  *sscf;
+    ngx_http_ssl_srv_conf_t             *sscf;
 
     sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssl_srv_conf_t));
     if (sscf == NULL) {
@@ -577,6 +588,8 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
      *     sscf->ocsp_responder = { 0, NULL };
      *     sscf->stapling_file = { 0, NULL };
      *     sscf->stapling_responder = { 0, NULL };
+     *     sscf->pass_phrase_dialog = NULL;
+     *
      */
 
     sscf->enable = NGX_CONF_UNSET;
@@ -608,6 +621,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
 {
     ngx_http_ssl_srv_conf_t *prev = parent;
     ngx_http_ssl_srv_conf_t *conf = child;
+    ngx_http_core_srv_conf_t            *cscf;
+    ngx_ssl_ppdialog_conf_t             dlg;
 
     ngx_pool_cleanup_t  *cln;
 
@@ -674,6 +689,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
     ngx_conf_merge_str_value(conf->stapling_responder,
                          prev->stapling_responder, "");
 
+    ngx_conf_merge_str_value(conf->pass_phrase_dialog,
+                         prev->pass_phrase_dialog, NGX_SSL_PASS_PHRASE_DEFAULT_VAL);
+
     conf->ssl.log = cf->log;
 
     if (conf->enable) {
@@ -736,6 +754,30 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
     cln->handler = ngx_ssl_cleanup_ctx;
     cln->data = &conf->ssl;
 
+    /** directive format: ssl_pass_phrase_dialog buildin|exec:filepath */
+    if (ngx_strncasecmp(conf->pass_phrase_dialog.data, (u_char *)"exec:", 5) == 0){
+       ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+            "ssl_pass_phrase_dialog config directive SET: %s ", conf->pass_phrase_dialog.data);
+    } else if (ngx_strncasecmp(conf->pass_phrase_dialog.data, (u_char *)NGX_SSL_PASS_PHRASE_DEFAULT_VAL,
+               sizeof(NGX_SSL_PASS_PHRASE_DEFAULT_VAL)) != 0){
+
+       ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+            "ssl_pass_phrase_dialog config directive accepts only the following "
+            "values: %s | exec:filepath", NGX_SSL_PASS_PHRASE_DEFAULT_VAL);
+
+       return NGX_CONF_ERROR;
+    }
+
+    cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);
+
+    dlg.data = &conf->pass_phrase_dialog;
+    if (cscf->server_name.len != 0) {
+        dlg.server = &cscf->server_name;
+    } else {
+        dlg.server = &ngx_ssl_server_null;
+    }
+
+
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
 
     if (SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,
@@ -786,7 +828,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
         /* configure certificates */
 
         if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,
-                                 conf->certificate_keys, conf->passwords)
+                                 conf->certificate_keys, conf->passwords, &dlg)
             != NGX_OK)
         {
             return NGX_CONF_ERROR;
@@ -1335,3 +1377,31 @@ ngx_http_ssl_init(ngx_conf_t *cf)
 
     return NGX_OK;
 }
+
+static char *
+ngx_conf_set_pass_phrase_dialog(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_ssl_srv_conf_t *sscf = conf;
+    ngx_str_t               *value;
+
+    if (sscf->pass_phrase_dialog.data){
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    sscf->pass_phrase_dialog = value[1];
+
+    if (sscf->pass_phrase_dialog.len == 0) {
+        return NGX_CONF_OK;
+    } else if (sscf->pass_phrase_dialog.len > NGX_SSL_PASS_PHRASE_ARG_MAX_LEN) {
+        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+            "ssl_pass_phrase_dialog argument length exceeded maximum possible length: %d",
+            NGX_SSL_PASS_PHRASE_ARG_MAX_LEN);
+
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h
index 7ab0f7e..2f83d75 100644
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -67,6 +67,8 @@ typedef struct {
 
     u_char                         *file;
     ngx_uint_t                      line;
+
+    ngx_str_t                       pass_phrase_dialog;
 } ngx_http_ssl_srv_conf_t;
 
 
diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c
index e4f721b..61efa99 100644
--- a/src/http/modules/ngx_http_uwsgi_module.c
+++ b/src/http/modules/ngx_http_uwsgi_module.c
@@ -2564,7 +2564,7 @@ ngx_http_uwsgi_set_ssl(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *uwcf)
             if (ngx_ssl_certificate(cf, uwcf->upstream.ssl,
                                     &uwcf->upstream.ssl_certificate->value,
                                     &uwcf->upstream.ssl_certificate_key->value,
-                                    uwcf->upstream.ssl_passwords)
+                                    uwcf->upstream.ssl_passwords, NULL)
                 != NGX_OK)
             {
                 return NGX_ERROR;
diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c
index 28737ac..728181d 100644
--- a/src/mail/ngx_mail_ssl_module.c
+++ b/src/mail/ngx_mail_ssl_module.c
@@ -13,6 +13,7 @@
 #define NGX_DEFAULT_CIPHERS     "HIGH:!aNULL:!MD5"
 #define NGX_DEFAULT_ECDH_CURVE  "auto"
 
+static ngx_str_t ngx_ssl_server_null = ngx_string(NGX_SSL_SERVER_NULL);
 
 #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
 static int ngx_mail_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn,
@@ -35,6 +36,8 @@ static char *ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
 static char *ngx_mail_ssl_conf_command_check(ngx_conf_t *cf, void *post,
     void *data);
 
+static char *ngx_conf_set_pass_phrase_dialog(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 
 static ngx_conf_enum_t  ngx_mail_starttls_state[] = {
     { ngx_string("off"), NGX_MAIL_STARTTLS_OFF },
@@ -216,6 +219,13 @@ static ngx_command_t  ngx_mail_ssl_commands[] = {
       offsetof(ngx_mail_ssl_conf_t, conf_commands),
       &ngx_mail_ssl_conf_command_post },
 
+    { ngx_string("ssl_pass_phrase_dialog"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_pass_phrase_dialog,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_ssl_conf_t, pass_phrase_dialog),
+      NULL },
+
       ngx_null_command
 };
 
@@ -345,6 +355,8 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
 {
     ngx_mail_ssl_conf_t *prev = parent;
     ngx_mail_ssl_conf_t *conf = child;
+    ngx_mail_core_srv_conf_t           *cscf;
+    ngx_ssl_ppdialog_conf_t             dlg;
 
     char                *mode;
     ngx_pool_cleanup_t  *cln;
@@ -388,6 +400,8 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
 
     ngx_conf_merge_ptr_value(conf->conf_commands, prev->conf_commands, NULL);
 
+    ngx_conf_merge_str_value(conf->pass_phrase_dialog,
+                         prev->pass_phrase_dialog, NGX_SSL_PASS_PHRASE_DEFAULT_VAL);
 
     conf->ssl.log = cf->log;
 
@@ -449,6 +463,29 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
     cln->handler = ngx_ssl_cleanup_ctx;
     cln->data = &conf->ssl;
 
+    /** directive format: ssl_pass_phrase_dialog buildin|exec:filepath */
+    if (ngx_strncasecmp(conf->pass_phrase_dialog.data, (u_char *)"exec:", 5) == 0){
+       ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+            "ssl_pass_phrase_dialog config directive SET: %s ", conf->pass_phrase_dialog.data);
+    } else if (ngx_strncasecmp(conf->pass_phrase_dialog.data, (u_char *)NGX_SSL_PASS_PHRASE_DEFAULT_VAL,
+               sizeof(NGX_SSL_PASS_PHRASE_DEFAULT_VAL)) != 0){
+
+       ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+            "ssl_pass_phrase_dialog config directive accepts only the following "
+            "values: %s | exec:filepath", NGX_SSL_PASS_PHRASE_DEFAULT_VAL);
+
+       return NGX_CONF_ERROR;
+    }
+
+    cscf = ngx_mail_conf_get_module_srv_conf(cf, ngx_mail_core_module);
+
+    dlg.data = &conf->pass_phrase_dialog;
+    if (cscf->server_name.len != 0) {
+        dlg.server = &cscf->server_name;
+    } else {
+        dlg.server = &ngx_ssl_server_null;
+    }
+
 #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
     SSL_CTX_set_alpn_select_cb(conf->ssl.ctx, ngx_mail_ssl_alpn_select, NULL);
 #endif
@@ -461,7 +498,7 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
     }
 
     if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,
-                             conf->certificate_keys, conf->passwords)
+                             conf->certificate_keys, conf->passwords, &dlg)
         != NGX_OK)
     {
         return NGX_CONF_ERROR;
@@ -745,3 +782,32 @@ ngx_mail_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data)
     return NGX_CONF_OK;
 #endif
 }
+
+static char *
+ngx_conf_set_pass_phrase_dialog(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_mail_ssl_conf_t     *sscf = conf;
+    ngx_str_t               *value;
+
+    if (sscf->pass_phrase_dialog.data){
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    sscf->pass_phrase_dialog = value[1];
+
+    if (sscf->pass_phrase_dialog.len == 0) {
+        return NGX_CONF_OK;
+    } else if (sscf->pass_phrase_dialog.len > NGX_SSL_PASS_PHRASE_ARG_MAX_LEN) {
+        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+            "ssl_pass_phrase_dialog argument length exceeded maximum possible length: %d",
+            NGX_SSL_PASS_PHRASE_ARG_MAX_LEN);
+
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
diff --git a/src/mail/ngx_mail_ssl_module.h b/src/mail/ngx_mail_ssl_module.h
index a0a6113..3d87d50 100644
--- a/src/mail/ngx_mail_ssl_module.h
+++ b/src/mail/ngx_mail_ssl_module.h
@@ -57,6 +57,8 @@ typedef struct {
 
     u_char          *file;
     ngx_uint_t       line;
+
+    ngx_str_t        pass_phrase_dialog;
 } ngx_mail_ssl_conf_t;
 
 
diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c
index ed275c0..1747aed 100644
--- a/src/stream/ngx_stream_proxy_module.c
+++ b/src/stream/ngx_stream_proxy_module.c
@@ -2305,7 +2305,7 @@ ngx_stream_proxy_set_ssl(ngx_conf_t *cf, ngx_stream_proxy_srv_conf_t *pscf)
             if (ngx_ssl_certificate(cf, pscf->ssl,
                                     &pscf->ssl_certificate->value,
                                     &pscf->ssl_certificate_key->value,
-                                    pscf->ssl_passwords)
+                                    pscf->ssl_passwords, NULL)
                 != NGX_OK)
             {
                 return NGX_ERROR;
diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
index 1ba1825..ba70547 100644
--- a/src/stream/ngx_stream_ssl_module.c
+++ b/src/stream/ngx_stream_ssl_module.c
@@ -17,6 +17,8 @@ typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,
 #define NGX_DEFAULT_CIPHERS     "HIGH:!aNULL:!MD5"
 #define NGX_DEFAULT_ECDH_CURVE  "auto"
 
+#define NGX_SSL_STREAM_NAME             "NGX_STREAM_SSL_MODULE"
+static ngx_str_t ngx_ssl_stream_default_name = ngx_string(NGX_SSL_STREAM_NAME);
 
 static ngx_int_t ngx_stream_ssl_handler(ngx_stream_session_t *s);
 static ngx_int_t ngx_stream_ssl_init_connection(ngx_ssl_t *ssl,
@@ -57,6 +59,9 @@ static char *ngx_stream_ssl_alpn(ngx_conf_t *cf, ngx_command_t *cmd,
 static char *ngx_stream_ssl_conf_command_check(ngx_conf_t *cf, void *post,
     void *data);
 
+static char *ngx_conf_set_pass_phrase_dialog(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+
 static ngx_int_t ngx_stream_ssl_init(ngx_conf_t *cf);
 
 
@@ -226,6 +231,13 @@ static ngx_command_t  ngx_stream_ssl_commands[] = {
       0,
       NULL },
 
+    { ngx_string("ssl_pass_phrase_dialog"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_pass_phrase_dialog,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_ssl_conf_t, pass_phrase_dialog),
+      NULL },
+
       ngx_null_command
 };
 
@@ -690,6 +702,7 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
 {
     ngx_stream_ssl_conf_t *prev = parent;
     ngx_stream_ssl_conf_t *conf = child;
+    ngx_ssl_ppdialog_conf_t             dlg;
 
     ngx_pool_cleanup_t  *cln;
 
@@ -732,6 +745,8 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
 
     ngx_conf_merge_ptr_value(conf->conf_commands, prev->conf_commands, NULL);
 
+    ngx_conf_merge_str_value(conf->pass_phrase_dialog,
+                         prev->pass_phrase_dialog, NGX_SSL_PASS_PHRASE_DEFAULT_VAL);
 
     conf->ssl.log = cf->log;
 
@@ -779,6 +794,23 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
     cln->handler = ngx_ssl_cleanup_ctx;
     cln->data = &conf->ssl;
 
+    /** directive format: ssl_pass_phrase_dialog buildin|exec:filepath */
+    if (ngx_strncasecmp(conf->pass_phrase_dialog.data, (u_char *)"exec:", 5) == 0){
+       ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+            "ssl_pass_phrase_dialog config directive SET: %s ", conf->pass_phrase_dialog.data);
+    } else if (ngx_strncasecmp(conf->pass_phrase_dialog.data, (u_char *)NGX_SSL_PASS_PHRASE_DEFAULT_VAL,
+               sizeof(NGX_SSL_PASS_PHRASE_DEFAULT_VAL)) != 0){
+
+       ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+            "ssl_pass_phrase_dialog config directive accepts only the following "
+            "values: %s | exec:filepath", NGX_SSL_PASS_PHRASE_DEFAULT_VAL);
+
+       return NGX_CONF_ERROR;
+    }
+
+    dlg.data = &conf->pass_phrase_dialog;
+    dlg.server = &ngx_ssl_stream_default_name;
+
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
     SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,
                                            ngx_stream_ssl_servername);
@@ -823,7 +855,7 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
         /* configure certificates */
 
         if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,
-                                 conf->certificate_keys, conf->passwords)
+                                 conf->certificate_keys, conf->passwords, &dlg)
             != NGX_OK)
         {
             return NGX_CONF_ERROR;
@@ -1209,3 +1241,31 @@ ngx_stream_ssl_init(ngx_conf_t *cf)
 
     return NGX_OK;
 }
+
+static char *
+ngx_conf_set_pass_phrase_dialog(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_stream_ssl_conf_t     *sscf = conf;
+    ngx_str_t                 *value;
+
+    if (sscf->pass_phrase_dialog.data){
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    sscf->pass_phrase_dialog = value[1];
+
+    if (sscf->pass_phrase_dialog.len == 0) {
+        return NGX_CONF_OK;
+    } else if (sscf->pass_phrase_dialog.len > NGX_SSL_PASS_PHRASE_ARG_MAX_LEN) {
+        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+            "ssl_pass_phrase_dialog argument length exceeded maximum possible length: %d",
+            NGX_SSL_PASS_PHRASE_ARG_MAX_LEN);
+
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
diff --git a/src/stream/ngx_stream_ssl_module.h b/src/stream/ngx_stream_ssl_module.h
index e7c825e..d80daa4 100644
--- a/src/stream/ngx_stream_ssl_module.h
+++ b/src/stream/ngx_stream_ssl_module.h
@@ -56,6 +56,8 @@ typedef struct {
 
     u_char          *file;
     ngx_uint_t       line;
+
+    ngx_str_t        pass_phrase_dialog;
 } ngx_stream_ssl_conf_t;