Add random serial# support.
authorRich Salz <rsalz@openssl.org>
Wed, 16 Aug 2017 19:49:25 +0000 (15:49 -0400)
committerRich Salz <rsalz@openssl.org>
Tue, 22 Aug 2017 13:00:04 +0000 (09:00 -0400)
Add -rand_serial to CA command and "serial_rand" config option.

Up RAND_BITS to 159, and comment why: now confirms to CABForum
guidelines (Ballot 164) as well as IETF RFC 5280 (PKIX).

Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/4185)

apps/apps.c
apps/apps.h
apps/ca.c
doc/man1/ca.pod
test/recipes/80-test_ca.t

index 6ff4197..79ef933 100644 (file)
@@ -1503,15 +1503,11 @@ int rand_serial(BIGNUM *b, ASN1_INTEGER *ai)
     BIGNUM *btmp;
     int ret = 0;
 
-    if (b)
-        btmp = b;
-    else
-        btmp = BN_new();
-
+    btmp = b == NULL ? BN_new() : b;
     if (btmp == NULL)
         return 0;
 
-    if (!BN_rand(btmp, SERIAL_RAND_BITS, 0, 0))
+    if (!BN_rand(btmp, SERIAL_RAND_BITS, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY))
         goto error;
     if (ai && !BN_to_ASN1_INTEGER(btmp, ai))
         goto error;
index 3b65978..fdf316a 100644 (file)
@@ -568,7 +568,12 @@ void store_setup_crl_download(X509_STORE *st);
 
 # define APP_PASS_LEN    1024
 
-# define SERIAL_RAND_BITS        64
+/*
+ * IETF RFC 5280 says serial number must be <= 20 bytes. Use 159 bits
+ * so that the first bit will never be one, so that the DER encoding
+ * rules won't force a leading octet.
+ */
+# define SERIAL_RAND_BITS        159
 
 int app_isdir(const char *);
 int app_access(const char *, int flag);
index c1c2c49..436aa85 100644 (file)
--- a/apps/ca.c
+++ b/apps/ca.c
@@ -62,6 +62,7 @@
 #define ENV_NEW_CERTS_DIR       "new_certs_dir"
 #define ENV_CERTIFICATE         "certificate"
 #define ENV_SERIAL              "serial"
+#define ENV_RAND_SERIAL         "rand_serial"
 #define ENV_CRLNUMBER           "crlnumber"
 #define ENV_PRIVATE_KEY         "private_key"
 #define ENV_DEFAULT_DAYS        "default_days"
