diff options
-rw-r--r-- | CHANGES.md | 24 | ||||
-rw-r--r-- | apps/x509.c | 62 | ||||
-rw-r--r-- | doc/man1/openssl-x509.pod.in | 41 |
3 files changed, 91 insertions, 36 deletions
diff --git a/CHANGES.md b/CHANGES.md index cd093491be..8ae1c7470a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -929,19 +929,25 @@ 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. + * Added the `-copy_extensions` option to the `x509` command for use with + `-req` and `-x509toreq`. When given with the `copy` or `copyall` argument, + all extensions in the request are copied to the certificate or vice versa. + + *David von Oheimb*, *Kirill Stefanenkov <kirill_stefanenkov@rambler.ru>* + + * Added the `-copy_extensions` option to the `req` command for use with + `-x509`. When given with the `copy` or `copyall` argument, + all extensions 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 - and an authorityKeyIdentifier extension is included for not self-signed certs - containing a keyIdentifier field with the hash value identifying the signing key. + * The `x509`, `req`, and `ca` commands now make sure that X.509v3 certificates + they generate are by default RFC 5280 compliant in the following sense: + There is a subjectKeyIdentifier extension with a hash value of the public key + and for not self-signed certs there is an authorityKeyIdentifier extension + with a keyIdentifier field or issuer information identifying the signing key. This is done unless some configuration overrides the new default behavior, - e.g. `authorityKeyIdentifier = none`. + such as `subjectKeyIdentifier = none` and `authorityKeyIdentifier = none`. *David von Oheimb* diff --git a/apps/x509.c b/apps/x509.c index 8012475341..6b2e5cfe28 100644 --- a/apps/x509.c +++ b/apps/x509.c @@ -30,6 +30,7 @@ #define POSTFIX ".srl" #define DEFAULT_DAYS 30 /* default cert validity period in days */ #define UNSET_DAYS -2 /* -1 is used for testing expiration checks */ +#define EXT_COPY_UNSET -1 static int callb(int ok, X509_STORE_CTX *ctx); static int sign(X509 *x, EVP_PKEY *pkey, X509 *issuer, @@ -45,7 +46,7 @@ static int x509_certify(X509_STORE *ctx, const char *CAfile, int days, int clrext, CONF *conf, const char *section, ASN1_INTEGER *sno, int preserve_dates); static int purpose_print(BIO *bio, X509 *cert, X509_PURPOSE *pt); -static int print_x509v3_exts(BIO *bio, X509 *x, const char *exts); +static int print_x509v3_exts(BIO *bio, X509 *x, const char *ext_names); typedef enum OPTION_choice { OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, @@ -60,8 +61,7 @@ typedef enum OPTION_choice { OPT_PURPOSE, OPT_STARTDATE, OPT_ENDDATE, OPT_CHECKEND, OPT_CHECKHOST, OPT_CHECKEMAIL, OPT_CHECKIP, OPT_NOOUT, OPT_TRUSTOUT, OPT_CLRTRUST, OPT_CLRREJECT, OPT_ALIAS, OPT_CACREATESERIAL, OPT_CLREXT, OPT_OCSPID, - OPT_SUBJECT_HASH_OLD, - OPT_ISSUER_HASH_OLD, + OPT_SUBJECT_HASH_OLD, OPT_ISSUER_HASH_OLD, OPT_COPY_EXTENSIONS, OPT_BADSIG, OPT_MD, OPT_ENGINE, OPT_NOCERT, OPT_PRESERVE_DATES, OPT_R_ENUM, OPT_PROV_ENUM, OPT_EXT } OPTION_CHOICE; @@ -77,6 +77,8 @@ const OPTIONS x509_options[] = { {"x509toreq", OPT_X509TOREQ, '-', "Output a certification request (rather than a certificate)"}, {"req", OPT_REQ, '-', "Input is a CSR file (rather than a certificate)"}, + {"copy_extensions", OPT_COPY_EXTENSIONS, 's', + "copy extensions when converting from CSR to x509 or vice versa"}, {"inform", OPT_INFORM, 'f', "CSR input file format (DER or PEM) - default PEM"}, {"vfyopt", OPT_VFYOPT, 's', "CSR verification parameter in n:v form"}, @@ -144,7 +146,7 @@ const OPTIONS x509_options[] = { {"subj", OPT_SUBJ, 's', "Set or override certificate subject (and issuer)"}, {"force_pubkey", OPT_FORCE_PUBKEY, '<', "Place the given key in new certificate"}, - {"clrext", OPT_CLREXT, '-', "Clear all certificate extensions"}, + {"clrext", OPT_CLREXT, '-', "Clear all extensions when producing a certificate "}, {"extfile", OPT_EXTFILE, '<', "Config file with X509V3 extensions to add"}, {"extensions", OPT_EXTENSIONS, 's', "Section of extfile to use - default: unnamed section"}, @@ -189,6 +191,7 @@ int x509_main(int argc, char **argv) ASN1_OBJECT *objtmp = NULL; BIO *out = NULL; CONF *extconf = NULL; + int ext_copy = EXT_COPY_UNSET; EVP_PKEY *signkey = NULL, *CAkey = NULL, *pubkey = NULL; int newcert = 0; char *subj = NULL; @@ -202,7 +205,8 @@ int x509_main(int argc, char **argv) X509_STORE *ctx = NULL; const EVP_MD *digest = NULL; char *CAkeyfile = NULL, *CAserial = NULL, *pubkeyfile = NULL, *alias = NULL; - char *checkhost = NULL, *checkemail = NULL, *checkip = NULL, *exts = NULL; + char *checkhost = NULL, *checkemail = NULL, *checkip = NULL; + char *ext_names = NULL; char *extsect = NULL, *extfile = NULL, *passin = NULL, *passinarg = NULL; char *infile = NULL, *outfile = NULL, *signkeyfile = NULL, *CAfile = NULL; char *prog; @@ -272,6 +276,13 @@ int x509_main(int argc, char **argv) case OPT_REQ: reqfile = 1; 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_SIGOPT: if (!sigopts) @@ -434,7 +445,7 @@ int x509_main(int argc, char **argv) break; case OPT_EXT: ext = ++num; - exts = opt_arg(); + ext_names = opt_arg(); break; case OPT_NOCERT: nocert = 1; @@ -632,6 +643,8 @@ int x509_main(int argc, char **argv) print_name(bio_err, "subject=", X509_REQ_get_subject_name(req), get_nameopt()); + } else if (!x509toreq && ext_copy != EXT_COPY_UNSET) { + BIO_printf(bio_err, "Ignoring -copy_extensions since neither -x509toreq nor -req is given\n"); } if (reqfile || newcert) { @@ -657,7 +670,18 @@ int x509_main(int argc, char **argv) } else if (!X509_set_serialNumber(x, sno)) { goto end; } - /* TODO: (optionally) copy X.509 extensions from req */ + if (req != NULL) { + if (ext_copy == EXT_COPY_UNSET) { + BIO_printf(bio_err, + "Warning: ignoring any extensions in the request since -copy_extensions is not given\n"); + } else if (clrext && ext_copy != EXT_COPY_NONE) { + BIO_printf(bio_err, "Must not use -clrext together with -copy_extensions\n"); + goto end; + } else if (!copy_extensions(x, req, ext_copy)) { + BIO_printf(bio_err, "Error copying extensions from request\n"); + goto end; + } + } } else { x = load_cert_pass(infile, 1, passin, "certificate"); if (x == NULL) @@ -712,14 +736,32 @@ int x509_main(int argc, char **argv) } if (x509toreq) { /* also works but makes little sense together with -req */ + const STACK_OF(X509_EXTENSION) *exts; + if (signkey == NULL) { BIO_printf(bio_err, "Must specify request key using -signkey\n"); goto end; } + if (clrext) + BIO_printf(bio_err, + "Warning: the -clrext option is ignored when producing a request\n"); - if ((rq = X509_to_X509_REQ(x, signkey, digest)) == NULL) + if ((rq = X509_to_X509_REQ(x, NULL, NULL)) == NULL) + goto end; + exts = X509_get0_extensions(x); + if (sk_X509_EXTENSION_num(exts /* may be NULL */) > 0) { + if (ext_copy == EXT_COPY_UNSET) { + BIO_printf(bio_err, + "Warning: ignoring extensions in the certificate since -copy_extensions is not given\n"); + } else if (ext_copy != EXT_COPY_NONE + && !X509_REQ_add_extensions(rq, exts)) { + BIO_printf(bio_err, + "Error copying extensions from certificate\n"); + goto end; + } + } + if (!X509_REQ_sign(rq, signkey, digest)) goto end; - /* TODO: (optionally) copy X.509 extensions from x */ if (!noout) { if (outformat == FORMAT_ASN1) { X509_REQ_print_ex(out, rq, get_nameopt(), X509_FLAG_COMPAT); @@ -907,7 +949,7 @@ int x509_main(int argc, char **argv) } else if (ocspid == i) { X509_ocspid_print(out, x); } else if (ext == i) { - print_x509v3_exts(out, x, exts); + print_x509v3_exts(out, x, ext_names); } } } diff --git a/doc/man1/openssl-x509.pod.in b/doc/man1/openssl-x509.pod.in index 1540162ba6..52badc28ab 100644 --- a/doc/man1/openssl-x509.pod.in +++ b/doc/man1/openssl-x509.pod.in @@ -14,6 +14,7 @@ B<openssl> B<x509> [B<-new>] [B<-x509toreq>] [B<-req>] +[B<-copy_extensions> I<arg>] [B<-inform> B<DER>|B<PEM>] [B<-vfyopt> I<nm>:I<v>] [B<-signkey> I<filename>|I<uri>] @@ -122,22 +123,30 @@ which implies self-signature. =item B<-x509toreq> -Output a certificate request (rather than a certificate). +Output a PKCS#10 certificate request (rather than a certificate). The B<-signkey> option must be used to provide the private key for self-signing; the corresponding public key is placed in the subjectPKInfo field. -Any X.509 extensions included in an input file are ignored. +X.509 extensions included in a certificate input are not copied by default. X.509 extensions to be added can be specified using the B<-extfile> option. =item B<-req> By default a certificate is expected on input. -With this option a certificate request is expected instead, -which is transformed into a certificate. +With this option a PKCS#10 certificate request is expected instead, +which must be correctly self-signed. -Any X.509 extensions included in the request file are ignored. +X.509 extensions included in the request are not copied by default. X.509 extensions to be added can be specified using the B<-extfile> option. +=item B<-copy_extensions> I<arg> + +Determines how to handle X.509 extensions +when converting from a certificate to a request using the B<-x509toreq> option +or converting from a request to a certificate using the B<-req> option. +If I<arg> is B<none> or this option is not present then extensions are ignored. +If I<arg> is B<copy> or B<copyall> then all extensions are copied. + =item B<-inform> B<DER>|B<PEM> The CSR input file format; the default is B<PEM>. @@ -160,9 +169,6 @@ by B<-force_pubkey>). Unless the B<-preserve_dates> option is supplied, it sets the validity start date to the current time and the end date to a value determined by the B<-days> option. -Unless the B<-clrext> option is supplied, it retains all certificate extensions -except for any subject identifier and authority key identifier. -For those, new values are generated unless prohibited by configuration. =item B<-keyform> B<DER>|B<PEM>|B<P12>|B<ENGINE> @@ -389,10 +395,14 @@ generate a certificate containing any desired public key. =item B<-clrext> -Delete any extensions from a certificate. This option is used when a -certificate is being created from another certificate (for example with -either the B<-signkey> or the B<-CA> option). -Normally all extensions are retained. +When a transforming a certificate to a new certificate +(for example with the B<-signkey> or B<-CA> option) +by default all certificate extensions are retained +except for any subject identifier and authority key identifier. +For those, new values are generated unless prohibited by configuration. + +When producing a certificate with the B<-clrext> option, +any extensions are deleted. =item B<-extfile> I<filename> @@ -830,12 +840,9 @@ must be present. =head1 BUGS -Extensions in certificates are not transferred to certificate requests and -vice versa. - It is possible to produce invalid certificates or requests by specifying the -wrong private key or using inconsistent options in some cases: these should -be checked. +wrong private key, using unsuitable X.509 extensions, +or using inconsistent options in some cases: these should be checked. There should be options to explicitly set such things as start and end dates rather than an offset from the current time. |