From b65c5ec8f5f8c9fa082c44bf805beed03d0fee0c Mon Sep 17 00:00:00 2001 From: "Dr. David von Oheimb" Date: Thu, 24 Dec 2020 12:43:39 +0100 Subject: [PATCH] apps/req.c: Add -copy_extensions option for use with -x509; default: none Fixes #13708 Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/13658) --- CHANGES.md | 6 ++++++ apps/req.c | 27 ++++++++++++++++++++++++--- doc/man1/openssl-req.pod.in | 13 +++++++++++++ test/certs/ext-check.csr | 18 ++++++++++++++++++ test/recipes/25-test_req.t | 15 ++++++++++++++- 5 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 test/certs/ext-check.csr diff --git a/CHANGES.md b/CHANGES.md index 56017e503b..ac0b22c6fb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -914,6 +914,12 @@ OpenSSL 3.0 *Richard Levitte* + * Added the `<-copy_extensions` option to the `req` command for use with `-x509`. + When given with the `copy` or `copyall` argument, + any extensions present in the certification request are copied to the certificate. + + *David von Oheimb* + * The `x509`, `req`, and `ca` commands now make sure that certificates they generate are RFC 5280 compliant by default: For X.509 version 3 certs they ensure that a subjectKeyIdentifier extension is included containing a hash value of the public key diff --git a/apps/req.c b/apps/req.c index 13d54770db..5663eebc45 100644 --- a/apps/req.c +++ b/apps/req.c @@ -44,6 +44,7 @@ #define MIN_KEY_LENGTH 512 #define DEFAULT_DAYS 30 /* default cert validity period in days */ #define UNSET_DAYS -2 /* -1 may be used for testing expiration checks */ +#define EXT_COPY_UNSET -1 static int make_REQ(X509_REQ *req, EVP_PKEY *pkey, X509_NAME *fsubj, int mutlirdn, int attribs, unsigned long chtype); @@ -86,7 +87,8 @@ typedef enum OPTION_choice { OPT_VERIFY, OPT_NOENC, OPT_NODES, OPT_NOOUT, OPT_VERBOSE, OPT_UTF8, OPT_NAMEOPT, OPT_REQOPT, OPT_SUBJ, OPT_SUBJECT, OPT_TEXT, OPT_X509, OPT_CA, OPT_CAKEY, - OPT_MULTIVALUE_RDN, OPT_DAYS, OPT_SET_SERIAL, OPT_ADDEXT, OPT_EXTENSIONS, + OPT_MULTIVALUE_RDN, OPT_DAYS, OPT_SET_SERIAL, + OPT_COPY_EXTENSIONS, OPT_ADDEXT, OPT_EXTENSIONS, OPT_REQEXTS, OPT_PRECERT, OPT_MD, OPT_SECTION, OPT_R_ENUM, OPT_PROV_ENUM @@ -125,6 +127,8 @@ const OPTIONS req_options[] = { "Deprecated; multi-valued RDNs support is always on."}, {"days", OPT_DAYS, 'p', "Number of days cert is valid for"}, {"set_serial", OPT_SET_SERIAL, 's', "Serial number to use"}, + {"copy_extensions", OPT_COPY_EXTENSIONS, 's', + "copy extensions from request when using -x509"}, {"addext", OPT_ADDEXT, 's', "Additional cert extension key=value pair (may be given more than once)"}, {"extensions", OPT_EXTENSIONS, 's', @@ -237,6 +241,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; + int ext_copy = EXT_COPY_UNSET; BIO *addext_bio = NULL; char *extensions = NULL; const char *infile = NULL, *CAfile = NULL, *CAkeyfile = NULL; @@ -430,6 +435,12 @@ int req_main(int argc, char **argv) case OPT_MULTIVALUE_RDN: /* obsolete */ break; + case OPT_COPY_EXTENSIONS: + if (!set_ext_copy(&ext_copy, opt_arg())) { + BIO_printf(bio_err, "Invalid extension copy option: \"%s\"\n", opt_arg()); + goto end; + } + break; case OPT_ADDEXT: p = opt_arg(); if (addexts == NULL) { @@ -468,8 +479,12 @@ int req_main(int argc, char **argv) if (argc != 0) goto opthelp; - if (days != UNSET_DAYS && !gen_x509) - BIO_printf(bio_err, "Ignoring -days; not generating a certificate\n"); + if (!gen_x509) { + if (days != UNSET_DAYS) + BIO_printf(bio_err, "Ignoring -days without -x509; not generating a certificate\n"); + if (ext_copy == EXT_COPY_NONE) + BIO_printf(bio_err, "Ignoring -copy_extensions 'none' when -x509 is not given\n"); + } if (gen_x509 && infile == NULL) newreq = 1; @@ -822,6 +837,12 @@ int req_main(int argc, char **argv) goto end; if (!pub_key || !X509_set_pubkey(new_x509, pub_key)) goto end; + if (ext_copy == EXT_COPY_UNSET) + BIO_printf(bio_err, "Warning: No -copy_extensions given; ignoring any extensions in the request\n"); + else if (!copy_extensions(new_x509, req, ext_copy)) { + BIO_printf(bio_err, "Error copying extensions from request\n"); + goto end; + } /* TODO: (optionally) copy X.509 extensions from req */ /* Set up V3 context struct */ diff --git a/doc/man1/openssl-req.pod.in b/doc/man1/openssl-req.pod.in index f73b7fbb9d..141774b7db 100644 --- a/doc/man1/openssl-req.pod.in +++ b/doc/man1/openssl-req.pod.in @@ -38,6 +38,7 @@ B B [B<-days> I] [B<-set_serial> I] [B<-newhdr>] +[B<-copy_extensions> I] [B<-addext> I] [B<-extensions> I
] [B<-reqexts> I
] @@ -267,6 +268,7 @@ to the a certificate; otherwise a request is created from scratch. Unless specified using the B<-set_serial> option, a large random number will be used for the serial number. +Unless the B<-copy_extensions> option is used, X.509 extensions are not copied from any provided request input file. X.509 extensions to be added can be specified in the configuration file or using the B<-addext> option. @@ -295,6 +297,17 @@ 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 C<0x>. +=item B<-copy_extensions> I + +Determines how extensions in certificate requests should be handled when B<-x509> is given. +If I is B or this option is not present +then extensions present in the request are ignored. +If I is B or B then +any extensions present in the request are copied to the certificate. + +The main use of this option is to allow a certificate request to supply +values for certain extensions such as subjectAltName. + =item B<-addext> I Add a specific extension to the certificate (if the B<-x509> option is diff --git a/test/certs/ext-check.csr b/test/certs/ext-check.csr new file mode 100644 index 0000000000..ee974e05ce --- /dev/null +++ b/test/certs/ext-check.csr @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICzTCCAbcCAQAwVDELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx +ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDENMAsGA1UEAwwEdGVz +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJadpD0ASxxfxsvdj9Ix +sogVzMSGLFziaYuE9KejU9+R479RifvwfBANO62sNWJ19X//9G5UjwWmkiOzn1k5 +0DkYsBBA3mJzik6wjt/c58lBIlSEgAgpvDU8ht8w3t20JP9+YqXAeugqFj/Wl9rF +QtsvaWSRywjXVlp5fxuEQelNnXcJEKhsKTNExsBUZebo4/J1BWpklWzA9P0lYW5I +NvDAAwcF1nzlEf0Y6Eot03IMNyg2MTE4hehxjdgCSci8GYnFirE/ojXqqpAcZGh7 +r2dqWgZUD1Dh+bT2vjrUzj8eTH3GdzI+oljt29102JIUaqj3yzRYkah8FLF9CLNN +sUcCAwEAAaA2MBYGCSqGSIb3DQEJAjEJDAdDb21wYW55MBwGCSqGSIb3DQEJDjEP +MA0wCwYDVR0PBAQDAgeAMAsGCSqGSIb3DQEBCwOCAQEAYd4B+FkWRuVVDPYfrN8P +UdZbLTggUGrpdhRibnoAsLNQ3cCS90OsCq5FLD6TVUCNb1gnp15Jp1WChQSyD3zC +jb8VgivDeDOuk08Zy2Fl2+QvuwyQ9hKTAOTdAmP/bapAi7zniElSTP6BZ8vyEtuP +FCEWJ5UjhvUYbZOG5WIHxhT+24CtYH3iHNir4OlDbsYrUBKEmQZIDj6WC01UT+4U +/up2xKq1Y+rOUv2Xy3K9O/U1W/3AF7IvcDyd7+qQTGD8U2X3efzZYOffhTN+9Rvn +5t82CnHLjFn4Co43RBiOcbjSDbvtaghtDiYB2tSUuqafHiuAJKx6zAm0Y2FR8X+z +gg== +-----END CERTIFICATE REQUEST----- diff --git a/test/recipes/25-test_req.t b/test/recipes/25-test_req.t index 7f699c065d..861212f110 100644 --- a/test/recipes/25-test_req.t +++ b/test/recipes/25-test_req.t @@ -15,7 +15,7 @@ use OpenSSL::Test qw/:DEFAULT srctop_file/; setup("test_req"); -plan tests => 38; +plan tests => 42; require_ok(srctop_file('test', 'recipes', 'tconversion.pl')); @@ -274,6 +274,11 @@ sub has_AKID { my $expect = shift @_; cert_contains($cert, "Authority Key Identifier", $expect); } +sub has_keyUsage { + my $cert = shift @_; + my $expect = shift @_; + cert_contains($cert, "Key Usage", $expect); +} sub strict_verify { my $cert = shift @_; my $expect = shift @_; @@ -329,3 +334,11 @@ generate_cert($cert, "-addext", "keyUsage = dataEncipherment", "-in", srctop_file(@certs, "x509-check.csr")); cert_ext_has_n_different_lines($cert, 4, $SKID_AKID); # SKID != AKID strict_verify($cert, 1); + +my $cert = "self-signed_CA_no_keyUsage.pem"; +generate_cert($cert, "-in", srctop_file(@certs, "ext-check.csr")); +has_keyUsage($cert, 0); +my $cert = "self-signed_CA_with_keyUsages.pem"; +generate_cert($cert, "-in", srctop_file(@certs, "ext-check.csr"), + "-copy_extensions", "copy"); +has_keyUsage($cert, 1);