@@ -153,6 +154,7 @@ typedef enum OPTION_choice {
     OPT_GENCRL, OPT_MSIE_HACK, OPT_CRLDAYS, OPT_CRLHOURS, OPT_CRLSEC,
     OPT_INFILES, OPT_SS_CERT, OPT_SPKAC, OPT_REVOKE, OPT_VALID,
     OPT_EXTENSIONS, OPT_EXTFILE, OPT_STATUS, OPT_UPDATEDB, OPT_CRLEXTS,
+    OPT_RAND_SERIAL,
     OPT_R_ENUM,
     /* Do not change the order here; see related case statements below */
     OPT_CRL_REASON, OPT_CRL_HOLD, OPT_CRL_COMPROMISE, OPT_CRL_CA_COMPROMISE
@@ -167,6 +169,8 @@ const OPTIONS ca_options[] = {
     {"utf8", OPT_UTF8, '-', "Input characters are UTF8 (default ASCII)"},
     {"create_serial", OPT_CREATE_SERIAL, '-',
      "If reading serial fails, create a new random serial"},
+    {"rand_serial", OPT_RAND_SERIAL, '-',
+     "Always create a random serial; do not store it"},
     {"multivalue-rdn", OPT_MULTIVALUE_RDN, '-',
      "Enable support for multivalued RDNs"},
     {"startdate", OPT_STARTDATE, 's', "Cert notBefore, YYMMDDHHMMSSZ"},
@@ -258,7 +262,7 @@ int ca_main(int argc, char **argv)
     int batch = 0, default_op = 1, doupdatedb = 0, ext_copy = EXT_COPY_NONE;
     int keyformat = FORMAT_PEM, multirdn = 0, notext = 0, output_der = 0;
     int ret = 1, email_dn = 1, req = 0, verbose = 0, gencrl = 0, dorevoke = 0;
-    int i, j, selfsign = 0;
+    int rand_ser = 0, i, j, selfsign = 0;
     long crldays = 0, crlhours = 0, crlsec = 0, days = 0;
     unsigned long chtype = MBSTRING_ASC, certopt = 0;
     X509 *x509 = NULL, *x509p = NULL, *x = NULL;
@@ -303,6 +307,9 @@ opthelp:
         case OPT_UTF8:
             chtype = MBSTRING_UTF8;
             break;
+        case OPT_RAND_SERIAL:
+            rand_ser = 1;
+            break;
         case OPT_CREATE_SERIAL:
             create_ser = 1;
             break;
@@ -774,9 +781,13 @@ end_of_options:
         if (verbose)
             BIO_printf(bio_err, "policy is %s\n", policy);
 
-        serialfile = lookup_conf(conf, section, ENV_SERIAL);
-        if (serialfile == NULL)
-            goto end;
+        if (NCONF_get_string(conf, section, ENV_RAND_SERIAL) != NULL) {
+            rand_ser = 1;
+        } else {
+            serialfile = lookup_conf(conf, section, ENV_SERIAL);
+            if (serialfile == NULL)
+                goto end;
+        }
 
         if (extconf == NULL) {
             /*
@@ -838,18 +849,25 @@ end_of_options:
             goto end;
         }
 
-        if ((serial = load_serial(serialfile, create_ser, NULL)) == NULL) {
-            BIO_printf(bio_err, "error while loading serial number\n");
-            goto end;
-        }
-        if (verbose) {
-            if (BN_is_zero(serial)) {
-                BIO_printf(bio_err, "next serial number is 00\n");
-            } else {
-                if ((f = BN_bn2hex(serial)) == NULL)
-                    goto end;
-                BIO_printf(bio_err, "next serial number is %s\n", f);
-                OPENSSL_free(f);
+        if (rand_ser) {
+            if ((serial = BN_new()) == NULL || !rand_serial(serial, NULL)) {
+                BIO_printf(bio_err, "error generating serial number\n");
+                goto end;
+            }
+        } else {
+            if ((serial = load_serial(serialfile, create_ser, NULL)) == NULL) {
+                BIO_printf(bio_err, "error while loading serial number\n");
+                goto end;
+            }
+            if (verbose) {
+                if (BN_is_zero(serial)) {
+                    BIO_printf(bio_err, "next serial number is 00\n");
+                } else {
+                    if ((f = BN_bn2hex(serial)) == NULL)
+                        goto end;
+                    BIO_printf(bio_err, "next serial number is %s\n", f);
+                    OPENSSL_free(f);
+                }
             }
         }
 
@@ -973,7 +991,8 @@ end_of_options:
             BIO_printf(bio_err, "Write out database with %d new entries\n",
                        sk_X509_num(cert_sk));
 
-            if (!save_serial(serialfile, "new", serial, NULL))
+            if (!rand_ser
+                    && !save_serial(serialfile, "new", serial, NULL))
                 goto end;
 
             if (!save_index(dbfile, "new", db))
@@ -1171,7 +1190,8 @@ end_of_options:
 
         /* we have a CRL number that need updating */
         if (crlnumberfile != NULL)
-            if (!save_serial(crlnumberfile, "new", crlnumber, NULL))
+            if (!rand_ser
+                    && !save_serial(crlnumberfile, "new", crlnumber, NULL))
                 goto end;
 
         BN_free(crlnumber);
@@ -1213,16 +1233,16 @@ end_of_options:
             BIO_printf(bio_err, "Data Base Updated\n");
         }
     }
-    /*****************************************************************/
     ret = 0;
+
  end:
+    if (ret)
+        ERR_print_errors(bio_err);
     BIO_free_all(Sout);
     BIO_free_all(out);
     BIO_free_all(in);
     sk_X509_pop_free(cert_sk, X509_free);
 
-    if (ret)
-        ERR_print_errors(bio_err);
     if (free_key)
         OPENSSL_free(key);
     BN_free(serial);
index a985631..21e692e 100644 (file)
@@ -51,6 +51,7 @@ B<openssl> B<ca>
 [B<-subj arg>]
 [B<-utf8>]
 [B<-create_serial>]
+[B<-rand_serial>]
 [B<-multivalue-rdn>]
 [B<-rand file...>]
 [B<-writerand file>]
@@ -262,6 +263,13 @@ configuration file, must be valid UTF8 strings.
 If reading serial from the text file as specified in the configuration
 fails, specifying this option creates a new random serial to be used as next
 serial number.
+To get random serial numbers, use the B<-rand_serial> flag instead; this
+should only be used for simple error-recovery.
+
+=item B<-rand_serial>
+
+Generate a large random number to use as the serial number.
+This overrides any option or configuration to use a serial number file.
 
 =item B<-multivalue-rdn>
 
@@ -614,6 +622,7 @@ A sample configuration file with the relevant sections for B<ca>:
 
  certificate    = $dir/cacert.pem       # The CA cert
  serial         = $dir/serial           # serial no file
+ #rand_serial    = yes                  # for random serial#'s
  private_key    = $dir/private/cakey.pem# CA private key
  RANDFILE       = $dir/private/.rand    # random number file
 
index 4c470fb..557777e 100644 (file)
@@ -35,7 +35,7 @@ plan tests => 5;
         if !ok(run(perlapp(["CA.pl","-newreq"])),
                'creating certificate request');
 
-     $ENV{OPENSSL_CONFIG} = '-config "'.$std_openssl_cnf.'"';
+     $ENV{OPENSSL_CONFIG} = '-rand_serial -config "'.$std_openssl_cnf.'"';
      skip "failed to sign certificate request", 2
         if !is(yes(cmdstr(perlapp(["CA.pl", "-sign"]))), 0,
                'signing certificate request');