diff options
-rw-r--r-- | apps/req.c | 17 | ||||
-rw-r--r-- | apps/x509.c | 8 | ||||
-rw-r--r-- | crypto/x509/v3_akey.c | 42 | ||||
-rw-r--r-- | crypto/x509/v3_alt.c | 6 | ||||
-rw-r--r-- | crypto/x509/v3_conf.c | 34 | ||||
-rw-r--r-- | crypto/x509/v3_skey.c | 63 | ||||
-rw-r--r-- | crypto/x509/v3_utf8.c | 1 | ||||
-rw-r--r-- | doc/internal/man3/s2i_ASN1_UTF8STRING.pod | 46 | ||||
-rw-r--r-- | doc/man3/ASN1_generate_nconf.pod | 6 | ||||
-rw-r--r-- | doc/man3/X509V3_set_ctx.pod | 60 | ||||
-rw-r--r-- | doc/man3/s2i_ASN1_IA5STRING.pod | 21 | ||||
-rw-r--r-- | doc/man5/x509v3_config.pod | 14 | ||||
-rw-r--r-- | include/crypto/x509.h | 2 | ||||
-rw-r--r-- | include/crypto/x509v3.h | 23 | ||||
-rw-r--r-- | include/openssl/x509v3.h.in | 13 | ||||
-rw-r--r-- | test/recipes/25-test_req.t | 25 | ||||
-rw-r--r-- | test/recipes/tconversion.pl | 32 | ||||
-rw-r--r-- | util/libcrypto.num | 3 | ||||
-rw-r--r-- | util/missingcrypto.txt | 1 | ||||
-rw-r--r-- | util/missingmacro.txt | 1 |
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) |