Improved handling of AKID/SKID extensions in CSRs and certs

With the advent of the new AKID and SKID value syntax:

    - subjectKeyIdentifier = none
    - authorityKeyIdentifier = none

the resulting extensions are not directly suppressed, instead they
are parsed respectively as an empty OCTET STRING or an empty SEQUENCE.

There was new code in "apps/" to then drop the extension when signing
certificates, but this did not address applications that sign
certificates via the API, nor did it avoid adding these in CSRs.

We now drop these specific empty extensions from both certificates and
CSRs as part of X509v3_add_ext(), with the parsed extension never added
to the extension stack.

This means that in the "apps" the default "hash" or "keyid, issuer"
values must now be created first, with config settings and command-line
options applied after replacing the default as requested (including
"none" to suppress the extensions if desired).

If somehow an application manages to construct a TBS cert or CSR with a
pending empty SKID or AKID extension, an error is raised and the TBS is
not signed.

When no SKID is present in a self-signed cert, attempts to
force an AKID keyid need to fail, rather than produce an AKID
that will never match.

Similarly, when issuer cert == subject cert (same object in memory), but
the issuer key is not the subject key (forced signing key), there is no
way to infer the unknown issuer's name or serial number, so, with
"issuer:always" we must fail, rather than record the subject's own
issuer name and serial.

