]> granicus.if.org Git - apache/commitdiff
On the trunk:
authorStefan Eissing <icing@apache.org>
Tue, 5 Sep 2017 13:10:11 +0000 (13:10 +0000)
committerStefan Eissing <icing@apache.org>
Tue, 5 Sep 2017 13:10:11 +0000 (13:10 +0000)
  *) mod_md: v0.9.1:
     - various fixes in MDRenewWindow handling when specifying percent. Serialization changed. If
       someone already used percent configurations, it is advised to change these to a new value,
       reload and change back to the wanted ones.
     - various fixes in handling of MDPrivateKeys when specifying 2048 bits (the default) explicitly.
     - mod_md version removed from top level md_store.json file. The store has its own format version
       to facilitate upgrades.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1807347 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
modules/md/md.h
modules/md/md_core.c
modules/md/md_crypt.c
modules/md/md_crypt.h
modules/md/md_reg.c
modules/md/md_reg.h
modules/md/md_store_fs.c
modules/md/md_version.h
modules/md/mod_md.c
modules/md/mod_md_config.c

diff --git a/CHANGES b/CHANGES
index 10ae87660a00219bbe9f9ce25bcf24368e8d8b54..4ca0e4cc8227e3a34aaaf529c80c4b80040ee038 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,15 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.0
 
+  *) mod_md: v0.9.1:
+     - various fixes in MDRenewWindow handling when specifying percent. Serialization changed. If 
+       someone already used percent configurations, it is advised to change these to a new value,
+       reload and change back to the wanted ones.
+     - various fixes in handling of MDPrivateKeys when specifying 2048 bits (the default) explicitly.
+     - mod_md version removed from top level md_store.json file. The store has its own format version
+       to facilitate upgrades.
+    [Stefan Eissing]
+
   *) mod_http2: DoS flow control protection is less agressive as long as active tasks stay
      below worker capacity. Intended to fix problems with media streaming. [Stefan Eissing]
      
index fb33460831b23a74cb8cc361cf841585e0f187b4..174363a41fc81e40d0305a993fd923660898b562 100644 (file)
@@ -28,7 +28,9 @@ struct md_srv_conf_t;
 struct md_pkey_spec_t;
 
 #define MD_TLSSNI01_DNS_SUFFIX     ".acme.invalid"
-#define MD_PKEY_RSA_BITS_DEF       2048U
+
+#define MD_PKEY_RSA_BITS_MIN       2048
+#define MD_PKEY_RSA_BITS_DEF       2048
 
 typedef enum {
     MD_S_UNKNOWN,                   /* MD has not been analysed yet */
@@ -121,7 +123,7 @@ struct md_t {
 #define MD_KEY_PKEY             "privkey"
 #define MD_KEY_PROTO            "proto"
 #define MD_KEY_REGISTRATION     "registration"
-#define MD_KEY_RENEW_NORM       "renew-norm"
+#define MD_KEY_RENEW            "renew"
 #define MD_KEY_RENEW_WINDOW     "renew-window"
 #define MD_KEY_RESOURCE         "resource"
 #define MD_KEY_STATE            "state"
@@ -237,6 +239,11 @@ md_t *md_merge(apr_pool_t *p, const md_t *add, const md_t *base);
 struct md_json_t *md_to_json (const md_t *md, apr_pool_t *p);
 md_t *md_from_json(struct md_json_t *json, apr_pool_t *p);
 
+/**
+ * Determine if MD should renew its cert (if it has one)
+ */
+int md_should_renew(const md_t *md);
+
 /**************************************************************************************************/
 /* domain credentials */
 
index 8294ba505cbcdeefba248b5b21ed217ff4e1f6fc..5582ae6b9b445931d8ee1b2dd4515d11ea731d6c 100644 (file)
@@ -201,6 +201,33 @@ md_t *md_create(apr_pool_t *p, apr_array_header_t *domains)
     return md;
 }
 
+int md_should_renew(const md_t *md) 
+{
+    apr_time_t now = apr_time_now();
+
+    if (md->expires <= now) {
+        return 1;
+    }
+    else if (md->expires > 0) {
+        apr_interval_time_t renew_win, left, life;
+        
+        renew_win = md->renew_window;
+        if (md->renew_norm > 0 
+            && md->renew_norm > renew_win
+            && md->expires > md->valid_from) {
+            /* Calc renewal days as fraction of cert lifetime - if known */
+            life = md->expires - md->valid_from; 
+            renew_win = (apr_time_t)(life * ((double)renew_win / md->renew_norm));
+        }
+        
+        left = md->expires - now;
+        if (left <= renew_win) {
+            return 1;
+        }                
+    }
+    return 0;
+}
+
 /**************************************************************************************************/
 /* lifetime */
 
@@ -304,12 +331,13 @@ md_json_t *md_to_json(const md_t *md, apr_pool_t *p)
             md_json_sets(ts, json, MD_KEY_CERT, MD_KEY_VALID_FROM, NULL);
         }
         if (md->renew_norm > 0) {
-            md_json_setl((long)apr_time_sec(md->renew_norm), json, MD_KEY_RENEW_NORM, NULL);
-            md_json_setl((long)apr_time_sec(md->renew_window), json, MD_KEY_RENEW_WINDOW, NULL);
+            md_json_sets(apr_psprintf(p, "%ld%%", (long)(md->renew_window * 100L / md->renew_norm)), 
+                                      json, MD_KEY_RENEW_WINDOW, NULL);
         }
         else {
             md_json_setl((long)apr_time_sec(md->renew_window), json, MD_KEY_RENEW_WINDOW, NULL);
         }
