apps/x509.c: Add -copy_extensions option, used when transforming x509 <-> req

Fixes #3638
Fixes #6481
Fixes #10458
Partly fixes #13708
Supersedes #9449

Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/13711)
This commit is contained in:
Dr. David von Oheimb 2021-01-06 14:44:03 +01:00 committed by Dr. David von Oheimb
parent 1d1d23128f
commit b9fbacaa7b
3 changed files with 91 additions and 36 deletions

View File

@ -929,19 +929,25 @@ OpenSSL 3.0
*Richard Levitte* *Richard Levitte*
* Added the `<-copy_extensions` option to the `req` command for use with `-x509`. * Added the `-copy_extensions` option to the `x509` command for use with
When given with the `copy` or `copyall` argument, `-req` and `-x509toreq`. When given with the `copy` or `copyall` argument,
any extensions present in the certification request are copied to the certificate. 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* *David von Oheimb*
* The `x509`, `req`, and `ca` commands now make sure that certificates they * The `x509`, `req`, and `ca` commands now make sure that X.509v3 certificates
generate are RFC 5280 compliant by default: For X.509 version 3 certs they ensure that they generate are by default RFC 5280 compliant in the following sense:
a subjectKeyIdentifier extension is included containing a hash value of the public key There is a subjectKeyIdentifier extension with a hash value of the public key
and an authorityKeyIdentifier extension is included for not self-signed certs and for not self-signed certs there is an authorityKeyIdentifier extension
containing a keyIdentifier field with the hash value identifying the signing key. with a keyIdentifier field or issuer information identifying the signing key.
This is done unless some configuration overrides the new default behavior, 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* *David von Oheimb*

View File

