APPS: generated certs bear X.509 V3, unless -x509v1 option of req app is given

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Hugo Landau <hlandau@openssl.org>
Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com>
(Merged from https://github.com/openssl/openssl/pull/19271)
This commit is contained in:
Dr. David von Oheimb 2022-09-24 23:59:12 +02:00 committed by Dr. David von Oheimb
parent 66fc90f18c
commit 342e3652c7
20 changed files with 155 additions and 45 deletions

View File

@ -127,6 +127,13 @@ OpenSSL 3.2
* Lutz Jänicke*
* The `x509`, `ca`, and `req` apps now produce X.509 v3 certificates.
The `-x509v1` option of `req` prefers generation of X.509 v1 certificates.
`X509_sign()` and `X509_sign_ctx()` make sure that the certificate has
X.509 version 3 if the certificate information includes X.509 extensions.
*David von Oheimb*
* Fix and extend certificate handling and the apps `x509`, `verify` etc.
such as adding a trace facility for debugging certificate chain building.

View File

@ -1926,7 +1926,7 @@ static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509,
!EVP_PKEY_missing_parameters(pkey))
EVP_PKEY_copy_parameters(pktmp, pkey);
if (!do_X509_sign(ret, pkey, dgst, sigopts, &ext_ctx))
if (!do_X509_sign(ret, 0, pkey, dgst, sigopts, &ext_ctx))
goto end;
/* We now just add it to the database as DB_TYPE_VAL('V') */

View File

