* 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.
!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') */
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,
}
/* 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;
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,
{"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"},
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;
case OPT_TEXT:
text = 1;
break;
+ case OPT_X509V1:
+ x509v1 = 1;
+ /* fall thru */
case OPT_X509:
gen_x509 = 1;
break;
}
}
- 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 {
}
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,
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) {
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
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,
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.
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.
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)>,
[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>]
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
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.
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.
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.
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>
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.
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.
=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
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()
=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:
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.
-Subproject commit b2b4d629f100eaee9f5942a106b1ccefe85b8808
+Subproject commit a6b90523e4ea6010b1109b0bae7e2a73b5b025c5
0.commonName = Brother 1
1.commonName = $ENV::CN2
+[ empty ]
+
[ v3_ee ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
setup("test_req");
-plan tests => 92;
+plan tests => 102;
require_ok(srctop_file('test', 'recipes', 'tconversion.pl'));
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 @_;
# # 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
# 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
$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);
# 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"));
setup("test_x509");
-plan tests => 29;
+plan tests => 32;
# Prevent MSys2 filename munging for arguments that look like file paths but
# aren't
# 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");
. 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",
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');
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,
});
}, 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;
# 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{$_}++ } @_;