@ -30,6 +30,7 @@
#define POSTFIX ".srl" #define POSTFIX ".srl"
#define DEFAULT_DAYS 30 /* default cert validity period in days */ #define DEFAULT_DAYS 30 /* default cert validity period in days */
#define UNSET_DAYS -2 /* -1 is used for testing expiration checks */ #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 callb(int ok, X509_STORE_CTX *ctx);
static int sign(X509 *x, EVP_PKEY *pkey, X509 *issuer, 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, int days, int clrext, CONF *conf, const char *section,
ASN1_INTEGER *sno, int preserve_dates); ASN1_INTEGER *sno, int preserve_dates);
static int purpose_print(BIO *bio, X509 *cert, X509_PURPOSE *pt); 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 { typedef enum OPTION_choice {
OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, 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_PURPOSE, OPT_STARTDATE, OPT_ENDDATE, OPT_CHECKEND, OPT_CHECKHOST,
OPT_CHECKEMAIL, OPT_CHECKIP, OPT_NOOUT, OPT_TRUSTOUT, OPT_CLRTRUST, OPT_CHECKEMAIL, OPT_CHECKIP, OPT_NOOUT, OPT_TRUSTOUT, OPT_CLRTRUST,
OPT_CLRREJECT, OPT_ALIAS, OPT_CACREATESERIAL, OPT_CLREXT, OPT_OCSPID, OPT_CLRREJECT, OPT_ALIAS, OPT_CACREATESERIAL, OPT_CLREXT, OPT_OCSPID,
OPT_SUBJECT_HASH_OLD, OPT_SUBJECT_HASH_OLD, OPT_ISSUER_HASH_OLD, OPT_COPY_EXTENSIONS,
OPT_ISSUER_HASH_OLD,
OPT_BADSIG, OPT_MD, OPT_ENGINE, OPT_NOCERT, OPT_PRESERVE_DATES, OPT_BADSIG, OPT_MD, OPT_ENGINE, OPT_NOCERT, OPT_PRESERVE_DATES,
OPT_R_ENUM, OPT_PROV_ENUM, OPT_EXT OPT_R_ENUM, OPT_PROV_ENUM, OPT_EXT
} OPTION_CHOICE; } OPTION_CHOICE;
@ -77,6 +77,8 @@ const OPTIONS x509_options[] = {
{"x509toreq", OPT_X509TOREQ, '-', {"x509toreq", OPT_X509TOREQ, '-',
"Output a certification request (rather than a certificate)"}, "Output a certification request (rather than a certificate)"},
{"req", OPT_REQ, '-', "Input is a CSR file (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', {"inform", OPT_INFORM, 'f',
"CSR input file format (DER or PEM) - default PEM"}, "CSR input file format (DER or PEM) - default PEM"},
{"vfyopt", OPT_VFYOPT, 's', "CSR verification parameter in n:v form"}, {"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)"}, {"subj", OPT_SUBJ, 's', "Set or override certificate subject (and issuer)"},
{"force_pubkey", OPT_FORCE_PUBKEY, '<', {"force_pubkey", OPT_FORCE_PUBKEY, '<',
"Place the given key in new certificate"}, "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"}, {"extfile", OPT_EXTFILE, '<', "Config file with X509V3 extensions to add"},
{"extensions", OPT_EXTENSIONS, 's', {"extensions", OPT_EXTENSIONS, 's',
"Section of extfile to use - default: unnamed section"}, "Section of extfile to use - default: unnamed section"},
@ -189,6 +191,7 @@ int x509_main(int argc, char **argv)
ASN1_OBJECT *objtmp = NULL; ASN1_OBJECT *objtmp = NULL;
BIO *out = NULL; BIO *out = NULL;
CONF *extconf = NULL; CONF *extconf = NULL;
int ext_copy = EXT_COPY_UNSET;
EVP_PKEY *signkey = NULL, *CAkey = NULL, *pubkey = NULL; EVP_PKEY *signkey = NULL, *CAkey = NULL, *pubkey = NULL;
int newcert = 0; int newcert = 0;
char *subj = NULL; char *subj = NULL;
@ -202,7 +205,8 @@ int x509_main(int argc, char **argv)
X509_STORE *ctx = NULL; X509_STORE *ctx = NULL;
const EVP_MD *digest = NULL; const EVP_MD *digest = NULL;
char *CAkeyfile = NULL, *CAserial = NULL, *pubkeyfile = NULL, *alias = 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 *extsect = NULL, *extfile = NULL, *passin = NULL, *passinarg = NULL;
char *infile = NULL, *outfile = NULL, *signkeyfile = NULL, *CAfile = NULL; char *infile = NULL, *outfile = NULL, *signkeyfile = NULL, *CAfile = NULL;
char *prog; char *prog;
@ -272,6 +276,13 @@ int x509_main(int argc, char **argv)
case OPT_REQ: case OPT_REQ:
reqfile = 1; reqfile = 1;
break; 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: case OPT_SIGOPT:
if (!sigopts) if (!sigopts)
@ -434,7 +445,7 @@ int x509_main(int argc, char **argv)
break; break;
case OPT_EXT: case OPT_EXT:
ext = ++num; ext = ++num;
exts = opt_arg(); ext_names = opt_arg();
break; break;
case OPT_NOCERT: case OPT_NOCERT:
nocert = 1; nocert = 1;
@ -632,6 +643,8 @@ int x509_main(int argc, char **argv)
print_name(bio_err, "subject=", X509_REQ_get_subject_name(req), print_name(bio_err, "subject=", X509_REQ_get_subject_name(req),
get_nameopt()); 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) { if (reqfile || newcert) {
@ -657,7 +670,18 @@ int x509_main(int argc, char **argv)
} else if (!X509_set_serialNumber(x, sno)) { } else if (!X509_set_serialNumber(x, sno)) {
goto end; 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 { } else {
x = load_cert_pass(infile, 1, passin, "certificate"); x = load_cert_pass(infile, 1, passin, "certificate");
if (x == NULL) 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 */ if (x509toreq) { /* also works but makes little sense together with -req */
const STACK_OF(X509_EXTENSION) *exts;
if (signkey == NULL) { if (signkey == NULL) {
BIO_printf(bio_err, "Must specify request key using -signkey\n"); BIO_printf(bio_err, "Must specify request key using -signkey\n");
goto end; 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; goto end;
/* TODO: (optionally) copy X.509 extensions from x */
if (!noout) { if (!noout) {
if (outformat == FORMAT_ASN1) { if (outformat == FORMAT_ASN1) {
X509_REQ_print_ex(out, rq, get_nameopt(), X509_FLAG_COMPAT); 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) { } else if (ocspid == i) {
X509_ocspid_print(out, x); X509_ocspid_print(out, x);
} else if (ext == i) { } else if (ext == i) {
print_x509v3_exts(out, x, exts); print_x509v3_exts(out, x, ext_names);
} }
} }
} }

View File

@ -14,6 +14,7 @@ B<openssl> B<x509>
[B<-new>] [B<-new>]
[B<-x509toreq>] [B<-x509toreq>]
[B<-req>] [B<-req>]
[B<-copy_extensions> I<arg>]
[B<-inform> B<DER>|B<PEM>] [B<-inform> B<DER>|B<PEM>]
[B<-vfyopt> I<nm>:I<v>] [B<-vfyopt> I<nm>:I<v>]
[B<-signkey> I<filename>|I<uri>] [B<-signkey> I<filename>|I<uri>]
@ -122,22 +123,30 @@ which implies self-signature.
=item B<-x509toreq> =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 B<-signkey> option must be used to provide the private key for self-signing;
the corresponding public key is placed in the subjectPKInfo field. 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. X.509 extensions to be added can be specified using the B<-extfile> option.
=item B<-req> =item B<-req>
By default a certificate is expected on input. By default a certificate is expected on input.
With this option a certificate request is expected instead, With this option a PKCS#10 certificate request is expected instead,
which is transformed into a certificate. 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. 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> =item B<-inform> B<DER>|B<PEM>
The CSR input file format; the default is 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, Unless the B<-preserve_dates> option is supplied,
it sets the validity start date to the current time it sets the validity start date to the current time
and the end date to a value determined by the B<-days> option. 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> =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> =item B<-clrext>
Delete any extensions from a certificate. This option is used when a When a transforming a certificate to a new certificate
certificate is being created from another certificate (for example with (for example with the B<-signkey> or B<-CA> option)
either the B<-signkey> or the B<-CA> option). by default all certificate extensions are retained
Normally all 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> =item B<-extfile> I<filename>
@ -830,12 +840,9 @@ must be present.
=head1 BUGS =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 It is possible to produce invalid certificates or requests by specifying the
wrong private key or using inconsistent options in some cases: these should wrong private key, using unsuitable X.509 extensions,
be checked. or using inconsistent options in some cases: these should be checked.
There should be options to explicitly set such things as start and end There should be options to explicitly set such things as start and end
dates rather than an offset from the current time. dates rather than an offset from the current time.