@ -259,7 +259,7 @@ int init_gen_str(EVP_PKEY_CTX **pctx,
const char *algname, ENGINE *e, int do_param,
OSSL_LIB_CTX *libctx, const char *propq);
int cert_matches_key(const X509 *cert, const EVP_PKEY *pkey);
int do_X509_sign(X509 *x, EVP_PKEY *pkey, const char *md,
int do_X509_sign(X509 *x, int force_v1, EVP_PKEY *pkey, const char *md,
STACK_OF(OPENSSL_STRING) *sigopts, X509V3_CTX *ext_ctx);
int do_X509_verify(X509 *x, EVP_PKEY *pkey, STACK_OF(OPENSSL_STRING) *vfyopts);
int do_X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const char *md,

View File

@ -2289,16 +2289,14 @@ int cert_matches_key(const X509 *cert, const EVP_PKEY *pkey)
}
/* Ensure RFC 5280 compliance, adapt keyIDs as needed, and sign the cert info */
int do_X509_sign(X509 *cert, EVP_PKEY *pkey, const char *md,
int do_X509_sign(X509 *cert, int force_v1, EVP_PKEY *pkey, const char *md,
STACK_OF(OPENSSL_STRING) *sigopts, X509V3_CTX *ext_ctx)
{
const STACK_OF(X509_EXTENSION) *exts = X509_get0_extensions(cert);
EVP_MD_CTX *mctx = EVP_MD_CTX_new();
int self_sign;
int rv = 0;
if (sk_X509_EXTENSION_num(exts /* may be NULL */) > 0) {
/* Prevent X509_V_ERR_EXTENSIONS_REQUIRE_VERSION_3 */
if (!force_v1) {
if (!X509_set_version(cert, X509_VERSION_3))
goto end;

View File

@ -85,8 +85,8 @@ typedef enum OPTION_choice {
OPT_KEYOUT, OPT_PASSIN, OPT_PASSOUT, OPT_NEWKEY,
OPT_PKEYOPT, OPT_SIGOPT, OPT_VFYOPT, OPT_BATCH, OPT_NEWHDR, OPT_MODULUS,
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_NAMEOPT, OPT_REQOPT, OPT_SUBJ, OPT_SUBJECT, OPT_TEXT,
OPT_X509, OPT_X509V1, OPT_CA, OPT_CAKEY,
OPT_MULTIVALUE_RDN, OPT_DAYS, OPT_SET_SERIAL,
OPT_COPY_EXTENSIONS, OPT_EXTENSIONS, OPT_REQEXTS, OPT_ADDEXT,
OPT_PRECERT, OPT_MD,
@ -117,6 +117,7 @@ const OPTIONS req_options[] = {
{"text", OPT_TEXT, '-', "Text form of request"},
{"x509", OPT_X509, '-',
"Output an X.509 certificate structure instead of a cert request"},
{"x509v1", OPT_X509V1, '-', "Request cert generation with X.509 version 1"},
{"CA", OPT_CA, '<', "Issuer cert to use for signing a cert, implies -x509"},
{"CAkey", OPT_CAKEY, 's',
"Issuer private key to use with -CA; default is -CA arg"},
@ -261,7 +262,7 @@ int req_main(int argc, char **argv)
int ret = 1, gen_x509 = 0, i = 0, newreq = 0, verbose = 0;
int informat = FORMAT_UNDEF, outformat = FORMAT_PEM, keyform = FORMAT_UNDEF;
int modulus = 0, multirdn = 1, verify = 0, noout = 0, text = 0;
int noenc = 0, newhdr = 0, subject = 0, pubkey = 0, precert = 0;
int noenc = 0, newhdr = 0, subject = 0, pubkey = 0, precert = 0, x509v1 = 0;
long newkey_len = -1;
unsigned long chtype = MBSTRING_ASC, reqflag = 0;
@ -403,6 +404,9 @@ int req_main(int argc, char **argv)
case OPT_TEXT:
text = 1;
break;
case OPT_X509V1:
x509v1 = 1;
/* fall thru */
case OPT_X509:
gen_x509 = 1;
break;
@ -867,7 +871,8 @@ int req_main(int argc, char **argv)
}
}
i = do_X509_sign(new_x509, issuer_key, digest, sigopts, &ext_ctx);
i = do_X509_sign(new_x509, x509v1, issuer_key, digest, sigopts,
&ext_ctx);
if (!i)
goto end;
} else {

View File

@ -894,7 +894,7 @@ int x509_main(int argc, char **argv)
}
noout = 1;
} else if (privkey != NULL) {
if (!do_X509_sign(x, privkey, digest, sigopts, &ext_ctx))
if (!do_X509_sign(x, 0, privkey, digest, sigopts, &ext_ctx))
goto end;
} else if (CAfile != NULL) {
if ((CAkey = load_key(CAkeyfile, CAkeyformat,
@ -906,7 +906,7 @@ int x509_main(int argc, char **argv)
goto err;
}
if (!do_X509_sign(x, CAkey, digest, sigopts, &ext_ctx))
if (!do_X509_sign(x, 0, CAkey, digest, sigopts, &ext_ctx))
goto end;
}
if (badsig) {

View File

@ -63,6 +63,9 @@ int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md)
ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
if (sk_X509_EXTENSION_num(X509_get0_extensions(x)) > 0
&& !X509_set_version(x, X509_VERSION_3))
return 0;
/*
* Setting the modified flag before signing it. This makes the cached
@ -83,6 +86,9 @@ int X509_sign_ctx(X509 *x, EVP_MD_CTX *ctx)
ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
if (sk_X509_EXTENSION_num(X509_get0_extensions(x)) > 0
&& !X509_set_version(x, X509_VERSION_3))
return 0;
x->cert_info.enc.modified = 1;
return ASN1_item_sign_ctx(ASN1_ITEM_rptr(X509_CINF),
&x->cert_info.signature,

View File

@ -71,6 +71,11 @@ B<openssl> B<ca>
This command emulates a CA application.
See the B<WARNINGS> especially when considering to use it productively.
It generates certificates bearing X.509 version 3.
Unless specified otherwise,
key identifier extensions are included as described in L<x509v3_config(5)>.
It can be used to sign certificate requests (CSRs) in a variety of forms
and generate certificate revocation lists (CRLs).
It also maintains a text database of issued certificates and their status.
@ -287,8 +292,7 @@ and all certificates will be certified automatically.
The section of the configuration file containing certificate extensions
to be added when a certificate is issued (defaults to B<x509_extensions>
unless the B<-extfile> option is used).
If no X.509 extensions are specified then a V1 certificate is created,
else a V3 certificate is created.
See the L<x509v3_config(5)> manual page for details of the
extension section format.
@ -833,6 +837,9 @@ has no effect.
The B<-engine> option was deprecated in OpenSSL 3.0.
Since OpenSSL 3.2, generated certificates bear X.509 version 3,
and key identifier extensions are included by default.
=head1 SEE ALSO
L<openssl(1)>,

View File

@ -33,6 +33,7 @@ B<openssl> B<req>
[B<-config> I<filename>]
[B<-section> I<name>]
[B<-x509>]
[B<-x509v1>]
[B<-CA> I<filename>|I<uri>]
[B<-CAkey> I<filename>|I<uri>]
[B<-days> I<n>]
@ -299,6 +300,16 @@ X.509 extensions to be added can be specified in the configuration file,
possibly using the B<-config> and B<-extensions> options,
and/or using the B<-addext> option.
Unless B<-x509v1> is given, generated certificates bear X.509 version 3.
Unless specified otherwise,
key identifier extensions are included as described in L<x509v3_config(5)>.
=item B<-x509v1>
Request generation of certificates with X.509 version 1.
This implies B<-x509>.
If X.509 extensions are given, anyway X.509 version 3 is set.
=item B<-CA> I<filename>|I<uri>
Specifies the "CA" certificate to be used for signing a new certificate
@ -349,7 +360,7 @@ file to specify requests for a variety of purposes.
Add a specific extension to the certificate (if B<-x509> is in use)
or certificate request. The argument must have the form of
a key=value pair as it would appear in a config file.
a C<key=value> pair as it would appear in a config file.
This option can be given multiple times.
@ -770,6 +781,10 @@ The <-nodes> option was deprecated in OpenSSL 3.0, too; use B<-noenc> instead.
The B<-reqexts> option has been made an alias of B<-extensions> in OpenSSL 3.2.
Since OpenSSL 3.2,
generated certificates bear X.509 version 3 unless B<-x509v1> is given,
and key identifier extensions are included by default.
=head1 COPYRIGHT
Copyright 2000-2021 The OpenSSL Project Authors. All Rights Reserved.

View File

@ -87,6 +87,10 @@ convert certificates to various forms, edit certificate trust settings,
generate certificates from scratch or from certificating requests
and then self-signing them or signing them like a "micro CA".
Generated certificates bear X.509 version 3.
Unless specified otherwise,
key identifier extensions are included as described in L<x509v3_config(5)>.
Since there are a large number of options they will split up into
various sections.
@ -303,7 +307,7 @@ as used by OpenSSL before version 1.0.0.
Prints out the certificate extensions in text form.
Can also be used to restrict which extensions to copy.
Extensions are specified
with a comma separated string, e.g., "subjectAltName,subjectKeyIdentifier".
with a comma separated string, e.g., "subjectAltName, subjectKeyIdentifier".
See the L<x509v3_config(5)> manual page for the extension names.
=item B<-ocspid>
@ -435,9 +439,13 @@ If this option is not
specified then the extensions should either be contained in the unnamed
(default) section or the default section should contain a variable called
"extensions" which contains the section to use.
See the L<x509v3_config(5)> manual page for details of the
extension section format.
Unless specified otherwise,
key identifier extensions are included as described in L<x509v3_config(5)>.
=item B<-sigopt> I<nm>:I<v>
Pass options to the signature algorithm during sign operations.
@ -782,6 +790,9 @@ The B<-engine> option was deprecated in OpenSSL 3.0.
The B<-C> option was removed in OpenSSL 3.0.
Since OpenSSL 3.2, generated certificates bear X.509 version 3,
and key identifier extensions are included by default.
=head1 COPYRIGHT
Copyright 2000-2021 The OpenSSL Project Authors. All Rights Reserved.

View File

@ -22,13 +22,13 @@ certificate request or CRL version
=head1 DESCRIPTION
X509_get_version() returns the numerical value of the version field of
certificate B<x>. These correspond to the constants B<X509_VERSION_1>,
certificate I<x>. These correspond to the constants B<X509_VERSION_1>,
B<X509_VERSION_2>, and B<X509_VERSION_3>. Note: the values of these constants
are defined by standards (X.509 et al) to be one less than the certificate
version. So B<X509_VERSION_3> has value 2 and B<X509_VERSION_1> has value 0.
X509_set_version() sets the numerical value of the version field of certificate
B<x> to B<version>.
I<x> to I<version>.
Similarly X509_REQ_get_version(), X509_REQ_set_version(),
X509_CRL_get_version() and X509_CRL_set_version() get and set the version

View File

@ -25,6 +25,8 @@ sign certificate, certificate request, or CRL signature
X509_sign() signs certificate I<x> using private key I<pkey> and message
digest I<md> and sets the signature in I<x>. X509_sign_ctx() also signs
certificate I<x> but uses the parameters contained in digest context I<ctx>.
If the certificate information includes X.509 extensions,
these two functions make sure that the certificate bears X.509 version 3.
X509_REQ_sign(), X509_REQ_sign_ctx(),
X509_CRL_sign(), and X509_CRL_sign_ctx()

View File

@ -173,14 +173,27 @@ Examples:
=head2 Subject Key Identifier
The SKID extension specification has a value with three choices.
If the value is the word B<none> then no SKID extension will be included.
If the value is the word B<hash>, or by default for the B<x509>, B<req>, and
B<ca> apps, the process specified in RFC 5280 section 4.2.1.2. (1) is followed:
=over 4
=item B<none>
No SKID extension will be included.
=item B<hash>
The process specified in RFC 5280 section 4.2.1.2. (1) is followed:
The keyIdentifier is composed of the 160-bit SHA-1 hash of the value of the BIT
STRING subjectPublicKey (excluding the tag, length, and number of unused bits).
Otherwise, the value must be a hex string (possibly with C<:> separating bytes)
to output directly, however, this is strongly discouraged.
=item A hex string (possibly with C<:> separating bytes)
The provided value is output directly.
This choice is strongly discouraged.
=back
By default the B<x509>, B<req>, and B<ca> apps behave as if B<hash> was given.
Example:
@ -195,6 +208,7 @@ or both of them, separated by C<,>.
Either or both can have the option B<always>,
indicated by putting a colon C<:> between the value and this option.
For self-signed certificates the AKID is suppressed unless B<always> is present.
By default the B<x509>, B<req>, and B<ca> apps behave as if B<none> was given
for self-signed certificates and B<keyid>C<,> B<issuer> otherwise.

@ -1 +1 @@
Subproject commit b2b4d629f100eaee9f5942a106b1ccefe85b8808
Subproject commit a6b90523e4ea6010b1109b0bae7e2a73b5b025c5

View File

@ -31,6 +31,8 @@ organizationName = Dodgy Brothers
0.commonName = Brother 1
1.commonName = $ENV::CN2
[ empty ]
[ v3_ee ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always

View File

@ -15,7 +15,7 @@ use OpenSSL::Test qw/:DEFAULT srctop_file/;
setup("test_req");
plan tests => 92;
plan tests => 102;
require_ok(srctop_file('test', 'recipes', 'tconversion.pl'));
@ -393,16 +393,7 @@ sub generate_cert {
push(@cmd, ("-CA", $ca_cert, "-CAkey", $ca_key)) unless $ss;
ok(run(app([@cmd])), "generate $cert");
}
sub has_SKID {
my $cert = shift @_;
my $expect = shift @_;
cert_contains($cert, "Subject Key Identifier", $expect);
}
sub has_AKID {
my $cert = shift @_;
my $expect = shift @_;
cert_contains($cert, "Authority Key Identifier", $expect);
}
sub has_keyUsage {
my $cert = shift @_;
my $expect = shift @_;
@ -424,6 +415,12 @@ my $SKID_AKID = "subjectKeyIdentifier,authorityKeyIdentifier";
# # SKID
my $cert = "self-signed_default_SKID_no_explicit_exts.pem";
generate_cert($cert);
has_version($cert, 3);
has_SKID($cert, 1); # SKID added, though no explicit extensions given
has_AKID($cert, 0);
my $cert = "self-signed_v3_CA_hash_SKID.pem";
generate_cert($cert, @v3_ca, "-addext", "subjectKeyIdentifier = hash");
has_SKID($cert, 1); # explicit hash SKID
@ -441,7 +438,8 @@ strict_verify($cert, 1);
# AKID of self-signed certs
$cert = "self-signed_v1_CA_no_KIDs.pem";
generate_cert($cert);
generate_cert($cert, "-x509v1");
has_version($cert, 1);
cert_ext_has_n_different_lines($cert, 0, $SKID_AKID); # no SKID and no AKID
#TODO strict_verify($cert, 1); # self-signed v1 root cert should be accepted as CA
@ -515,6 +513,8 @@ strict_verify($cert, 1);
$cert = "self-issued_v3_CA_no_AKID.pem";
generate_cert($cert, "-addext", "authorityKeyIdentifier = none",
"-in", srctop_file(@certs, "x509-check.csr"));
has_version($cert, 3);
has_SKID($cert, 1); # SKID added, though no explicit extensions given
has_AKID($cert, 0);
strict_verify($cert, 1);
@ -556,6 +556,11 @@ cert_ext_has_n_different_lines($cert, 6, $SKID_AKID); # SKID != AKID, both force
# AKID of not self-issued certs
$cert = "regular_v3_EE_default_KIDs_no_other_exts.pem";
generate_cert($cert, "-key", srctop_file(@certs, "ee-key.pem"));
has_version($cert, 3);
cert_ext_has_n_different_lines($cert, 4, $SKID_AKID); # SKID != AKID
$cert = "regular_v3_EE_default_KIDs.pem";
generate_cert($cert, "-addext", "keyUsage = dataEncipherment",
"-key", srctop_file(@certs, "ee-key.pem"));

View File

@ -16,7 +16,7 @@ use OpenSSL::Test qw/:DEFAULT srctop_file/;
setup("test_x509");
plan tests => 29;
plan tests => 32;
# Prevent MSys2 filename munging for arguments that look like file paths but
# aren't
@ -202,6 +202,11 @@ ok(run(app(["openssl", "x509", "-req", "-text", "-CAcreateserial",
# Verify issuer is CA
ok(get_issuer($b_cert) =~ /CN=ca.example.com/);
# although no explicit extensions given:
has_version($b_cert, 3);
has_SKID($b_cert, 1);
has_AKID($b_cert, 1);
SKIP: {
skip "EC is not supported by this OpenSSL build", 1
if disabled("ec");

View File

@ -25,18 +25,25 @@ my $std_openssl_cnf = '"'
. srctop_file("apps", $^O eq "VMS" ? "openssl-vms.cnf" : "openssl.cnf")
. '"';
sub src_file {
return srctop_file("test", "certs", shift);
}
rmtree("demoCA", { safe => 0 });
plan tests => 15;
plan tests => 20;
require_ok(srctop_file("test", "recipes", "tconversion.pl"));
SKIP: {
my $cakey = srctop_file("test", "certs", "ca-key.pem");
my $cakey = src_file("ca-key.pem");
$ENV{OPENSSL_CONFIG} = qq(-config "$cnf");
skip "failed creating CA structure", 4
if !ok(run(perlapp(["CA.pl","-newca",
"-extra-req", "-key $cakey"], stdin => undef)),
'creating CA structure');
my $eekey = srctop_file("test", "certs", "ee-key.pem");
my $eekey = src_file("ee-key.pem");
$ENV{OPENSSL_CONFIG} = qq(-config "$cnf");
skip "failed creating new certificate request", 3
if !ok(run(perlapp(["CA.pl","-newreq",
@ -53,7 +60,7 @@ plan tests => 15;
skip "CT not configured, can't use -precert", 1
if disabled("ct");
my $eekey2 = srctop_file("test", "certs", "ee-key-3072.pem");
my $eekey2 = src_file("ee-key-3072.pem");
$ENV{OPENSSL_CONFIG} = qq(-config "$cnf");
ok(run(perlapp(["CA.pl", "-precert", '-extra-req', "-section userreq -key $eekey2"], stderr => undef)),
'creating new pre-certificate');
@ -65,17 +72,25 @@ SKIP: {
is(yes(cmdstr(app(["openssl", "ca", "-config",
$cnf,
"-in", srctop_file("test", "certs", "sm2-csr.pem"),
"-in", src_file("sm2-csr.pem"),
"-out", "sm2-test.crt",
"-sigopt", "distid:1234567812345678",
"-vfyopt", "distid:1234567812345678",
"-md", "sm3",
"-cert", srctop_file("test", "certs", "sm2-root.crt"),
"-keyfile", srctop_file("test", "certs", "sm2-root.key")]))),
"-cert", src_file("sm2-root.crt"),
"-keyfile", src_file("sm2-root.key")]))),
0,
"Signing SM2 certificate request");
}
my $v3_cert = "v3-test.crt";
ok(run(app(["openssl", "ca", "-batch", "-config", $cnf, "-extensions", "empty",
"-in", src_file("x509-check.csr"), "-out", $v3_cert])));
# although no explicit extensions given:
has_version($v3_cert, 3);
has_SKID($v3_cert, 1);
has_AKID($v3_cert, 1);
test_revoke('notimes', {
should_succeed => 1,
});

View File

@ -402,7 +402,7 @@ sub init {
}, grep(/-key-pkcs8-pbes2-sha256\.pem$/, @generated_files))
# *-cert.pem (intermediary for the .p12 inits)
&& run(app(["openssl", "req", "-x509", @std_args,
"-config", $cnf, "-noenc",
"-config", $cnf, "-reqexts", "v3_ca", "-noenc",
"-key", $cakey, "-out", "cacert.pem"]))
&& runall(sub {
my $srckey = shift;

View File

@ -132,6 +132,24 @@ sub cert_contains {
# not unlinking $out
}
sub has_version {
my $cert = shift @_;
my $expect = shift @_;
cert_contains($cert, "Version: $expect", 1);
}
sub has_SKID {
my $cert = shift @_;
my $expect = shift @_;
cert_contains($cert, "Subject Key Identifier", $expect);
}
sub has_AKID {
my $cert = shift @_;
my $expect = shift @_;
cert_contains($cert, "Authority Key Identifier", $expect);
}
sub uniq (@) {
my %seen = ();
grep { not $seen{$_}++ } @_;