Add 'openssl req' option to specify extension values on command line
authorRichard Levitte <levitte@openssl.org>
Wed, 27 Dec 2017 17:29:36 +0000 (18:29 +0100)
committerRichard Levitte <levitte@openssl.org>
Thu, 28 Dec 2017 15:07:58 +0000 (16:07 +0100)
The idea is to be able to add extension value lines directly on the
command line instead of through the config file, for example:

    openssl req -new -extension 'subjectAltName = DNS:dom.ain, DNS:oth.er' \
                     -extension 'certificatePolicies = 1.2.3.4'

Fixes #3311

Thank you Jacob Hoffman-Andrews for the inspiration

Reviewed-by: Andy Polyakov <appro@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/4986)

apps/apps.c
apps/apps.h
apps/req.c
doc/man1/req.pod

index 30405662571ddf9573592d129f1903a5c89f2c86..834cedd5a33db8f623301cce115e0648f59de605 100644 (file)
@@ -442,7 +442,7 @@ static char *app_get_pass(const char *arg, int keepbio)
     return OPENSSL_strdup(tpass);
 }
 
     return OPENSSL_strdup(tpass);
 }
 
-static CONF *app_load_config_(BIO *in, const char *filename)
+CONF *app_load_config_bio(BIO *in, const char *filename)
 {
     long errorline = -1;
     CONF *conf;
 {
     long errorline = -1;
     CONF *conf;
@@ -453,12 +453,17 @@ static CONF *app_load_config_(BIO *in, const char *filename)
     if (i > 0)
         return conf;
 
     if (i > 0)
         return conf;
 
-    if (errorline <= 0)
-        BIO_printf(bio_err, "%s: Can't load config file \"%s\"\n",
-                   opt_getprog(), filename);
+    if (errorline <= 0) {
+        BIO_printf(bio_err, "%s: Can't load ", opt_getprog());
+    } else {
+        BIO_printf(bio_err, "%s: Error on line %ld of ", opt_getprog(),
+                   errorline);
+    }
+    if (filename != NULL)
+        BIO_printf(bio_err, "config file \"%s\"\n", filename);
     else
     else
-        BIO_printf(bio_err, "%s: Error on line %ld of config file \"%s\"\n",
-                   opt_getprog(), errorline, filename);
+        BIO_printf(bio_err, "config input");
+
     NCONF_free(conf);
     return NULL;
 }
     NCONF_free(conf);
     return NULL;
 }
@@ -472,7 +477,7 @@ CONF *app_load_config(const char *filename)
     if (in == NULL)
         return NULL;
 
     if (in == NULL)
         return NULL;
 
-    conf = app_load_config_(in, filename);
+    conf = app_load_config_bio(in, filename);
     BIO_free(in);
     return conf;
 }
     BIO_free(in);
     return conf;
 }
@@ -486,7 +491,7 @@ CONF *app_load_config_quiet(const char *filename)
     if (in == NULL)
         return NULL;
 
     if (in == NULL)
         return NULL;
 
-    conf = app_load_config_(in, filename);
+    conf = app_load_config_bio(in, filename);
     BIO_free(in);
     return conf;
 }
     BIO_free(in);
     return conf;
 }
index 321f6444da8a1e00333b80780c1103d5627b57cf..6d0d7015f886e61b1196ffac1af52bfd7f0498eb 100644 (file)
@@ -52,6 +52,7 @@ BIO *dup_bio_err(int format);
 BIO *bio_open_owner(const char *filename, int format, int private);
 BIO *bio_open_default(const char *filename, char mode, int format);
 BIO *bio_open_default_quiet(const char *filename, char mode, int format);
 BIO *bio_open_owner(const char *filename, int format, int private);
 BIO *bio_open_default(const char *filename, char mode, int format);
 BIO *bio_open_default_quiet(const char *filename, char mode, int format);
+CONF *app_load_config_bio(BIO *in, const char *filename);
 CONF *app_load_config(const char *filename);
 CONF *app_load_config_quiet(const char *filename);
 int app_load_modules(const CONF *config);
 CONF *app_load_config(const char *filename);
 CONF *app_load_config_quiet(const char *filename);
 int app_load_modules(const CONF *config);
index 989a6ad7cd85b08700a882263b8e3590990f8d1f..dca6038a285481a4812ba2448ace7aab275222fe 100644 (file)
@@ -70,6 +70,7 @@ static EVP_PKEY_CTX *set_keygen_ctx(const char *gstr,
                                     int *pkey_type, long *pkeylen,
                                     char **palgnam, ENGINE *keygen_engine);
 static CONF *req_conf = NULL;
                                     int *pkey_type, long *pkeylen,
                                     char **palgnam, ENGINE *keygen_engine);
 static CONF *req_conf = NULL;