+        md_json_setb(md_should_renew(md), json, MD_KEY_RENEW, NULL);
         if (md->ca_challenges && md->ca_challenges->nelts > 0) {
             apr_array_header_t *na;
             na = md_array_str_compact(p, md->ca_challenges, 0);
@@ -348,8 +376,18 @@ md_t *md_from_json(md_json_t *json, apr_pool_t *p)
         if (s && *s) {
             md->valid_from = apr_date_parse_rfc(s);
         }
-        md->renew_norm = apr_time_from_sec(md_json_getl(json, MD_KEY_RENEW_NORM, NULL));
+        md->renew_norm = 0;
         md->renew_window = apr_time_from_sec(md_json_getl(json, MD_KEY_RENEW_WINDOW, NULL));
+        if (md->renew_window <= 0) {
+            const char *s = md_json_gets(json, MD_KEY_RENEW_WINDOW, NULL);
+            if (s && strchr(s, '%')) {
+                int percent = atoi(s);
+                if (0 < percent && percent < 100) {
+                    md->renew_norm = apr_time_from_sec(100 * MD_SECS_PER_DAY);
+                    md->renew_window = apr_time_from_sec(percent * MD_SECS_PER_DAY);
+                }
+            }
+        }
         if (md_json_has_key(json, MD_KEY_CA, MD_KEY_CHALLENGES, NULL)) {
             md->ca_challenges = apr_array_make(p, 5, sizeof(const char*));
             md_json_dupsa(md->ca_challenges, p, json, MD_KEY_CA, MD_KEY_CHALLENGES, NULL);
index 10ddf51ed91c3e420ff54130193ec21f32091ae8..7b27075a8f62ab76f6980d9a413206e81b181180 100644 (file)
@@ -191,7 +191,7 @@ md_json_t *md_pkey_spec_to_json(const md_pkey_spec_t *spec, apr_pool_t *p)
                 break;
             case MD_PKEY_TYPE_RSA:
                 md_json_sets("RSA", json, MD_KEY_TYPE, NULL);
-                if (spec->params.rsa.bits > 2048) {
+                if (spec->params.rsa.bits >= MD_PKEY_RSA_BITS_MIN) {
                     md_json_setl(spec->params.rsa.bits, json, MD_KEY_BITS, NULL);
                 }
                 break;
@@ -217,14 +217,36 @@ md_pkey_spec_t *md_pkey_spec_from_json(struct md_json_t *json, apr_pool_t *p)
         else if (!apr_strnatcasecmp("RSA", s)) {
             spec->type = MD_PKEY_TYPE_RSA;
             l = md_json_getl(json, MD_KEY_BITS, NULL);
-            if (l > 2048) {
+            if (l >= MD_PKEY_RSA_BITS_MIN) {
                 spec->params.rsa.bits = (unsigned int)l;
             }
+            else {
+                spec->params.rsa.bits = MD_PKEY_RSA_BITS_DEF;
+            }
         }
     }
     return spec;
 }
 
+int md_pkey_spec_eq(md_pkey_spec_t *spec1, md_pkey_spec_t *spec2)
+{
+    if (spec1 == spec2) {
+        return 1;
+    }
+    if (spec1 && spec2 && spec1->type == spec2->type) {
+        switch (spec1->type) {
+            case MD_PKEY_TYPE_DEFAULT:
+                return 1;
+            case MD_PKEY_TYPE_RSA:
+                if (spec1->params.rsa.bits == spec2->params.rsa.bits) {
+                    return 1;
+                }
+                break;
+        }
+    }
+    return 0;
+}
+
 static md_pkey_t *make_pkey(apr_pool_t *p) 
 {
     md_pkey_t *pkey = apr_pcalloc(p, sizeof(*pkey));
@@ -363,7 +385,7 @@ static apr_status_t gen_rsa(md_pkey_t **ppkey, apr_pool_t *p, unsigned int bits)
         rv = APR_SUCCESS;
     }
     else {
-        md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, p, "unable to generate new key"); 
+        md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, p, "error generate pkey RSA %d", bits); 
         *ppkey = NULL;
         rv = APR_EGENERAL;
     }