New "keyid:nonss" and "issuer:nonss" syntax is now supported, which
allows either part of the AKID to be conditional on the certificate not
being self-signed.  In the case if "issuer:nonss" this is also
conditional on there being no non-empty issuer keyid.  To force
inclusion of issuer+serial in the AKID use "issuer:always".

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Neil Horman <nhorman@openssl.org>
MergeDate: Sat Feb 28 15:22:47 2026
(Merged from https://github.com/openssl/openssl/pull/29057)
This commit is contained in:
Viktor Dukhovni committed 2026-03-01 02:17:57 +11:00
1 parent b678c18c9e
commit 84419e373a
15 files changed
+298 -111

No files matched your search

+5 -50
View File
@@ -2508,33 +2508,6 @@ int do_EXT_REQ_add_nconf(CONF *conf1, CONF *conf2, X509V3_CTX *ctx,
return 0;
}
static int adapt_keyid_ext(X509 *cert, X509V3_CTX *ext_ctx,
const char *name, const char *value, int add_default)
{
const STACK_OF(X509_EXTENSION) *exts = X509_get0_extensions(cert);
X509_EXTENSION *new_ext = X509V3_EXT_nconf(NULL, ext_ctx, name, value);
int idx, rv = 0;
if (new_ext == NULL)
return rv;
idx = X509v3_get_ext_by_OBJ(exts, X509_EXTENSION_get_object(new_ext), -1);
if (idx >= 0) {
const X509_EXTENSION *found_ext = X509v3_get_ext(exts, idx);
const ASN1_OCTET_STRING *encoded = X509_EXTENSION_get_data(found_ext);
int disabled = ASN1_STRING_length(encoded) <= 2; /* indicating "none" */
if (disabled) {
X509_EXTENSION_free(X509_delete_ext(cert, idx));
} /* else keep existing key identifier, which might be outdated */
rv = 1;
} else {
rv = !add_default || X509_add_ext(cert, new_ext, -1);
}
X509_EXTENSION_free(new_ext);
return rv;
}
int cert_matches_key(const X509 *cert, const EVP_PKEY *pkey)
{
int match;
@@ -2549,32 +2522,14 @@ int cert_matches_key(const X509 *cert, const EVP_PKEY *pkey)
int do_X509_sign(X509 *cert, int force_v1, EVP_PKEY *pkey, const char *md,
STACK_OF(OPENSSL_STRING) *sigopts, X509V3_CTX *ext_ctx)
{
EVP_MD_CTX *mctx = EVP_MD_CTX_new();
int self_sign;
EVP_MD_CTX *mctx;
int rv = 0;
if (!force_v1) {
if (!X509_set_version(cert, X509_VERSION_3))
goto end;
/*
* Add default SKID before AKID such that AKID can make use of it
* in case the certificate is self-signed
*/
/* Prevent X509_V_ERR_MISSING_SUBJECT_KEY_IDENTIFIER */
if (!adapt_keyid_ext(cert, ext_ctx, "subjectKeyIdentifier", "hash", 1))
goto end;
/* Prevent X509_V_ERR_MISSING_AUTHORITY_KEY_IDENTIFIER */
self_sign = cert_matches_key(cert, pkey);
if (!adapt_keyid_ext(cert, ext_ctx, "authorityKeyIdentifier",
"keyid, issuer", !self_sign))
goto end;
}
/* May add further measures for ensuring RFC 5280 compliance, see #19805 */
if (mctx != NULL && do_sign_init(mctx, pkey, md, sigopts) > 0)
if (!force_v1 && !X509_set_version(cert, X509_VERSION_3))
return 0;
if ((mctx = EVP_MD_CTX_new()) != NULL
&& do_sign_init(mctx, pkey, md, sigopts) > 0)
rv = (X509_sign_ctx(cert, mctx) > 0);
end:
EVP_MD_CTX_free(mctx);
return rv;
}
+1 -1
View File
@@ -593,7 +593,7 @@ int req_main(int argc, char **argv)
digest = p;
}
if (extsect == NULL)
if (extsect == NULL && !x509v1)
extsect = app_conf_try_string(req_conf, section,
gen_x509 ? V3_EXTENSIONS : REQ_EXTENSIONS);
if (extsect != NULL) {
+81 -17
View File
@@ -330,6 +330,40 @@ static int add_object(STACK_OF(ASN1_OBJECT) **sk, const char *name,
return 0;
}
static CONF *load_ext_conf(const char *file, const char *sect, int quiet)
{
BIO *bio = NULL;
CONF *conf;
if (file == NULL)
file = default_config_file;
if (file != NULL && *file != '\0')
bio = bio_open_default_quiet(file, 'r', FORMAT_TEXT);
else if (!quiet) {
BIO_puts(bio_err, "The -extfile option, or else the default "
"configuration filename must be nonempty");
return NULL;
}
if (bio == NULL) {
if (!quiet)
BIO_printf(bio_err, "Error opening: %s\n", file);
return NULL;
}
conf = NCONF_new_ex(app_get0_libctx(), NULL);
if (conf != NULL && NCONF_load_bio(conf, bio, NULL) <= 0) {
NCONF_free(conf);
conf = NULL;
}
BIO_free(bio);
if (conf != NULL)
return conf;
if (!quiet)
BIO_printf(bio_err, "Error loading configuration from: %s\n", file);
return conf;
}
int x509_main(int argc, char **argv)
{
ASN1_INTEGER *sno = NULL;
@@ -339,7 +373,7 @@ int x509_main(int argc, char **argv)
X509V3_CTX ext_ctx;
EVP_PKEY *privkey = NULL, *CAkey = NULL, *pubkey = NULL;
EVP_PKEY *pkey;
int newcert = 0;
int newcert = 0, newout = 0;
char *issu = NULL, *subj = NULL, *digest = NULL;
X509_NAME *fissu = NULL, *fsubj = NULL;
const unsigned long chtype = MBSTRING_ASC;
@@ -679,6 +713,29 @@ int x509_main(int argc, char **argv)
if (!opt_check_md(digest))
goto opthelp;
if (reqfile || newcert || privkey != NULL || CAfile != NULL)
newout = 1;
else if (sno != NULL
|| not_before != NULL
|| not_after != NULL
|| days != UNSET_DAYS
|| preserve_dates
|| issu != NULL
|| subj != NULL
|| pubkeyfile != NULL
|| clrext
|| extfile != NULL
|| extsect != NULL
|| sigopts != NULL) {
BIO_printf(bio_err,
"The %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s and %s "
"options are only valid when signing a new certificate\n",
"set_serial", "not_before", "not_after", "days",
"preserve_dates", "set_issuer", "set_subject", "subj",
"force_pubkey", "clrext", "extfile", "extensions", "sigopt");
goto err;
}
if (preserve_dates && not_before != NULL) {
BIO_puts(bio_err, "Cannot use -preserve_dates with -not_before option\n");
goto err;
@@ -766,24 +823,29 @@ int x509_main(int argc, char **argv)
WARN_NO_CA("-CAcreateserial");
}
if (extfile == NULL) {
if (extsect != NULL)
BIO_puts(bio_err,
"Warning: ignoring -extensions option without -extfile\n");
} else {
/*
* Failure to find or process the default config file is silent, when no
* `-extensions` was specified. Otherwise, the requested extensions
* section must be present.
*/
int confquiet = extsect == NULL && extfile == NULL;
if (newout)
extconf = load_ext_conf(extfile, extsect, confquiet);
if (extconf != NULL) {
X509V3_CTX ctx2;
if ((extconf = app_load_config(extfile)) == NULL)
goto err;
if (extsect == NULL) {
extsect = app_conf_try_string(extconf, "default", "extensions");
if (extsect == NULL)
if (extfile != NULL && extsect == NULL)
extsect = "default";
}
X509V3_set_ctx_test(&ctx2);
if (!do_EXT_add_nconf(extconf, extconf, &ctx2, NULL,
"Error checking extension section %s\n", extsect))
goto err;
} else if (newout && !confquiet) {
goto err;
}
if (multi && (reqfile || newcert)) {
@@ -947,10 +1009,11 @@ cert_loop:
if (!X509V3_set_issuer_pkey(&ext_ctx, privkey))
goto err;
}
if (extconf != NULL && !x509toreq
&& !do_EXT_add_nconf(extconf, extconf, &ext_ctx, x,
"Error adding extensions from section %s\n", extsect))
goto err;
if (extconf != NULL && !x509toreq) {
if (!do_EXT_add_nconf(extconf, extconf, &ext_ctx, x,
"Error adding extensions from section %s\n", extsect))
goto err;
}
/* At this point the contents of the certificate x have been finished. */
@@ -971,10 +1034,11 @@ cert_loop:
}
if ((rq = x509_to_req(x, ext_copy, ext_names)) == NULL)
goto err;
if (extconf != NULL
&& !do_EXT_REQ_add_nconf(extconf, extconf, &ext_ctx, rq,
"Error adding request extensions from section %s\n", extsect))
goto err;
if (extconf != NULL) {
if (!do_EXT_REQ_add_nconf(extconf, extconf, &ext_ctx, rq,
"Error adding request extensions from section %s\n", extsect))
goto err;
}
if (!do_X509_REQ_sign(rq, privkey, digest, sigopts))
goto err;
if (!noout) {
+1
View File
@@ -1852,6 +1852,7 @@ X509_R_IDP_MISMATCH:128:idp mismatch
X509_R_INVALID_ATTRIBUTES:138:invalid attributes
X509_R_INVALID_DIRECTORY:113:invalid directory
X509_R_INVALID_DISTPOINT:143:invalid distpoint
X509_R_INVALID_EXTENSION:146:invalid extension
X509_R_INVALID_FIELD_NAME:119:invalid field name
X509_R_INVALID_TRUST:123:invalid trust
X509_R_ISSUER_MISMATCH:129:issuer mismatch
+51 -26
View File
@@ -109,30 +109,32 @@ static AUTHORITY_KEYID *v2i_AUTHORITY_KEYID(X509V3_EXT_METHOD *method,
const X509_EXTENSION *ext;
X509 *issuer_cert;
int same_issuer, ss;
int nonss = 0, always = 0;
AUTHORITY_KEYID *akeyid = AUTHORITY_KEYID_new();
if (akeyid == NULL)
goto err;
if (n == 1 && strcmp(sk_CONF_VALUE_value(values, 0)->name, "none") == 0) {
if (n == 1 && strcmp(sk_CONF_VALUE_value(values, 0)->name, "none") == 0)
return akeyid;
}
for (i = 0; i < n; i++) {
cnf = sk_CONF_VALUE_value(values, i);
if (cnf->value != NULL && strcmp(cnf->value, "always") != 0) {
ERR_raise_data(ERR_LIB_X509V3, X509V3_R_UNKNOWN_OPTION,
"name=%s option=%s", cnf->name, cnf->value);
goto err;
if (cnf->value != NULL && *cnf->value != '\0') {
if (strcmp(cnf->value, "always") == 0) {
always = 1;
} else if (strcmp(cnf->value, "nonss") == 0) {
nonss = 1;
} else {
ERR_raise_data(ERR_LIB_X509V3, X509V3_R_UNKNOWN_OPTION,
"name=%s option=%s", cnf->name, cnf->value);
goto err;
}
}
if (strcmp(cnf->name, "keyid") == 0 && keyid == 0) {
keyid = 1;
if (cnf->value != NULL)
keyid = 2;
keyid = always ? 3 : (nonss ? 1 : 2);
} else if (strcmp(cnf->name, "issuer") == 0 && issuer == 0) {
issuer = 1;
if (cnf->value != NULL)
issuer = 2;
issuer = always ? 3 : (nonss ? 1 : 2);
} else if (strcmp(cnf->name, "none") == 0
|| strcmp(cnf->name, "keyid") == 0
|| strcmp(cnf->name, "issuer") == 0) {
@@ -165,43 +167,66 @@ static AUTHORITY_KEYID *v2i_AUTHORITY_KEYID(X509V3_EXT_METHOD *method,
ss = same_issuer;
ERR_pop_to_mark();
/* unless forced with "always", AKID is suppressed for self-signed certs */
if (keyid == 2 || (keyid == 1 && !ss)) {
if (keyid > 1 || (keyid == 1 && !ss)) {
/*
* prefer any pre-existing subject key identifier of the issuer cert
* except issuer cert is same as subject cert and is not self-signed
* The subject key identifier of the issuer cert is acceptable unless
* the issuer cert is same as subject cert, but the subject will not
* not be self-signed (i.e. will be signed with a different key).
*/
i = X509_get_ext_by_NID(issuer_cert, NID_subject_key_identifier, -1);
if (i >= 0 && (ext = X509_get_ext(issuer_cert, i)) != NULL
&& !(same_issuer && !ss)) {
ikeyid = X509V3_EXT_d2i(ext);
if (ASN1_STRING_length(ikeyid) == 0) /* indicating "none" */ {
/* Ignore empty keyids in the issuer cert */
if (ASN1_STRING_length(ikeyid) == 0) {
ASN1_OCTET_STRING_free(ikeyid);
ikeyid = NULL;
}
}
if (ikeyid == NULL && same_issuer && ctx->issuer_pkey != NULL) {
/* generate fallback AKID, emulating s2i_skey_id(..., "hash") */
/*
* If we have that other key in hand, synthesise a fallback AKID,
* emulating s2i_skey_id(..., "hash").
*
* When creating self-signed certificates, we do not synthesise
* best-effort keyids, instead the keyid needs be set first. The
* X509V3_EXT_add_nconf_sk() function makes every effort to process the
* SKID before the AKID, but some callers may specify extensions
* piecemeal. We don't want to mispredict it being set later and end
* up with a "dangling" AKID keyid.
*/
if (ikeyid == NULL && same_issuer && !ss && ctx->issuer_pkey != NULL) {
X509_PUBKEY *pubkey = NULL;
if (X509_PUBKEY_set(&pubkey, ctx->issuer_pkey))
ikeyid = ossl_x509_pubkey_hash(pubkey);
X509_PUBKEY_free(pubkey);
}
if (keyid == 2 && ikeyid == NULL) {
if (keyid == 3 && ikeyid == NULL) {
ERR_raise(ERR_LIB_X509V3, X509V3_R_UNABLE_TO_GET_ISSUER_KEYID);
goto err;
}
}
if (issuer == 2 || (issuer == 1 && !ss && 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;
/*
* When the same object is specified as both the issuer and subject
* certificate, but with a different (forced) issuer public key (so not
* self-signed), we don't have access to the true issuer certificate's
* serial number, so can't create an isser+serial AKID.
*/
if (!same_issuer || ss) {
if (issuer == 3 /* always */
|| (ikeyid == NULL
&& (issuer == 2 /* absent a keyid */
|| (issuer == 1 && !ss) /* unless self-signed */))) {
isname = X509_NAME_dup(X509_get_issuer_name(issuer_cert));
serial = ASN1_INTEGER_dup(X509_get0_serialNumber(issuer_cert));
}
}
/* "always" fails unless both issuer and serial are available */
if (issuer == 3 && (isname == NULL || serial == NULL)) {
ERR_raise(ERR_LIB_X509V3, X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS);
goto err;
}
if (isname != NULL) {
if ((gens = sk_GENERAL_NAME_new_null()) == NULL
+49 -2
View File
@@ -375,6 +375,37 @@ int X509V3_EXT_CRL_add_nconf(CONF *conf, X509V3_CTX *ctx, const char *section,
return X509V3_EXT_add_nconf_sk(conf, ctx, section, sk);
}
static int
update_req_extensions(X509_REQ *req, int *pnid, STACK_OF(X509_EXTENSION) *exts)
{
unsigned char *ext = NULL;
int ret = 0, loc = -1, extlen;
if (pnid == NULL || *pnid == NID_undef)
if ((pnid = X509_REQ_get_extension_nids()) == NULL)
return 0;
loc = X509at_get_attr_by_NID(req->req_info.attributes, *pnid, -1);
extlen = ASN1_item_i2d((const ASN1_VALUE *)exts,
&ext, ASN1_ITEM_rptr(X509_EXTENSIONS));
if (extlen <= 0)
return ret;
if (loc != -1) {
X509_ATTRIBUTE *att = X509at_delete_attr(req->req_info.attributes, loc);
if (att == NULL)
goto end;
X509at_delete_attr(req->req_info.attributes, loc);
X509_ATTRIBUTE_free(att);
}
ret = X509_REQ_add1_attr_by_NID(req, *pnid, V_ASN1_SEQUENCE, ext, extlen);
end:
OPENSSL_free(ext);
return ret;
}
/*
* Add extensions to certificate request. Just check in case req is NULL.
* Note that on error new elements may remain added to req if req != NULL.
@@ -383,10 +414,26 @@ int X509V3_EXT_REQ_add_nconf(CONF *conf, X509V3_CTX *ctx, const char *section,
X509_REQ *req)
{
STACK_OF(X509_EXTENSION) *exts = NULL;
int ret = X509V3_EXT_add_nconf_sk(conf, ctx, section, &exts);
int ret, *pnid = NULL;
/*
* Load current extensions if any, so we can replace any duplicates, possibly
* with nothing in the case of empty AKID/SKID.
*/
if (req != NULL) {
for (pnid = X509_REQ_get_extension_nids(); *pnid != NID_undef; pnid++) {
exts = ossl_x509_req_get1_extensions_by_nid(req, *pnid);
if (sk_X509_EXTENSION_num(exts) > 0)
break;
sk_X509_EXTENSION_free(exts);
exts = NULL;
}
}
ret = X509V3_EXT_add_nconf_sk(conf, ctx, section, &exts);
/* Replace original extension list (stack) with updated stack */
if (ret && req != NULL && exts != NULL)
ret = X509_REQ_add_extensions(req, exts);
ret = update_req_extensions(req, pnid, exts);
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
return ret;
}
+2 -1
View File
@@ -1,6 +1,6 @@
/*
* Generated by util/mkerr.pl DO NOT EDIT
* Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved.
* Copyright 1995-2025 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
@@ -39,6 +39,7 @@ static const ERR_STRING_DATA X509_str_reasons[] = {
"invalid attributes" },
{ ERR_PACK(ERR_LIB_X509, 0, X509_R_INVALID_DIRECTORY), "invalid directory" },
{ ERR_PACK(ERR_LIB_X509, 0, X509_R_INVALID_DISTPOINT), "invalid distpoint" },
{ ERR_PACK(ERR_LIB_X509, 0, X509_R_INVALID_EXTENSION), "invalid extension" },
{ ERR_PACK(ERR_LIB_X509, 0, X509_R_INVALID_FIELD_NAME),
"invalid field name" },
{ ERR_PACK(ERR_LIB_X509, 0, X509_R_INVALID_TRUST), "invalid trust" },
+25 -2
View File
@@ -102,12 +102,30 @@ const X509_EXTENSION *X509_get_ext(const X509 *x, int loc)
X509_EXTENSION *X509_delete_ext(X509 *x, int loc)
{
return delete_ext(&x->cert_info.extensions, loc);
X509_EXTENSION *ret;
if (x->cert_info.extensions == NULL)
return NULL;
if ((ret = delete_ext(&x->cert_info.extensions, loc)) != NULL)
x->cert_info.enc.modified = 1;
return ret;
}
int X509_add_ext(X509 *x, const X509_EXTENSION *ex, int loc)
{
return (X509v3_add_ext(&(x->cert_info.extensions), ex, loc) != NULL);
STACK_OF(X509_EXTENSION) *exts = x->cert_info.extensions;
if (X509v3_add_ext(&exts, ex, loc) == NULL)
return 0;
if (sk_X509_EXTENSION_num(exts) != 0) {
x->cert_info.extensions = exts;
} else {
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
sk_X509_EXTENSION_pop_free(x->cert_info.extensions, X509_EXTENSION_free);
x->cert_info.extensions = NULL;
}
x->cert_info.enc.modified = 1;
return 1;
}
void *X509_get_ext_d2i(const X509 *x, int nid, int *crit, int *idx)
@@ -118,6 +136,11 @@ void *X509_get_ext_d2i(const X509 *x, int nid, int *crit, int *idx)
int X509_add1_ext_i2d(X509 *x, int nid, void *value, int crit,
unsigned long flags)
{
/*
* Assume modified, sadly the underlying function does not tell us whether
* changes were made, or not.
*/
x->cert_info.enc.modified = 1;
return X509V3_add1_i2d(&x->cert_info.extensions, nid, value, crit,
flags);
}
+4 -4
View File
@@ -117,8 +117,8 @@ void X509_REQ_set_extension_nids(int *nids)
ext_nids = nids;
}
static STACK_OF(X509_EXTENSION) *get_extensions_by_nid(const X509_REQ *req,
int nid)
STACK_OF(X509_EXTENSION) *
ossl_x509_req_get1_extensions_by_nid(const X509_REQ *req, int nid)
{
X509_ATTRIBUTE *attr;
const ASN1_TYPE *ext = NULL;
@@ -147,7 +147,7 @@ STACK_OF(X509_EXTENSION) *X509_REQ_get_extensions(const X509_REQ *req)
if (req == NULL || ext_nids == NULL)
return NULL;
for (pnid = ext_nids; *pnid != NID_undef; pnid++) {
exts = get_extensions_by_nid(req, *pnid);
exts = ossl_x509_req_get1_extensions_by_nid(req, *pnid);
if (exts == NULL)
return NULL;
if (sk_X509_EXTENSION_num(exts) > 0)
@@ -176,7 +176,7 @@ int X509_REQ_add_extensions_nid(X509_REQ *req,
loc = X509at_get_attr_by_NID(req->req_info.attributes, nid, -1);
if (loc != -1) {
if ((mod_exts = get_extensions_by_nid(req, nid)) == NULL)
if ((mod_exts = ossl_x509_req_get1_extensions_by_nid(req, nid)) == NULL)
return 0;
if (X509v3_add_extensions(&mod_exts, exts) == NULL)
goto end;
+15
View File
@@ -120,6 +120,20 @@ STACK_OF(X509_EXTENSION) *X509v3_add_ext(STACK_OF(X509_EXTENSION) **x,
} else
sk = *x;
/*
* Empty OCTET STRINGs and empty SEQUENCEs encode to just two bytes of tag
* (0x04 or 0x30) and length (0x00).
*/
if (ex->value.length == 2
&& (ex->value.data[0] == 0x30 || ex->value.data[0] == 0x04)) {
ASN1_OBJECT *obj = ex->object;
ASN1_OBJECT *skid = OBJ_nid2obj(NID_subject_key_identifier);
ASN1_OBJECT *akid = OBJ_nid2obj(NID_authority_key_identifier);
if (OBJ_cmp(obj, skid) == 0 || OBJ_cmp(obj, akid) == 0)
goto done;
}
n = sk_X509_EXTENSION_num(sk);
if (loc > n)
loc = n;
@@ -134,6 +148,7 @@ STACK_OF(X509_EXTENSION) *X509v3_add_ext(STACK_OF(X509_EXTENSION) **x,
ERR_raise(ERR_LIB_X509, ERR_R_CRYPTO_LIB);
goto err;
}
done:
if (*x == NULL)
*x = sk;
return sk;
+55 -2
View File
@@ -25,6 +25,7 @@
#include <openssl/dsa.h>
#include <openssl/x509v3.h>
#include "internal/asn1.h"
#include "crypto/asn1.h"
#include "crypto/pkcs7.h"
#include "crypto/x509.h"
#include "crypto/x509_acert.h"
@@ -74,14 +75,46 @@ int NETSCAPE_SPKI_verify(NETSCAPE_SPKI *a, EVP_PKEY *r)
&a->sig_algor, a->signature, a->spkac, r);
}
/* Detect invalid empty SKID or AKID extensions. */
static int bad_keyid_exts(const STACK_OF(X509_EXTENSION) *exts)
{
int i, n = sk_X509_EXTENSION_num(exts);
for (i = 0; i < n; ++i) {
X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
const ASN1_STRING *der = X509_EXTENSION_get_data(ext);
/*
* Empty OCTET STRINGs and empty SEQUENCEs encode to just two bytes of
* tag (0x04 or 0x30) and length (0x00).
*/
if (der->length == 2 && (der->data[0] == 0x04 || der->data[0] == 0x30)) {
const ASN1_OBJECT *obj = X509_EXTENSION_get_object(ext);
const ASN1_OBJECT *skid = OBJ_nid2obj(NID_subject_key_identifier);
const ASN1_OBJECT *akid = OBJ_nid2obj(NID_authority_key_identifier);
if (OBJ_cmp(obj, skid) != 0 && OBJ_cmp(obj, akid) != 0)
continue;
ERR_raise_data(ERR_LIB_X509, X509_R_INVALID_EXTENSION,
"Invalid empty X.509 %s extension", obj->sn);
return 1;
}
}
return 0;
}
int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md)
{
const STACK_OF(X509_EXTENSION) *exts;
if (x == NULL) {
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))
if ((exts = X509_get0_extensions(x)) != NULL
&& sk_X509_EXTENSION_num(exts) > 0
&& (bad_keyid_exts(exts) || !X509_set_version(x, X509_VERSION_3)))
return 0;
/*
@@ -99,10 +132,14 @@ int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md)
int X509_sign_ctx(X509 *x, EVP_MD_CTX *ctx)
{
const STACK_OF(X509_EXTENSION) *exts;
if (x == NULL) {
ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
if ((exts = X509_get0_extensions(x)) != NULL && bad_keyid_exts(exts))
return 0;
if (sk_X509_EXTENSION_num(X509_get0_extensions(x)) > 0
&& !X509_set_version(x, X509_VERSION_3))
return 0;
@@ -139,10 +176,18 @@ X509 *X509_load_http(const char *url, BIO *bio, BIO *rbio, int timeout)
int X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md)
{
STACK_OF(X509_EXTENSION) *exts;
int bad = 0;
if (x == NULL) {
ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
if ((exts = ossl_x509_req_get1_extensions_by_nid(x, NID_ext_req)) != NULL)
bad = bad_keyid_exts(exts);
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
if (bad)
return 0;
x->req_info.enc.modified = 1;
return ASN1_item_sign_ex(ASN1_ITEM_rptr(X509_REQ_INFO), &x->sig_alg, NULL,
x->signature, &x->req_info, NULL,
@@ -151,10 +196,18 @@ int X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md)
int X509_REQ_sign_ctx(X509_REQ *x, EVP_MD_CTX *ctx)
{
STACK_OF(X509_EXTENSION) *exts;
int bad = 0;
if (x == NULL) {
ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
if ((exts = ossl_x509_req_get1_extensions_by_nid(x, NID_ext_req)) != NULL)
bad = bad_keyid_exts(exts);
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
if (bad)
return 0;
x->req_info.enc.modified = 1;
return ASN1_item_sign_ctx(ASN1_ITEM_rptr(X509_REQ_INFO),
&x->sig_alg, NULL, x->signature, &x->req_info,
+2
View File
@@ -331,6 +331,8 @@ int ossl_x509_add_cert_new(STACK_OF(X509) **sk, const X509 *cert, int flags);
int ossl_x509_add_certs_new(STACK_OF(X509) **p_sk, const STACK_OF(X509) *certs, int flags);
STACK_OF(X509_ATTRIBUTE) *ossl_x509at_dup(const STACK_OF(X509_ATTRIBUTE) *x);
STACK_OF(X509_EXTENSION) *
ossl_x509_req_get1_extensions_by_nid(const X509_REQ *req, int nid);
int ossl_x509_PUBKEY_get0_libctx(OSSL_LIB_CTX **plibctx, const char **ppropq,
const X509_PUBKEY *key);
+1 -1
View File
@@ -1,6 +1,6 @@
/*
* Generated by util/mkerr.pl DO NOT EDIT
* Copyright 2020-2022 The OpenSSL Project Authors. All Rights Reserved.
* Copyright 2020-2025 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
+2 -1
View File
@@ -1,6 +1,6 @@
/*
* Generated by util/mkerr.pl DO NOT EDIT
* Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved.
* Copyright 1995-2025 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
@@ -35,6 +35,7 @@
#define X509_R_INVALID_ATTRIBUTES 138
#define X509_R_INVALID_DIRECTORY 113
#define X509_R_INVALID_DISTPOINT 143
#define X509_R_INVALID_EXTENSION 146
#define X509_R_INVALID_FIELD_NAME 119
#define X509_R_INVALID_TRUST 123
#define X509_R_ISSUER_MISMATCH 129
+4 -4
View File
@@ -610,7 +610,7 @@ has_AKID($cert, 0); # forced no AKID
$cert = "self-signed_v3_CA_explicit_AKID.pem";
generate_cert($cert, @v3_ca, "-addext", "authorityKeyIdentifier = keyid");
has_AKID($cert, 0); # for self-signed cert, AKID suppressed and not forced
has_AKID($cert, 1);
$cert = "self-signed_v3_CA_forced_AKID.pem";
generate_cert($cert, @v3_ca, "-addext", "authorityKeyIdentifier = keyid:always");
@@ -619,7 +619,7 @@ strict_verify($cert, 1);
$cert = "self-signed_v3_CA_issuer_AKID.pem";
generate_cert($cert, @v3_ca, "-addext", "authorityKeyIdentifier = issuer");
has_AKID($cert, 0); # suppressed AKID since not forced
has_AKID($cert, 1);
$cert = "self-signed_v3_CA_forced_issuer_AKID.pem";
generate_cert($cert, @v3_ca, "-addext", "authorityKeyIdentifier = issuer:always");
@@ -627,11 +627,11 @@ cert_contains($cert, "Authority Key Identifier: DirName:/CN=CA serial:", 1); # f
$cert = "self-signed_v3_CA_nonforced_keyid_issuer_AKID.pem";
generate_cert($cert, @v3_ca, "-addext", "authorityKeyIdentifier = keyid, issuer");
has_AKID($cert, 0); # AKID not present because not forced and cert self-signed
has_AKID($cert, 1);
$cert = "self-signed_v3_CA_keyid_forced_issuer_AKID.pem";
generate_cert($cert, @v3_ca, "-addext", "authorityKeyIdentifier = keyid, issuer:always");
cert_contains($cert, "Authority Key Identifier: DirName:/CN=CA serial:", 1); # issuer AKID forced, with keyid not forced
cert_contains($cert, "Authority Key Identifier: keyid(:[0-9A-Fa-f]{2})+ DirName:/CN=CA serial:", 1);
$cert = "self-signed_v3_CA_forced_keyid_issuer_AKID.pem";
generate_cert($cert, @v3_ca, "-addext", "authorityKeyIdentifier = keyid:always, issuer");