+static CONF *addext_conf = NULL;
 static int batch = 0;
 
 typedef enum OPTION_choice {
 static int batch = 0;
 
 typedef enum OPTION_choice {
@@ -80,7 +81,7 @@ typedef enum OPTION_choice {
     OPT_PKEYOPT, OPT_SIGOPT, OPT_BATCH, OPT_NEWHDR, OPT_MODULUS,
     OPT_VERIFY, OPT_NODES, OPT_NOOUT, OPT_VERBOSE, OPT_UTF8,
     OPT_NAMEOPT, OPT_REQOPT, OPT_SUBJ, OPT_SUBJECT, OPT_TEXT, OPT_X509,
     OPT_PKEYOPT, OPT_SIGOPT, OPT_BATCH, OPT_NEWHDR, OPT_MODULUS,
     OPT_VERIFY, OPT_NODES, OPT_NOOUT, OPT_VERBOSE, OPT_UTF8,
     OPT_NAMEOPT, OPT_REQOPT, OPT_SUBJ, OPT_SUBJECT, OPT_TEXT, OPT_X509,
-    OPT_MULTIVALUE_RDN, OPT_DAYS, OPT_SET_SERIAL, OPT_EXTENSIONS,
+    OPT_MULTIVALUE_RDN, OPT_DAYS, OPT_SET_SERIAL, OPT_ADDEXT, OPT_EXTENSIONS,
     OPT_REQEXTS, OPT_PRECERT, OPT_MD,
     OPT_R_ENUM
 } OPTION_CHOICE;
     OPT_REQEXTS, OPT_PRECERT, OPT_MD,
     OPT_R_ENUM
 } OPTION_CHOICE;
@@ -124,6 +125,8 @@ const OPTIONS req_options[] = {
      "Enable support for multivalued RDNs"},
     {"days", OPT_DAYS, 'p', "Number of days cert is valid for"},
     {"set_serial", OPT_SET_SERIAL, 's', "Serial number to use"},
      "Enable support for multivalued RDNs"},
     {"days", OPT_DAYS, 'p', "Number of days cert is valid for"},
     {"set_serial", OPT_SET_SERIAL, 's', "Serial number to use"},
+    {"addext", OPT_ADDEXT, 's',
+     "Additional cert extension key=value pair (may be given more than once)"},
     {"extensions", OPT_EXTENSIONS, 's',
      "Cert extension section (override value in config file)"},
     {"reqexts", OPT_REQEXTS, 's',
     {"extensions", OPT_EXTENSIONS, 's',
      "Cert extension section (override value in config file)"},
     {"reqexts", OPT_REQEXTS, 's',
@@ -150,6 +153,7 @@ int req_main(int argc, char **argv)
     X509_REQ *req = NULL;
     const EVP_CIPHER *cipher = NULL;
     const EVP_MD *md_alg = NULL, *digest = NULL;
     X509_REQ *req = NULL;
     const EVP_CIPHER *cipher = NULL;
     const EVP_MD *md_alg = NULL, *digest = NULL;
+    BIO *addext_bio = NULL;
     char *extensions = NULL, *infile = NULL;
     char *outfile = NULL, *keyfile = NULL;
     char *keyalgstr = NULL, *p, *prog, *passargin = NULL, *passargout = NULL;
     char *extensions = NULL, *infile = NULL;
     char *outfile = NULL, *keyfile = NULL;
     char *keyalgstr = NULL, *p, *prog, *passargin = NULL, *passargout = NULL;
@@ -313,6 +317,14 @@ int req_main(int argc, char **argv)
         case OPT_MULTIVALUE_RDN:
             multirdn = 1;
             break;
         case OPT_MULTIVALUE_RDN:
             multirdn = 1;
             break;
+        case OPT_ADDEXT:
+            if (addext_bio == NULL) {
+                addext_bio = BIO_new(BIO_s_mem());
+            }
+            if (addext_bio == NULL
+                || BIO_printf(addext_bio, "%s\n", opt_arg()) < 0)
+                goto end;
+            break;
         case OPT_EXTENSIONS:
             extensions = opt_arg();
             break;
         case OPT_EXTENSIONS:
             extensions = opt_arg();
             break;
@@ -349,6 +361,12 @@ int req_main(int argc, char **argv)
     if (verbose)
         BIO_printf(bio_err, "Using configuration from %s\n", template);
     req_conf = app_load_config(template);
     if (verbose)
         BIO_printf(bio_err, "Using configuration from %s\n", template);
     req_conf = app_load_config(template);
+    if (addext_bio) {
+        if (verbose)
+            BIO_printf(bio_err,
+                       "Using additional configuraton from command line\n");
+        addext_conf = app_load_config_bio(addext_bio, NULL);
+    }
     if (template != default_config_file && !app_load_modules(req_conf))
         goto end;
 
     if (template != default_config_file && !app_load_modules(req_conf))
         goto end;
 
@@ -401,6 +419,16 @@ int req_main(int argc, char **argv)
             goto end;
         }
     }
             goto end;
         }
     }