index 5a536559262182b3954da2113b79c071af839c9d..401e6403a3f0d492b5c23c8c929da610946eeb7d 100644 (file)
@@ -80,6 +80,7 @@ void *md_pkey_get_EVP_PKEY(struct md_pkey_t *pkey);
 
 struct md_json_t *md_pkey_spec_to_json(const md_pkey_spec_t *spec, apr_pool_t *p);
 md_pkey_spec_t *md_pkey_spec_from_json(struct md_json_t *json, apr_pool_t *p);
+int md_pkey_spec_eq(md_pkey_spec_t *spec1, md_pkey_spec_t *spec2);
 
 /**************************************************************************************************/
 /* X509 certificates */
index 96a294be7c39a462471a8ae8b199a45ff8064c86..6a32508f97039b543b9fa44628c10df6bf6454f3 100644 (file)
@@ -151,7 +151,7 @@ static apr_status_t check_values(md_reg_t *reg, apr_pool_t *p, const md_t *md, i
 /**************************************************************************************************/
 /* state assessment */
 
-static apr_status_t state_init(md_reg_t *reg, apr_pool_t *p, md_t *md)
+static apr_status_t state_init(md_reg_t *reg, apr_pool_t *p, md_t *md, int save_changes)
 {
     md_state_t state = MD_S_UNKNOWN;
     const md_creds_t *creds;
@@ -166,7 +166,7 @@ static apr_status_t state_init(md_reg_t *reg, apr_pool_t *p, md_t *md)
             md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, 
                           "md{%s}: incomplete, without private key", md->name);
         }
-        else if (!creds->pubcert) {
+        else if (!creds->cert) {
             md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, 
                           "md{%s}: incomplete, has key but no certificate", md->name);
         }
@@ -216,41 +216,24 @@ out:
         state = MD_S_ERROR;
         md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p, "md{%s}: error", md->name);
     }
+    
+    if (save_changes && md->state == state
+        && md->valid_from == valid_from && md->expires == expires) {
+        save_changes = 0;
+    }
     md->state = state;
     md->valid_from = valid_from;
     md->expires = expires;
