From ffb46830e2dfd3203044e6190f50a20fec50162d Mon Sep 17 00:00:00 2001 From: Rich Salz Date: Wed, 16 Aug 2017 15:49:25 -0400 Subject: [PATCH] Add random serial# support. 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 (Merged from https://github.com/openssl/openssl/pull/4185) --- apps/apps.c | 8 ++--- apps/apps.h | 7 ++++- apps/ca.c | 62 ++++++++++++++++++++++++++------------- doc/man1/ca.pod | 9 ++++++ test/recipes/80-test_ca.t | 2 +- 5 files changed, 59 insertions(+), 29 deletions(-) diff --git a/apps/apps.c b/apps/apps.c index 6ff41972e3..79ef933935 100644 --- a/apps/apps.c +++ b/apps/apps.c @@ -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; diff --git a/apps/apps.h b/apps/apps.h index 3b6597869f..fdf316a08e 100644 --- a/apps/apps.h +++ b/apps/apps.h @@ -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); diff --git a/apps/ca.c b/apps/ca.c index c1c2c49047..436aa855cf 100644 --- 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); diff --git a/doc/man1/ca.pod b/doc/man1/ca.pod index a985631531..21e692e511 100644 --- a/doc/man1/ca.pod +++ b/doc/man1/ca.pod @@ -51,6 +51,7 @@ B B [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: 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 diff --git a/test/recipes/80-test_ca.t b/test/recipes/80-test_ca.t index 4c470fbfe2..557777e191 100644 --- a/test/recipes/80-test_ca.t +++ b/test/recipes/80-test_ca.t @@ -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');