+    if (addext_conf != NULL) {
+        /* Check syntax of command line extensions */
+        X509V3_CTX ctx;
+        X509V3_set_ctx_test(&ctx);
+        X509V3_set_nconf(&ctx, addext_conf);
+        if (!X509V3_EXT_add_nconf(addext_conf, &ctx, "default", NULL)) {
+            BIO_printf(bio_err, "Error Loading command line extensions\n");
+            goto end;
+        }
+    }
 
     if (passin == NULL) {
         passin = nofree_passin =
 
     if (passin == NULL) {
         passin = nofree_passin =
@@ -605,7 +633,8 @@ int req_main(int argc, char **argv)
                 goto end;
 
             /* Set version to V3 */
                 goto end;
 
             /* Set version to V3 */
-            if (extensions != NULL && !X509_set_version(x509ss, 2))
+            if ((extensions != NULL || addext_conf != NULL)
+                && !X509_set_version(x509ss, 2))
                 goto end;
             if (serial != NULL) {
                 if (!X509_set_serialNumber(x509ss, serial))
                 goto end;
             if (serial != NULL) {
                 if (!X509_set_serialNumber(x509ss, serial))
@@ -643,6 +672,12 @@ int req_main(int argc, char **argv)
                            extensions);
                 goto end;
             }
                            extensions);
                 goto end;
             }
+            if (addext_conf != NULL
+                && !X509V3_EXT_add_nconf(addext_conf, &ext_ctx, "default",
+                                         x509ss)) {
+                BIO_printf(bio_err, "Error Loading command line extensions\n");
+                goto end;
+            }
 
             /* If a pre-cert was requested, we need to add a poison extension */
             if (precert) {
 
             /* If a pre-cert was requested, we need to add a poison extension */
             if (precert) {
@@ -674,6 +709,12 @@ int req_main(int argc, char **argv)
                            req_exts);
                 goto end;
             }
                            req_exts);
                 goto end;
             }
+            if (addext_conf != NULL
+                && !X509V3_EXT_REQ_add_nconf(addext_conf, &ext_ctx, "default",
+                                             req)) {
+                BIO_printf(bio_err, "Error Loading command line extensions\n");
+                goto end;
+            }
             i = do_X509_REQ_sign(req, pkey, digest, sigopts);
             if (!i) {
                 ERR_print_errors(bio_err);
             i = do_X509_REQ_sign(req, pkey, digest, sigopts);
             if (!i) {
                 ERR_print_errors(bio_err);
@@ -817,6 +858,7 @@ int req_main(int argc, char **argv)
         ERR_print_errors(bio_err);
     }
     NCONF_free(req_conf);
         ERR_print_errors(bio_err);
     }
     NCONF_free(req_conf);
+    BIO_free(addext_bio);
     BIO_free(in);
     BIO_free_all(out);
     EVP_PKEY_free(pkey);
     BIO_free(in);
     BIO_free_all(out);
     EVP_PKEY_free(pkey);
index 5ed90ada740a53b5aa80206e8271a7f91eda143f..db467bba17f0447f88b19d11f579f35a1f4518ae 100644 (file)
@@ -37,6 +37,7 @@ B<openssl> B<req>
 [B<-days n>]
 [B<-set_serial n>]
 [B<-newhdr>]
 [B<-days n>]
 [B<-set_serial n>]
 [B<-newhdr>]
+[B<-addext ext>]
 [B<-extensions section>]
 [B<-reqexts section>]
 [B<-precert>]
 [B<-extensions section>]
 [B<-reqexts section>]
 [B<-precert>]
@@ -255,6 +256,14 @@ be a positive integer. The default is 30 days.
 Serial number to use when outputting a self signed certificate. This
 may be specified as a decimal value or a hex value if preceded by B<0x>.
 
 Serial number to use when outputting a self signed certificate. This
 may be specified as a decimal value or a hex value if preceded by B<0x>.
 
+=item B<-addext ext>
+
+Add a specific extension to the certificate (if the B<-x509> option is
+present) or certificate request.  The argument must have the form of
+a key=value pair as it would appear in a config file.
+
+This option can be given multiple times.
+
 =item B<-extensions section>
 
 =item B<-reqexts section>
 =item B<-extensions section>
 
 =item B<-reqexts section>
@@ -591,6 +600,14 @@ Sample configuration containing all field values:
  [ req_attributes ]
  challengePassword              = A challenge password
 
  [ req_attributes ]
  challengePassword              = A challenge password
 
+Example of giving the most common attributes (subject and extensions)
+on the command line:
+
+ openssl req -new -subj "/C=GB/CN=foo" \
+                  -addext "subjectAltName = DNS:foo.co.uk" \
+                  -addext "certificatePolicies = 1.2.3.4" \
+                  -newkey rsa:2048 -keyout key.pem -out req.pem
+
 
 =head1 NOTES
 
 
 =head1 NOTES