-    return rv;
-}
-
-static apr_status_t state_vinit(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_list ap)
-{
-    md_reg_t *reg = baton;
-    md_t *md = va_arg(ap, md_t *);
-    
-    return state_init(reg, p, md);
-}
-
-static apr_status_t md_state_init(md_reg_t *reg, md_t *md, apr_pool_t *p)
-{
-    return md_util_pool_vdo(state_vinit, reg, p, md, NULL);
-}
-
-static md_t *state_check(md_reg_t *reg, md_t *md, apr_pool_t *p) 
-{
-    if (md) {
-        int ostate = md->state;
-        if (APR_SUCCESS == state_init(reg, p, md) && md->state != ostate) {
-            md_save(reg->store, p, MD_SG_DOMAINS, md, 0);
-        }
+    if (save_changes && APR_SUCCESS == rv) {
+        return md_save(reg->store, p, MD_SG_DOMAINS, md, 0);
     }
-    return md;
+    return rv;
 }
 
 apr_status_t md_reg_assess(md_reg_t *reg, md_t *md, int *perrored, int *prenew, apr_pool_t *p)
 {
     int renew = 0;
     int errored = 0;
-    apr_time_t now = apr_time_now();
     
     switch (md->state) {
         case MD_S_UNKNOWN:
@@ -271,31 +254,13 @@ apr_status_t md_reg_assess(md_reg_t *reg, md_t *md, int *perrored, int *prenew,
                              "md(%s): looks complete, but has unknown expiration date.", md->name);
                 errored = 1;
             }
-            else if (md->expires <= now) {
+            else if (md->expires <= apr_time_now()) {
                 /* Maybe we hibernated in the meantime? */
                 md->state = MD_S_EXPIRED;
                 renew = 1;
             }
             else {
-                apr_interval_time_t renew_win, left, life;
-
-                renew_win = md->renew_window;
-                if (md->renew_norm > 0 
-                    && md->renew_norm > renew_win
-                    && md->expires > md->valid_from) {
-                    /* Calc renewal days as fraction of cert lifetime - if known */
-                    life = md->expires - md->valid_from; 
-                    renew_win = life * md->renew_norm / renew_win;
-                }
-                
-                left = md->expires - now;
-                if (left <= renew_win) {
-                    int days_left = (int)(apr_time_sec(left) / MD_SECS_PER_DAY);
-                    md_log_perror( MD_LOG_MARK, MD_LOG_DEBUG, 0, p,  
-                                  "md(%s): %d days to expiry, attempt renewal", 
-                                  md->name, days_left);
-                    renew = 1;
-                }                
+                renew = md_should_renew(md);
             }
             break;
         case MD_S_INCOMPLETE:
@@ -326,7 +291,7 @@ static int reg_md_iter(void *baton, md_store_t *store, md_t *md, apr_pool_t *pte
     reg_do_ctx *ctx = baton;
     
     if (!ctx->exclude || strcmp(ctx->exclude, md->name)) {
-        md = state_check(ctx->reg, (md_t*)md, ptemp);
+        state_init(ctx->reg, ptemp, (md_t*)md, 1);
         return ctx->cb(ctx->baton, ctx->reg, md);
     }
     return 1;
@@ -357,7 +322,8 @@ md_t *md_reg_get(md_reg_t *reg, const char *name, apr_pool_t *p)
     md_t *md;
     
     if (APR_SUCCESS == md_load(reg->store, MD_SG_DOMAINS, name, &md, p)) {
-        return state_check(reg, md, p);
+        state_init(reg, p, md, 1);
+        return md;
     }
     return NULL;
 }
@@ -386,12 +352,15 @@ md_t *md_reg_find(md_reg_t *reg, const char *domain, apr_pool_t *p)
     ctx.md = NULL;
     
     md_reg_do(find_domain, &ctx, reg, p);
-    return state_check(reg, (md_t*)ctx.md, p);
+    if (ctx.md) {
+        state_init(reg, p, ctx.md, 1);
+    }
+    return ctx.md;
 }
 
 typedef struct {
     const md_t *md_checked;
-    const md_t *md;
+    md_t *md;
     const char *s;
 } find_overlap_ctx;
 
@@ -420,7 +389,10 @@ md_t *md_reg_find_overlap(md_reg_t *reg, const md_t *md, const char **pdomain, a
     if (pdomain && ctx.s) {
         *pdomain = ctx.s;
     }
-    return state_check(reg, (md_t*)ctx.md, p);
+    if (ctx.md) {
+        state_init(reg, p, ctx.md, 1);
+    }
+    return ctx.md;
 }
 
 apr_status_t md_reg_get_cred_files(md_reg_t *reg, const md_t *md, apr_pool_t *p,
@@ -447,7 +419,7 @@ static apr_status_t p_md_add(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_l
     md = va_arg(ap, md_t *);
     mine = md_clone(ptemp, md);
     if (APR_SUCCESS == (rv = check_values(reg, ptemp, md, MD_UPD_ALL))
-        && APR_SUCCESS == (rv = md_state_init(reg, mine, ptemp))
+        && APR_SUCCESS == (rv = state_init(reg, ptemp, mine, 0))
         && APR_SUCCESS == (rv = md_save(reg->store, p, MD_SG_DOMAINS, mine, 1))) {
     }
     return rv;
@@ -525,9 +497,16 @@ static apr_status_t p_md_update(void *baton, apr_pool_t *p, apr_pool_t *ptemp, v
         nmd->ca_challenges = (updates->ca_challenges? 
                               apr_array_copy(p, updates->ca_challenges) : NULL);
     }
+    if (MD_UPD_PKEY_SPEC & fields) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, ptemp, "update pkey spec: %s", name);
+        nmd->pkey_spec = NULL;
+        if (updates->pkey_spec) {
+            nmd->pkey_spec = apr_pmemdup(p, updates->pkey_spec, sizeof(md_pkey_spec_t));
+        }
+    }
     
     if (fields && APR_SUCCESS == (rv = md_save(reg->store, p, MD_SG_DOMAINS, nmd, 0))) {
-        rv = md_state_init(reg, nmd, ptemp);
+        rv = state_init(reg, ptemp, nmd, 0);
     }
     return rv;
 }
@@ -792,6 +771,13 @@ apr_status_t md_reg_sync(md_reg_t *reg, apr_pool_t *p, apr_pool_t *ptemp,
                     smd->ca_challenges = NULL;
                     fields |= MD_UPD_CA_CHALLENGES;
                 }
+                if (!md_pkey_spec_eq(md->pkey_spec, smd->pkey_spec)) {
+                    fields |= MD_UPD_PKEY_SPEC;
+                    smd->pkey_spec = NULL;
+                    if (md->pkey_spec) {
+                        smd->pkey_spec = apr_pmemdup(p, md->pkey_spec, sizeof(md_pkey_spec_t));
+                    }
+                }
                 
                 if (fields) {
                     rv = md_reg_update(reg, ptemp, smd->name, smd, fields);
index 1ab0dc0fd58640d2b7e38f1eb8576d2b05cda204..d889b8abd02c0b16dc00bb0cb7533526e0f1f63a 100644 (file)
@@ -91,7 +91,8 @@ int md_reg_do(md_reg_do_cb *cb, void *baton, md_reg_t *reg, apr_pool_t *p);
 #define MD_UPD_DRIVE_MODE   0x0080
 #define MD_UPD_RENEW_WINDOW 0x0100
 #define MD_UPD_CA_CHALLENGES 0x0200
-#define MD_UPD_ALL          0x7FFF
+#define MD_UPD_PKEY_SPEC    0x0400
+#define MD_UPD_ALL          0x7FFFFFFF
 
 /**
  * Update the given fields for the managed domain. Take the new
index 49f73945850ba96f7e0ad6e324fefff74865f032..0ba017c25630b81ad603f7e910162726daa2ff01 100644 (file)
@@ -37,7 +37,7 @@
 /**************************************************************************************************/
 /* file system based implementation of md_store_t */
 
-#define MD_STORE_VERSION        2
+#define MD_STORE_VERSION        3
 
 typedef struct {
     apr_fileperms_t dir;
@@ -99,7 +99,6 @@ static apr_status_t init_store_file(md_store_fs_t *s_fs, const char *fname,
     unsigned char *key;
     apr_status_t rv;
     
-    md_json_sets(MOD_MD_VERSION, json, MD_KEY_VERSION, NULL);
     md_json_setn(MD_STORE_VERSION, json, MD_KEY_STORE, MD_KEY_VERSION, NULL);
 
     s_fs->key_len = FS_STORE_KLEN;
@@ -217,9 +216,13 @@ static apr_status_t read_store_file(md_store_fs_t *s_fs, const char *fname,
         /* Need to migrate format? */
         if (store_version < MD_STORE_VERSION) {
             if (store_version <= 1.0) {
-                md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, "migrating store v1.0 -> v1.1");
+                md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, "migrating store v1 -> v2");
                 rv = upgrade_from_1_0(s_fs, p, ptemp);
             }
+            if (store_version <= 2.0) {
+                md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, "migrating store v2 -> v3");
+                md_json_del(json, MD_KEY_VERSION, NULL);
+            }
             
             if (APR_SUCCESS == rv) {
                 md_json_setn(MD_STORE_VERSION, json, MD_KEY_STORE, MD_KEY_VERSION, NULL);
index 5ef47d426b9de76e215fe183db186a2c6c3dc8a5..7c03dc75489f8908adbf265eaab1130851611e91 100644 (file)
@@ -26,7 +26,7 @@
  * @macro
  * Version number of the md module as c string
  */
-#define MOD_MD_VERSION "0.9.0"
+#define MOD_MD_VERSION "0.9.1-git"
 
 /**
  * @macro
@@ -34,7 +34,7 @@
  * release. This is a 24 bit number with 8 bits for major number, 8 bits
  * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
  */
-#define MOD_MD_VERSION_NUM 0x000900
+#define MOD_MD_VERSION_NUM 0x000901
 
 #define MD_EXPERIMENTAL 0
 #define MD_ACME_DEF_URL    "https://acme-v01.api.letsencrypt.org/directory"
index 4d8721651b29c877396fba1d8c9c0056cad1c620..ac31aa98481e98e23c2700680110c933b5b9cded 100644 (file)
@@ -78,10 +78,8 @@ static void md_merge_srv(md_t *md, md_srv_conf_t *base_sc, apr_pool_t *p)
     if (md->drive_mode == MD_DRIVE_DEFAULT) {
         md->drive_mode = md_config_geti(md->sc, MD_CONFIG_DRIVE_MODE);
     }
-    if (md->renew_norm <= 0) {
+    if (md->renew_norm <= 0 && md->renew_window <= 0) {
         md->renew_norm = md_config_get_interval(md->sc, MD_CONFIG_RENEW_NORM);
-    }
-    if (md->renew_window <= 0) {
         md->renew_window = md_config_get_interval(md->sc, MD_CONFIG_RENEW_WINDOW);
     }
     if (md->transitive < 0) {
index 17fbbe3f2c558b47ae8eb2759372c7ef970ac07b..85e30d61e90bab2c3db81a751fc9b8f0f077a1e3 100644 (file)
@@ -496,8 +496,8 @@ static const char *md_config_set_renew_window(cmd_parms *cmd, void *dc, const ch
     else {
         switch (percentage_parse(value, &percent)) {
             case APR_SUCCESS:
-                config->renew_norm = 100;
-                config->renew_window = percent;
+                config->renew_norm = apr_time_from_sec(100 * MD_SECS_PER_DAY);
+                config->renew_window = apr_time_from_sec(percent * MD_SECS_PER_DAY);
                 return NULL;
             case APR_BADARG:
                 return "MDRenewWindow as percent must be less than 100";
@@ -630,10 +630,11 @@ static const char *md_config_set_pkeys(cmd_parms *cmd, void *arg,
         }
         else if (argc == 2) {
             bits = (int)apr_atoi64(argv[1]);
-            if (bits < 2048 || bits >= INT_MAX) {
-                return "must be a 2048 or higher in order to be considered safe. "
-                "Too large a value will slow down everything. Larger then 4096 probably does "
-                "not make sense unless quantum cryptography really changes spin.";
+            if (bits < MD_PKEY_RSA_BITS_MIN || bits >= INT_MAX) {
+                return apr_psprintf(cmd->pool, "must be %d or higher in order to be considered "
+                "safe. Too large a value will slow down everything. Larger then 4096 probably does "
+                "not make sense unless quantum cryptography really changes spin.", 
+                MD_PKEY_RSA_BITS_MIN);
             }
         }
         else {