summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/req.c17
-rw-r--r--apps/x509.c8
-rw-r--r--crypto/x509/v3_akey.c42
-rw-r--r--crypto/x509/v3_alt.c6
-rw-r--r--crypto/x509/v3_conf.c34
-rw-r--r--crypto/x509/v3_skey.c63
-rw-r--r--crypto/x509/v3_utf8.c1
-rw-r--r--doc/internal/man3/s2i_ASN1_UTF8STRING.pod46
-rw-r--r--doc/man3/ASN1_generate_nconf.pod6
-rw-r--r--doc/man3/X509V3_set_ctx.pod60
-rw-r--r--doc/man3/s2i_ASN1_IA5STRING.pod21
-rw-r--r--doc/man5/x509v3_config.pod14
-rw-r--r--include/crypto/x509.h2
-rw-r--r--include/crypto/x509v3.h23
-rw-r--r--include/openssl/x509v3.h.in13
-rw-r--r--test/recipes/25-test_req.t25
-rw-r--r--test/recipes/tconversion.pl32
-rw-r--r--util/libcrypto.num3
-rw-r--r--util/missingcrypto.txt1
-rw-r--r--util/missingmacro.txt1
20 files changed, 264 insertions, 154 deletions
diff --git a/apps/req.c b/apps/req.c
index c57d338ec9..13d54770db 100644
--- a/apps/req.c
+++ b/apps/req.c
@@ -532,6 +532,7 @@ int req_main(int argc, char **argv)
if (extensions != NULL) {
/* Check syntax of file */
X509V3_CTX ctx;
+
X509V3_set_ctx_test(&ctx);
X509V3_set_nconf(&ctx, req_conf);
if (!X509V3_EXT_add_nconf(req_conf, &ctx, extensions, NULL)) {
@@ -544,6 +545,7 @@ int req_main(int argc, char **argv)
if (addext_conf != NULL) {
/* Check syntax of command line extensions */
X509V3_CTX ctx;
+
X509V3_set_ctx_test(&ctx);
X509V3_set_nconf(&ctx, addext_conf);
if (!X509V3_EXT_add_nconf(addext_conf, &ctx, "default", NULL)) {
@@ -591,6 +593,7 @@ int req_main(int argc, char **argv)
if (req_exts != NULL) {
/* Check syntax of file */
X509V3_CTX ctx;
+
X509V3_set_ctx_test(&ctx);
X509V3_set_nconf(&ctx, req_conf);
if (!X509V3_EXT_add_nconf(req_conf, &ctx, req_exts, NULL)) {
@@ -773,7 +776,7 @@ int req_main(int argc, char **argv)
}
if (newreq || gen_x509) {
if (pkey == NULL /* can happen only if !newreq */) {
- BIO_printf(bio_err, "Must provide the corresponding private key using -key\n");
+ BIO_printf(bio_err, "Must provide a signature key using -key\n");
goto end;
}
@@ -793,7 +796,8 @@ int req_main(int argc, char **argv)
X509V3_CTX ext_ctx;
X509_NAME *issuer = CAcert != NULL ? X509_get_subject_name(CAcert) :
X509_REQ_get_subject_name(req);
- X509_NAME *n_subj = X509_REQ_get_subject_name(req);
+ X509_NAME *n_subj = fsubj != NULL ? fsubj :
+ X509_REQ_get_subject_name(req);
if ((new_x509 = X509_new_ex(app_get0_libctx(),
app_get0_propq())) == NULL)
@@ -823,6 +827,15 @@ int req_main(int argc, char **argv)
/* Set up V3 context struct */
X509V3_set_ctx(&ext_ctx, CAcert != NULL ? CAcert : new_x509,
new_x509, NULL, NULL, X509V3_CTX_REPLACE);
+ if (CAcert == NULL) { /* self-issued, possibly self-signed */
+ if (!X509V3_set_issuer_pkey(&ext_ctx, pkey)) /* prepare right AKID */
+ goto end;
+ ERR_set_mark();
+ if (!X509_check_private_key(new_x509, pkey))
+ BIO_printf(bio_err,
+ "Warning: Signature key and public key of cert do not match\n");
+ ERR_pop_to_mark();
+ }
X509V3_set_nconf(&ext_ctx, req_conf);
/* Add extensions */
diff --git a/apps/x509.c b/apps/x509.c
index 34d654c8f2..5769f5f982 100644
--- a/apps/x509.c
+++ b/apps/x509.c
@@ -1079,7 +1079,13 @@ static int sign(X509 *x, EVP_PKEY *pkey, X509 *issuer,
while (X509_get_ext_count(x) > 0)
X509_delete_ext(x, 0);
}
+
X509V3_set_ctx(&ext_ctx, issuer, x, NULL, NULL, X509V3_CTX_REPLACE);
+ if (issuer == x
+ /* prepare the correct AKID of self-issued, possibly self-signed cert */
+ && !X509V3_set_issuer_pkey(&ext_ctx, pkey))
+ return 0;
+
if (conf != NULL) {
X509V3_set_nconf(&ext_ctx, conf);
if (!X509V3_EXT_add_nconf(conf, &ext_ctx, section, x)) {
@@ -1149,7 +1155,7 @@ static int print_x509v3_exts(BIO *bio, X509 *x, const char *ext_names)
exts = X509_get0_extensions(x);
if ((num = sk_X509_EXTENSION_num(exts)) <= 0) {
- BIO_printf(bio, "No extensions in certificate\n");
+ BIO_printf(bio_err, "No extensions in certificate\n");
ret = 1;
goto end;
}
diff --git a/crypto/x509/v3_akey.c b/crypto/x509/v3_akey.c
index 2e90d495c5..d0d20c4455 100644
--- a/crypto/x509/v3_akey.c
+++ b/crypto/x509/v3_akey.c
@@ -13,6 +13,7 @@
#include <openssl/asn1.h>
#include <openssl/asn1t.h>
#include <openssl/x509v3.h>
+#include "crypto/x509.h"
#include "ext_dat.h"
static STACK_OF(CONF_VALUE) *i2v_AUTHORITY_KEYID(X509V3_EXT_METHOD *method,
@@ -86,7 +87,7 @@ static AUTHORITY_KEYID *v2i_AUTHORITY_KEYID(X509V3_EXT_METHOD *method,
GENERAL_NAME *gen = NULL;
ASN1_INTEGER *serial = NULL;
X509_EXTENSION *ext;
- X509 *cert;
+ X509 *issuer_cert;
AUTHORITY_KEYID *akeyid = AUTHORITY_KEYID_new();
if (akeyid == NULL)
@@ -113,36 +114,49 @@ static AUTHORITY_KEYID *v2i_AUTHORITY_KEYID(X509V3_EXT_METHOD *method,
}
}
- if (!ctx || !ctx->issuer_cert) {
- if (ctx && (ctx->flags == CTX_TEST))
- return akeyid;
+ if (ctx != NULL && (ctx->flags & CTX_TEST) != 0)
+ return akeyid;
+
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_NULL_PARAMETER);
+ goto err;
+ }
+ if ((issuer_cert = ctx->issuer_cert) == NULL) {
ERR_raise(ERR_LIB_X509V3, X509V3_R_NO_ISSUER_CERTIFICATE);
goto err;
}
- cert = ctx->issuer_cert;
- if (keyid) {
- i = X509_get_ext_by_NID(cert, NID_subject_key_identifier, -1);
- if ((i >= 0) && (ext = X509_get_ext(cert, i)))
+ if (keyid != 0) {
+ /* prefer any pre-existing subject key identifier of the issuer cert */
+ i = X509_get_ext_by_NID(issuer_cert, NID_subject_key_identifier, -1);
+ if (i >= 0 && (ext = X509_get_ext(issuer_cert, i)) != NULL)
ikeyid = X509V3_EXT_d2i(ext);
+ if (ikeyid == NULL && ctx->issuer_pkey != NULL) { /* fallback */
+ /* generate AKID from scratch, emulating s2i_skey_id(..., "hash") */
+ X509_PUBKEY *pubkey = NULL;
+
+ if (X509_PUBKEY_set(&pubkey, ctx->issuer_pkey))
+ ikeyid = x509_pubkey_hash(pubkey);
+ X509_PUBKEY_free(pubkey);
+ }
if ((keyid == 2 || issuer == 0)
&& (ikeyid == NULL
- || ASN1_STRING_length(ikeyid) <= 2) /* indicating "none" */ ) {
+ || ASN1_STRING_length(ikeyid) <= 2) /* indicating "none" */) {
ERR_raise(ERR_LIB_X509V3, X509V3_R_UNABLE_TO_GET_ISSUER_KEYID);
goto err;
}
}
- if ((issuer && !ikeyid) || (issuer == 2)) {
- isname = X509_NAME_dup(X509_get_issuer_name(cert));
- serial = ASN1_INTEGER_dup(X509_get0_serialNumber(cert));
- if (!isname || !serial) {
+ if (issuer == 2 || (issuer == 1 && ikeyid == NULL)) {
+ isname = X509_NAME_dup(X509_get_issuer_name(issuer_cert));
+ serial = ASN1_INTEGER_dup(X509_get0_serialNumber(issuer_cert));
+ if (isname == NULL || serial == NULL) {
ERR_raise(ERR_LIB_X509V3, X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS);
goto err;
}
}
- if (isname) {
+ if (isname != NULL) {
if ((gens = sk_GENERAL_NAME_new_null()) == NULL
|| (gen = GENERAL_NAME_new()) == NULL
|| !sk_GENERAL_NAME_push(gens, gen)) {
diff --git a/crypto/x509/v3_alt.c b/crypto/x509/v3_alt.c
index 2344c554fa..d2e3ec138b 100644
--- a/crypto/x509/v3_alt.c
+++ b/crypto/x509/v3_alt.c
@@ -325,7 +325,7 @@ static int copy_issuer(X509V3_CTX *ctx, GENERAL_NAMES *gens)
X509_EXTENSION *ext;
int i, num;
- if (ctx && (ctx->flags == CTX_TEST))
+ if (ctx != NULL && (ctx->flags & CTX_TEST) != 0)
return 1;
if (!ctx || !ctx->issuer_cert) {
ERR_raise(ERR_LIB_X509V3, X509V3_R_NO_ISSUER_DETAILS);
@@ -410,12 +410,12 @@ static int copy_email(X509V3_CTX *ctx, GENERAL_NAMES *gens, int move_p)
GENERAL_NAME *gen = NULL;
int i = -1;
- if (ctx != NULL && ctx->flags == CTX_TEST)
+ if (ctx != NULL && (ctx->flags & CTX_TEST) != 0)
return 1;
if (ctx == NULL
|| (ctx->subject_cert == NULL && ctx->subject_req == NULL)) {
ERR_raise(ERR_LIB_X509V3, X509V3_R_NO_SUBJECT_DETAILS);
- goto err;
+ return 0;
}
/* Find the subject name */
if (ctx->subject_cert)
diff --git a/crypto/x509/v3_conf.c b/crypto/x509/v3_conf.c
index 1f424325a0..f8a2e3fe27 100644
--- a/crypto/x509/v3_conf.c
+++ b/crypto/x509/v3_conf.c
@@ -437,6 +437,10 @@ static X509V3_CONF_METHOD nconf_method = {
void X509V3_set_nconf(X509V3_CTX *ctx, CONF *conf)
{
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_NULL_PARAMETER);
+ return;
+ }
ctx->db_meth = &nconf_method;
ctx->db = conf;
}
@@ -444,11 +448,33 @@ void X509V3_set_nconf(X509V3_CTX *ctx, CONF *conf)
void X509V3_set_ctx(X509V3_CTX *ctx, X509 *issuer, X509 *subj, X509_REQ *req,
X509_CRL *crl, int flags)
{
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_NULL_PARAMETER);
+ return;
+ }
+ ctx->flags = flags;
ctx->issuer_cert = issuer;
ctx->subject_cert = subj;
- ctx->crl = crl;
ctx->subject_req = req;
- ctx->flags = flags;
+ ctx->crl = crl;
+ ctx->db_meth = NULL;
+ ctx->db = NULL;
+ ctx->issuer_pkey = NULL;
+}
+
+/* For API backward compatibility, this is separate from X509V3_set_ctx() */
+int X509V3_set_issuer_pkey(X509V3_CTX *ctx, EVP_PKEY *pkey)
+{
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+ if (ctx->subject_cert == NULL && pkey != NULL) {
+ ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+ ctx->issuer_pkey = pkey;
+ return 1;
}
/* Old conf compatibility functions */
@@ -489,6 +515,10 @@ static X509V3_CONF_METHOD conf_lhash_method = {
void X509V3_set_conf_lhash(X509V3_CTX *ctx, LHASH_OF(CONF_VALUE) *lhash)
{
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_NULL_PARAMETER);
+ return;
+ }
ctx->db_meth = &conf_lhash_method;
ctx->db = lhash;
}
diff --git a/crypto/x509/v3_skey.c b/crypto/x509/v3_skey.c
index 6122596081..8d13dc248a 100644
--- a/crypto/x509/v3_skey.c
+++ b/crypto/x509/v3_skey.c
@@ -52,56 +52,49 @@ ASN1_OCTET_STRING *s2i_ASN1_OCTET_STRING(X509V3_EXT_METHOD *method,
}
-static ASN1_OCTET_STRING *s2i_skey_id(X509V3_EXT_METHOD *method,
- X509V3_CTX *ctx, char *str)
+ASN1_OCTET_STRING *x509_pubkey_hash(X509_PUBKEY *pubkey)
{
ASN1_OCTET_STRING *oct;
- X509_PUBKEY *pubkey;
const unsigned char *pk;
int pklen;
unsigned char pkey_dig[EVP_MAX_MD_SIZE];
unsigned int diglen;
- if (strcmp(str, "none") == 0)
- return ASN1_OCTET_STRING_new(); /* dummy */
-
- if (strcmp(str, "hash") != 0)
- return s2i_ASN1_OCTET_STRING(method, ctx, str);
-
- if ((oct = ASN1_OCTET_STRING_new()) == NULL) {
- ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE);
+ if (pubkey == NULL) {
+ ERR_raise(ERR_LIB_X509V3, X509V3_R_NO_PUBLIC_KEY);
return NULL;
}
+ if ((oct = ASN1_OCTET_STRING_new()) == NULL)
+ return NULL;
- if (ctx && (ctx->flags == CTX_TEST))
+ X509_PUBKEY_get0_param(NULL, &pk, &pklen, NULL, pubkey);
+ /* TODO(3.0) - explicitly fetch the digest */
+ if (EVP_Digest(pk, pklen, pkey_dig, &diglen, EVP_sha1(), NULL)
+ && ASN1_OCTET_STRING_set(oct, pkey_dig, diglen))
return oct;
- if (!ctx || (!ctx->subject_req && !ctx->subject_cert)) {
- ERR_raise(ERR_LIB_X509V3, X509V3_R_NO_PUBLIC_KEY);
- goto err;
- }
-
- pubkey = ctx->subject_req != NULL ?
- ctx->subject_req->req_info.pubkey :
- ctx->subject_cert->cert_info.key;
- if (pubkey == NULL) {
- ERR_raise(ERR_LIB_X509V3, X509V3_R_NO_PUBLIC_KEY);
- goto err;
- }
+ ASN1_OCTET_STRING_free(oct);
+ return NULL;
+}
- X509_PUBKEY_get0_param(NULL, &pk, &pklen, NULL, pubkey);
+static ASN1_OCTET_STRING *s2i_skey_id(X509V3_EXT_METHOD *method,
+ X509V3_CTX *ctx, char *str)
+{
+ if (strcmp(str, "none") == 0)
+ return ASN1_OCTET_STRING_new(); /* dummy */
- if (!EVP_Digest(pk, pklen, pkey_dig, &diglen, EVP_sha1(), NULL))
- goto err;
+ if (strcmp(str, "hash") != 0)
+ return s2i_ASN1_OCTET_STRING(method, ctx /* not used */, str);
- if (!ASN1_OCTET_STRING_set(oct, pkey_dig, diglen)) {
- ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE);
- goto err;
+ if (ctx != NULL && (ctx->flags & CTX_TEST) != 0)
+ return ASN1_OCTET_STRING_new();
+ if (ctx == NULL
+ || (ctx->subject_cert == NULL && ctx->subject_req == NULL)) {
+ ERR_raise(ERR_LIB_X509V3, X509V3_R_NO_SUBJECT_DETAILS);
+ return NULL;
}
- return oct;
-
- err:
- ASN1_OCTET_STRING_free(oct);
- return NULL;
+ return x509_pubkey_hash(ctx->subject_req != NULL ?
+ ctx->subject_req->req_info.pubkey :
+ ctx->subject_cert->cert_info.key);
}
diff --git a/crypto/x509/v3_utf8.c b/crypto/x509/v3_utf8.c
index d37ac73246..465e0a39a3 100644
--- a/crypto/x509/v3_utf8.c
+++ b/crypto/x509/v3_utf8.c
@@ -12,7 +12,6 @@
#include <openssl/asn1.h>
#include <openssl/conf.h>
#include <openssl/x509v3.h>
-#include <crypto/x509v3.h>
#include "ext_dat.h"
/*
diff --git a/doc/internal/man3/s2i_ASN1_UTF8STRING.pod b/doc/internal/man3/s2i_ASN1_UTF8STRING.pod
deleted file mode 100644
index b6d1375189..0000000000
--- a/doc/internal/man3/s2i_ASN1_UTF8STRING.pod
+++ /dev/null
@@ -1,46 +0,0 @@
-=pod
-
-=head1 NAME
-
-i2s_ASN1_UTF8STRING,
-s2i_ASN1_UTF8STRING
-- convert objects from/to ASN.1/string representation
-
-=head1 SYNOPSIS
-
- #include "crypto/x509v3.h"
-
- char *i2s_ASN1_UTF8STRING(X509V3_EXT_METHOD *method,
- ASN1_UTF8STRING *utf8);
- ASN1_UTF8STRING *s2i_ASN1_UTF8STRING(X509V3_EXT_METHOD *method,
- X509V3_CTX *ctx, const char *str);
-
-=head1 DESCRIPTION
-
-These functions convert OpenSSL objects to and from their ASN.1/string
-representation. This function is used for B<X509v3> extensions.
-
-=head1 NOTES
-
-The letters B<i> and B<s> in i2s_ASN1_UTF8STRING() stand for
-"internal" (that is, an internal C structure) and string respectively.
-So B<i2s_ASN1_UTF8STRING>() converts from internal to string.
-
-=head1 RETURN VALUES
-
-B<s2i_ASN1_UTF8STRING>() return a valid
-B<ASN1_UTF8STRING> structure or NULL if an error occurs.
-
-B<i2s_ASN1_UTF8STRING>() returns the pointer to a UTF-8 string
-or NULL if an error occurs.
-
-=head1 COPYRIGHT
-
-Copyright 2020 The OpenSSL Project Authors. All Rights Reserved.
-
-Licensed under the Apache License 2.0 (the "License"). You may not use
-this file except in compliance with the License. You can obtain a copy
-in the file LICENSE in the source distribution or at
-L<https://www.openssl.org/source/license.html>.
-
-=cut
diff --git a/doc/man3/ASN1_generate_nconf.pod b/doc/man3/ASN1_generate_nconf.pod
index a507e444c6..8b195d523f 100644
--- a/doc/man3/ASN1_generate_nconf.pod
+++ b/doc/man3/ASN1_generate_nconf.pod
@@ -2,7 +2,7 @@
=head1 NAME
-ASN1_generate_nconf, ASN1_generate_v3 - ASN1 generation functions
+ASN1_generate_nconf, ASN1_generate_v3 - ASN1 string generation functions
=head1 SYNOPSIS
@@ -16,10 +16,10 @@ ASN1_generate_nconf, ASN1_generate_v3 - ASN1 generation functions
These functions generate the ASN1 encoding of a string
in an B<ASN1_TYPE> structure.
-I<str> contains the string to encode I<nconf> or I<cnf> contains
+I<str> contains the string to encode. I<nconf> or I<cnf> contains
the optional configuration information where additional strings
will be read from. I<nconf> will typically come from a config
-file whereas I<cnf> is obtained from an B<X509V3_CTX> structure
+file whereas I<cnf> is obtained from an B<X509V3_CTX> structure,
which will typically be used by X509 v3 certificate extension
functions. I<cnf> or I<nconf> can be set to NULL if no additional
configuration will be used.
diff --git a/doc/man3/X509V3_set_ctx.pod b/doc/man3/X509V3_set_ctx.pod
new file mode 100644
index 0000000000..136e3f1982
--- /dev/null
+++ b/doc/man3/X509V3_set_ctx.pod
@@ -0,0 +1,60 @@
+=pod
+
+=head1 NAME
+
+X509V3_set_ctx,
+X509V3_set_issuer_pkey - X.509v3 extension generation utility functions
+
+=head1 SYNOPSIS
+
+ #include <openssl/x509v3.h>
+
+ void X509V3_set_ctx(X509V3_CTX *ctx, X509 *issuer, X509 *subject,
+ X509_REQ *req, X509_CRL *crl, int flags);
+ int X509V3_set_issuer_pkey(X509V3_CTX *ctx, EVP_PKEY *pkey);
+
+=head1 DESCRIPTION
+
+X509V3_set_ctx() fills in the basic fields of I<ctx> of type B<X509V3_CTX>,
+providing details potentially needed by functions producing X509 v3 certificate
+extensions, e.g., to look up values for filling in authority key identifiers.
+Any of I<subj>, I<req>, or I<crl> may be provided, pointing to a certificate,
+certification request, or certificate revocation list, respectively.
+If I<subj> or I<crl> is provided, I<issuer> should point to its issuer,
+for instance to help generating an authority key identifier extension.
+Note that if I<subj> is provided, I<issuer> may be the same as I<subj>,
+which means that I<subj> is self-issued (or even self-signed).
+I<flags> may be 0 or contain B<CTX_TEST>, which means that just the syntax of
+extension definitions is to be checked without actually producing an extension,
+or B<X509V3_CTX_REPLACE>, which means that each X.509v3 extension added as
+defined in some configuration section shall replace any already existing
+extension with the same OID.
+
+X509V3_set_issuer_pkey() explicitly sets the issuer private key of
+the certificate that has been provided in I<ctx>.
+This should be done for self-issued certificates (which may be self-signed
+or not) to provide fallback data for the authority key identifier extension.
+
+=head1 RETURN VALUES
+
+X509V3_set_ctx() and X509V3_set_issuer_pkey()
+return 1 on success and 0 on error.
+
+=head1 SEE ALSO
+
+L<X509_add_ext(3)>
+
+=head1 HISTORY
+
+X509V3_set_issuer_pkey() was added in OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 2015-2020 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License"). You may not use
+this file except in compliance with the License. You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man3/s2i_ASN1_IA5STRING.pod b/doc/man3/s2i_ASN1_IA5STRING.pod
index e59a85606b..5215b6a179 100644
--- a/doc/man3/s2i_ASN1_IA5STRING.pod
+++ b/doc/man3/s2i_ASN1_IA5STRING.pod
@@ -10,11 +10,13 @@ i2s_ASN1_OCTET_STRING,
s2i_ASN1_OCTET_STRING,
i2s_ASN1_ENUMERATED,
i2s_ASN1_ENUMERATED_TABLE,
+i2s_ASN1_UTF8STRING,
+s2i_ASN1_UTF8STRING
- convert objects from/to ASN.1/string representation
=head1 SYNOPSIS
-=for openssl generic
+ #include <openssl/x509v3.h>
char *i2s_ASN1_IA5STRING(X509V3_EXT_METHOD *method, ASN1_IA5STRING *ia5);
ASN1_IA5STRING *s2i_ASN1_IA5STRING(X509V3_EXT_METHOD *method,
@@ -29,6 +31,11 @@ i2s_ASN1_ENUMERATED_TABLE,
char *i2s_ASN1_ENUMERATED_TABLE(X509V3_EXT_METHOD *method,
const ASN1_ENUMERATED *e);
+ char *i2s_ASN1_UTF8STRING(X509V3_EXT_METHOD *method,
+ ASN1_UTF8STRING *utf8);
+ ASN1_UTF8STRING *s2i_ASN1_UTF8STRING(X509V3_EXT_METHOD *method,
+ X509V3_CTX *ctx, const char *str);
+
=head1 DESCRIPTION
These functions convert OpenSSL objects to and from their ASN.1/string
@@ -36,7 +43,7 @@ representation. This function is used for B<X509v3> extensions.
=head1 NOTES
-The letters B<i> and B<s> in B<i2s_ASN1_IA5STRING>() stand for
+The letters B<i> and B<s> in B<i2s> and B<s2i> stand for
"internal" (that is, an internal C structure) and string respectively.
So B<i2s_ASN1_IA5STRING>() converts from internal to string.
@@ -70,6 +77,16 @@ string or NULL if an error occurs.
B<s2i_ASN1_ENUMERATED>() returns the pointer to a B<ASN1_ENUMERATED>
structure or NULL if an error occurs.
+B<s2i_ASN1_UTF8STRING>() return a valid
+B<ASN1_UTF8STRING> structure or NULL if an error occurs.
+
+B<i2s_ASN1_UTF8STRING>() returns the pointer to a UTF-8 string
+or NULL if an error occurs.
+
+=head1 HISTORY
+
+i2s_ASN1_UTF8STRING() and s2i_ASN1_UTF8STRING() were made public in OpenSSL 3.0.
+
=head1 COPYRIGHT
Copyright 2020 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/doc/man5/x509v3_config.pod b/doc/man5/x509v3_config.pod
index b2ee41b853..c15a1d0ce0 100644
--- a/doc/man5/x509v3_config.pod
+++ b/doc/man5/x509v3_config.pod
@@ -169,7 +169,7 @@ 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<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:
The keyIdentifier is composed of the 160-bit SHA-1 hash of the value of the BIT
@@ -193,14 +193,14 @@ indicated by putting a colon C<:> between the value and this option.
By default the B<x509>, B<req>, and B<ca> apps behave as if
"none" was given for self-signed certificates and "keyid, issuer" otherwise.
-If B<keyid> is present, an attempt is made to copy the subject key identifier
-(SKID) from the issuer certificate, which is the default behavior.
+If B<keyid> is present, an attempt is made to compute the hash of the public key
+corresponding to the signing key in case the certificate is self-signed,
+or else to copy the subject key identifier (SKID) from the issuer certificate.
If this fails and the option B<always> is present, an error is returned.
-For self-issued certs the specification for the SKID must be given before.
-If B<issuer> is present and no B<keyid> has been added
-or it has the option B<always> specified, then
-the issuer DN and serial number are copied from the issuer certificate.
+If B<issuer> is present, and in addition it has the option B<always> specified
+or B<keyid> is not present,
+then the issuer DN and serial number are copied from the issuer certificate.
Examples:
diff --git a/include/crypto/x509.h b/include/crypto/x509.h
index d88cd31902..1003f6f652 100644
--- a/include/crypto/x509.h
+++ b/include/crypto/x509.h
@@ -318,3 +318,5 @@ int X509_add_cert_new(STACK_OF(X509) **sk, X509 *cert, int flags);
int X509_PUBKEY_get0_libctx(OSSL_LIB_CTX **plibctx, const char **ppropq,
const X509_PUBKEY *key);
+/* Calculate default key identifier according to RFC 5280 section 4.2.1.2 (1) */
+ASN1_OCTET_STRING *x509_pubkey_hash(X509_PUBKEY *pubkey);
diff --git a/include/crypto/x509v3.h b/include/crypto/x509v3.h
deleted file mode 100644
index 4ca85e9a2e..0000000000
--- a/include/crypto/x509v3.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2020 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
-#ifndef OSSL_CRYPTO_X509V3_H
-# define OSSL_CRYPTO_X509V3_H
-
-#define EXT_UTF8STRING(nid) { nid, 0, ASN1_ITEM_ref(ASN1_UTF8STRING), \
- 0,0,0,0, \
- (X509V3_EXT_I2S)i2s_ASN1_UTF8STRING, \
- (X509V3_EXT_S2I)s2i_ASN1_UTF8STRING, \
- 0,0,0,0, \
- NULL}
-
-char *i2s_ASN1_UTF8STRING(X509V3_EXT_METHOD *method, ASN1_UTF8STRING *utf8);
-ASN1_UTF8STRING *s2i_ASN1_UTF8STRING(X509V3_EXT_METHOD *method,
- X509V3_CTX *ctx, const char *str);
-
-#endif
diff --git a/include/openssl/x509v3.h.in b/include/openssl/x509v3.h.in
index dad8694ffa..3726f37999 100644
--- a/include/openssl/x509v3.h.in
+++ b/include/openssl/x509v3.h.in
@@ -98,6 +98,7 @@ struct v3_ext_ctx {
X509_CRL *crl;
X509V3_CONF_METHOD *db_meth;
void *db;
+ EVP_PKEY *issuer_pkey;
/* Maybe more here */
};
@@ -380,6 +381,13 @@ struct ISSUING_DIST_POINT_st {
0,0,0,0, \
NULL}
+#define EXT_UTF8STRING(nid) { nid, 0, ASN1_ITEM_ref(ASN1_UTF8STRING), \
+ 0,0,0,0, \
+ (X509V3_EXT_I2S)i2s_ASN1_UTF8STRING, \
+ (X509V3_EXT_S2I)s2i_ASN1_UTF8STRING, \
+ 0,0,0,0, \
+ NULL}
+
# define EXT_END { -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
/* X509_PURPOSE stuff */
@@ -525,6 +533,9 @@ STACK_OF(CONF_VALUE) *i2v_ASN1_BIT_STRING(X509V3_EXT_METHOD *method,
char *i2s_ASN1_IA5STRING(X509V3_EXT_METHOD *method, ASN1_IA5STRING *ia5);
ASN1_IA5STRING *s2i_ASN1_IA5STRING(X509V3_EXT_METHOD *method,
X509V3_CTX *ctx, const char *str);
+char *i2s_ASN1_UTF8STRING(X509V3_EXT_METHOD *method, ASN1_UTF8STRING *utf8);
+ASN1_UTF8STRING *s2i_ASN1_UTF8STRING(X509V3_EXT_METHOD *method,
+ X509V3_CTX *ctx, const char *str);
STACK_OF(CONF_VALUE) *i2v_GENERAL_NAME(X509V3_EXT_METHOD *method,
GENERAL_NAME *gen,
@@ -645,6 +656,8 @@ void X509V3_string_free(X509V3_CTX *ctx, char *str);
void X509V3_section_free(X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *section);
void X509V3_set_ctx(X509V3_CTX *ctx, X509 *issuer, X509 *subject,
X509_REQ *req, X509_CRL *crl, int flags);
+/* For API backward compatibility, this is separate from X509V3_set_ctx(): */
+int X509V3_set_issuer_pkey(X509V3_CTX *ctx, EVP_PKEY *pkey);
int X509V3_add_value(const char *name, const char *value,
STACK_OF(CONF_VALUE) **extlist);
diff --git a/test/recipes/25-test_req.t b/test/recipes/25-test_req.t
index cb9f8888a5..7f699c065d 100644
--- a/test/recipes/25-test_req.t
+++ b/test/recipes/25-test_req.t
@@ -15,9 +15,9 @@ use OpenSSL::Test qw/:DEFAULT srctop_file/;
setup("test_req");
-plan tests => 39;
+plan tests => 38;
-require_ok(srctop_file('test','recipes','tconversion.pl'));
+require_ok(srctop_file('test', 'recipes', 'tconversion.pl'));
my @certs = qw(test certs);
@@ -259,7 +259,7 @@ sub generate_cert {
my $cn = $is_ca ? "CA" : "EE";
my $ca_key = srctop_file(@certs, "ca-key.pem");
my $key = $is_ca ? $ca_key : srctop_file(@certs, "ee-key.pem");
- my @cmd = ("openssl", "req", "-config", "\"\"","-x509",
+ my @cmd = ("openssl", "req", "-config", "\"\"", "-x509",
"-key", $key, "-subj", "/CN=$cn", @_, "-out", $cert);
push(@cmd, ("-CA", $ca_cert, "-CAkey", $ca_key)) unless $ss;
ok(run(app([@cmd])), "generate $cert");
@@ -286,10 +286,10 @@ sub strict_verify {
my @v3_ca = ("-addext", "basicConstraints = critical,CA:true",
"-addext", "keyUsage = keyCertSign");
+my $SKID_AKID = "subjectKeyIdentifier,authorityKeyIdentifier";
my $cert = "self-signed_v1_CA_no_KIDs.pem";
generate_cert($cert);
-has_SKID($ca_cert, 0);
-has_AKID($ca_cert, 0);
+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
$ca_cert = "self-signed_v3_CA_default_SKID.pem";
@@ -300,15 +300,13 @@ strict_verify($ca_cert, 1);
$cert = "self-signed_v3_CA_no_SKID.pem";
generate_cert($cert, @v3_ca, "-addext", "subjectKeyIdentifier = none");
-has_SKID($cert, 0);
-has_AKID($cert, 0);
+cert_ext_has_n_different_lines($cert, 0, $SKID_AKID); # no SKID and no AKID
#TODO strict_verify($cert, 0);
$cert = "self-signed_v3_CA_both_KIDs.pem";
generate_cert($cert, @v3_ca, "-addext", "subjectKeyIdentifier = hash",
"-addext", "authorityKeyIdentifier = keyid");
-has_SKID($cert, 1);
-has_AKID($cert, 1);
+cert_ext_has_n_different_lines($cert, 3, $SKID_AKID); # SKID == AKID
strict_verify($cert, 1);
$cert = "self-signed_v3_EE_wrong_keyUsage.pem";
@@ -317,8 +315,7 @@ generate_cert($cert, "-addext", "keyUsage = keyCertSign");
$cert = "v3_EE_default_KIDs.pem";
generate_cert($cert, "-addext", "keyUsage = dataEncipherment");
-has_SKID($cert, 1);
-has_AKID($cert, 1);
+cert_ext_has_n_different_lines($cert, 4, $SKID_AKID); # SKID != AKID
strict_verify($cert, 1, $ca_cert);
$cert = "v3_EE_no_AKID.pem";
@@ -326,3 +323,9 @@ generate_cert($cert, "-addext", "authorityKeyIdentifier = none");
has_SKID($cert, 1);
has_AKID($cert, 0);
strict_verify($cert, 0, $ca_cert);
+
+$cert = "self-issued_v3_EE_default_KIDs.pem";
+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);
diff --git a/test/recipes/tconversion.pl b/test/recipes/tconversion.pl
index bf096994e3..6ae5cf17ea 100644
--- a/test/recipes/tconversion.pl
+++ b/test/recipes/tconversion.pl
@@ -111,8 +111,8 @@ sub cmp_text {
sub file_contains {
$_ = shift @_;
my $pattern = shift @_;
- open(DATA,$_) or return 0;
- $_= join('',<DATA>);
+ open(DATA, $_) or return 0;
+ $_= join('', <DATA>);
close(DATA);
return m/$pattern/ ? 1 : 0;
}
@@ -122,11 +122,37 @@ sub cert_contains {
my $pattern = shift @_;
my $expected = shift @_;
my $name = shift @_;
- my $out = "tmp.out";
+ my $out = "cert_contains.out";
run(app(["openssl", "x509", "-noout", "-text", "-in", $cert, "-out", $out]));
is(file_contains($out, $pattern), $expected, ($name ? "$name: " : "").
"$cert should ".($expected ? "" : "not ")."contain $pattern");
# not unlinking $out
}
+sub uniq (@) {
+ my %seen = ();
+ grep { not $seen{$_}++ } @_;
+}
+
+sub file_n_different_lines {
+ my $filename = shift @_;
+ open(DATA, $filename) or return 0;
+ chomp(my @lines = <DATA>);
+ close(DATA);
+ return scalar(uniq @lines);
+}
+
+sub cert_ext_has_n_different_lines {
+ my $cert = shift @_;
+ my $expected = shift @_;
+ my $exts = shift @_;
+ my $name = shift @_;
+ my $out = "cert_n_different_exts.out";
+ run(app(["openssl", "x509", "-noout", "-ext", $exts,
+ "-in", $cert, "-out", $out]));
+ is(file_n_different_lines($out), $expected, ($name ? "$name: " : "").
+ "$cert '$exts' output should contain $expected different lines");
+ # not unlinking $out
+}
+
1;
diff --git a/util/libcrypto.num b/util/libcrypto.num
index aa35b4185c..2f3255303d 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5100,6 +5100,9 @@ X509_STORE_load_file_ex ? 3_0_0 EXIST::FUNCTION:
X509_STORE_load_store_ex ? 3_0_0 EXIST::FUNCTION:
X509_STORE_load_locations_ex ? 3_0_0 EXIST::FUNCTION:
X509_STORE_set_default_paths_ex ? 3_0_0 EXIST::FUNCTION:
+X509V3_set_issuer_pkey ? 3_0_0 EXIST::FUNCTION:
+i2s_ASN1_UTF8STRING ? 3_0_0 EXIST::FUNCTION:
+s2i_ASN1_UTF8STRING ? 3_0_0 EXIST::FUNCTION:
OSSL_STORE_open_ex ? 3_0_0 EXIST::FUNCTION:
OSSL_DECODER_fetch ? 3_0_0 EXIST::FUNCTION:
OSSL_DECODER_up_ref ? 3_0_0 EXIST::FUNCTION:
diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt
index a4da3bc3fb..8b3ebab119 100644
--- a/util/missingcrypto.txt
+++ b/util/missingcrypto.txt
@@ -1294,7 +1294,6 @@ X509V3_get_value_int(3)
X509V3_parse_list(3)
X509V3_section_free(3)
X509V3_set_conf_lhash(3)
-X509V3_set_ctx(3)
X509V3_set_nconf(3)
X509V3_string_free(3)
X509_ALGORS_it(3)
diff --git a/util/missingmacro.txt b/util/missingmacro.txt
index a24cb8a685..4cad414f3a 100644
--- a/util/missingmacro.txt
+++ b/util/missingmacro.txt
@@ -174,3 +174,4 @@ X509V3_set_ctx_test(3)
X509V3_set_ctx_nodb(3)
EXT_BITSTRING(3)
EXT_IA5STRING(3)
+EXT_UTF8STRING(3)