diff mbox series

keys: X.509 public key issuer lookup without AKID

Message ID 20210113142340.1963770-1-andrew.zaborowski@intel.com (mailing list archive)
State Superseded
Headers show
Series keys: X.509 public key issuer lookup without AKID | expand

Commit Message

Andrew Zaborowski Jan. 13, 2021, 2:23 p.m. UTC
There are non-root X.509 v3 certificates in use there that contain no
Authority Key Identifier extension (RFC5280 section 4.2.1.1) which the
kernel asymmetric key code relies on for any lookup and verification.
Such certificates are generated by openssl if you don't explicitly tell
it to include the extension.

Looking at RFC5280 4.2.1.1 it says:

"This extension is used where an issuer has multiple signing keys [...]"

making it sound like the extension is optional if the issuer uses only
one signing key.  It also says:

"The keyIdentifier field of the authorityKeyIdentifier extension MUST
be included in all certificates [...]"

now making it sound like it's mandatory for everyone.  It doesn't say it
as explicitly as it does for the Subject Key Identifier extension though
(4.2.1.2).  Openssl offers no code comments explaining why neither of
the two is included by default.  It marks a certificate as V3 as soon as
*any* extension is included.

In this patch I add lookups by just the Distinguished Names in the
certificate to handle this, I believe this is what (2) in the struct
asymmetric_key_id (include/keys/asymmetric-type.h) talks about.  I add a
third key ID in asymmetric_key_ids and I attempt to keep the logic
intact whenever either of the first two IDs is present in the subject
key (the ones derived from the AKID in the case of the the x509 public
key subtype.)

Lookups are still unambiguous provided that the CAs respect the condition
that the AKID may only be omitted if they use a single signing key.
I didn't apply the third ID logic to the certificate chain veritification
in pkcs7_verify.c, the AKID extensions is still required there.

Signed-off-by: Andrew Zaborowski <andrew.zaborowski@intel.com>
---
 crypto/asymmetric_keys/asymmetric_type.c  | 56 +++++++++++++++++------
 crypto/asymmetric_keys/pkcs7_trust.c      |  6 +--
 crypto/asymmetric_keys/restrict.c         | 48 +++++++++++--------
 crypto/asymmetric_keys/x509_cert_parser.c | 10 ++++
 crypto/asymmetric_keys/x509_public_key.c  | 10 ++++
 include/crypto/public_key.h               |  2 +-
 include/keys/asymmetric-type.h            |  3 +-
 7 files changed, 98 insertions(+), 37 deletions(-)

Comments

Jarkko Sakkinen Jan. 14, 2021, 3:23 a.m. UTC | #1
On Wed, Jan 13, 2021 at 03:23:40PM +0100, Andrew Zaborowski wrote:
> There are non-root X.509 v3 certificates in use there that contain no
> Authority Key Identifier extension (RFC5280 section 4.2.1.1) which the
> kernel asymmetric key code relies on for any lookup and verification.
> Such certificates are generated by openssl if you don't explicitly tell
> it to include the extension.
> 
> Looking at RFC5280 4.2.1.1 it says:
> 
> "This extension is used where an issuer has multiple signing keys [...]"
> 
> making it sound like the extension is optional if the issuer uses only
> one signing key.  It also says:
> 
> "The keyIdentifier field of the authorityKeyIdentifier extension MUST
> be included in all certificates [...]"
> 
> now making it sound like it's mandatory for everyone.  It doesn't say it
> as explicitly as it does for the Subject Key Identifier extension though
> (4.2.1.2).  Openssl offers no code comments explaining why neither of
> the two is included by default.  It marks a certificate as V3 as soon as
> *any* extension is included.
> 
> In this patch I add lookups by just the Distinguished Names in the
> certificate to handle this, I believe this is what (2) in the struct
> asymmetric_key_id (include/keys/asymmetric-type.h) talks about.  I add a
> third key ID in asymmetric_key_ids and I attempt to keep the logic
> intact whenever either of the first two IDs is present in the subject
> key (the ones derived from the AKID in the case of the the x509 public
> key subtype.)
> 
> Lookups are still unambiguous provided that the CAs respect the condition
> that the AKID may only be omitted if they use a single signing key.
> I didn't apply the third ID logic to the certificate chain veritification
> in pkcs7_verify.c, the AKID extensions is still required there.
> 
> Signed-off-by: Andrew Zaborowski <andrew.zaborowski@intel.com>

David, Konstantin, any ideas why the patchwork is jammed, and how
to get it up and running again.

https://patchwork.kernel.org/project/keyrings/list/

I mainly would like to have it working because of git-pw, which is
really nice tool for applying patches for testing.

/Jarkko
Jarkko Sakkinen Jan. 14, 2021, 3:47 a.m. UTC | #2
On Wed, Jan 13, 2021 at 03:23:40PM +0100, Andrew Zaborowski wrote:
> There are non-root X.509 v3 certificates in use there that contain no
> Authority Key Identifier extension (RFC5280 section 4.2.1.1) which the
> kernel asymmetric key code relies on for any lookup and verification.
> Such certificates are generated by openssl if you don't explicitly tell
> it to include the extension.
> 
> Looking at RFC5280 4.2.1.1 it says:
> 
> "This extension is used where an issuer has multiple signing keys [...]"
> 
> making it sound like the extension is optional if the issuer uses only
> one signing key.  It also says:
> 
> "The keyIdentifier field of the authorityKeyIdentifier extension MUST
> be included in all certificates [...]"
> 
> now making it sound like it's mandatory for everyone.  It doesn't say it
> as explicitly as it does for the Subject Key Identifier extension though
> (4.2.1.2).  Openssl offers no code comments explaining why neither of
> the two is included by default.  It marks a certificate as V3 as soon as
> *any* extension is included.
> 
> In this patch I add lookups by just the Distinguished Names in the
> certificate to handle this, I believe this is what (2) in the struct
> asymmetric_key_id (include/keys/asymmetric-type.h) talks about.  I add a
> third key ID in asymmetric_key_ids and I attempt to keep the logic
> intact whenever either of the first two IDs is present in the subject
> key (the ones derived from the AKID in the case of the the x509 public
> key subtype.)

Please use imperative form ("Add" instead of "I add").

> Lookups are still unambiguous provided that the CAs respect the condition
> that the AKID may only be omitted if they use a single signing key.
> I didn't apply the third ID logic to the certificate chain veritification
> in pkcs7_verify.c, the AKID extensions is still required there.
> 
> Signed-off-by: Andrew Zaborowski <andrew.zaborowski@intel.com>

"I'm not sure if these certificates are generated by
openssl with a special setting or by some other software."

I'll give you a test case:

https://letsencrypt.org/docs/certificates-for-localhost/

As can been seen:

$ cat > get_x509_extensions.py 
from OpenSSL import crypto
import sys
cert = crypto.load_certificate(crypto.FILETYPE_PEM, open(sys.argv[1]).read())
for i in range(0, cert.get_extension_count()):
    ext = cert.get_extension(i)
    print(ext.get_short_name().decode('utf-8'))

$ python3 get_x509_extensions.py localhost.crt
subjectAltName
keyUsage
extendedKeyUsage

You could remove the discussion about whether specs allow this
or not, and just use this certificate to demonstrate it. And
focus only on details how you've implemented your solution.

Just random example: the commit message lacks an explanation
how you've changed ID matching.

/Jarkko
Jarkko Sakkinen Jan. 14, 2021, 3:48 a.m. UTC | #3
On Thu, Jan 14, 2021 at 05:47:51AM +0200, Jarkko Sakkinen wrote:
> On Wed, Jan 13, 2021 at 03:23:40PM +0100, Andrew Zaborowski wrote:
> > There are non-root X.509 v3 certificates in use there that contain no
> > Authority Key Identifier extension (RFC5280 section 4.2.1.1) which the
> > kernel asymmetric key code relies on for any lookup and verification.
> > Such certificates are generated by openssl if you don't explicitly tell
> > it to include the extension.
> > 
> > Looking at RFC5280 4.2.1.1 it says:
> > 
> > "This extension is used where an issuer has multiple signing keys [...]"
> > 
> > making it sound like the extension is optional if the issuer uses only
> > one signing key.  It also says:
> > 
> > "The keyIdentifier field of the authorityKeyIdentifier extension MUST
> > be included in all certificates [...]"
> > 
> > now making it sound like it's mandatory for everyone.  It doesn't say it
> > as explicitly as it does for the Subject Key Identifier extension though
> > (4.2.1.2).  Openssl offers no code comments explaining why neither of
> > the two is included by default.  It marks a certificate as V3 as soon as
> > *any* extension is included.
> > 
> > In this patch I add lookups by just the Distinguished Names in the
> > certificate to handle this, I believe this is what (2) in the struct
> > asymmetric_key_id (include/keys/asymmetric-type.h) talks about.  I add a
> > third key ID in asymmetric_key_ids and I attempt to keep the logic
> > intact whenever either of the first two IDs is present in the subject
> > key (the ones derived from the AKID in the case of the the x509 public
> > key subtype.)
> 
> Please use imperative form ("Add" instead of "I add").
> 
> > Lookups are still unambiguous provided that the CAs respect the condition
> > that the AKID may only be omitted if they use a single signing key.
> > I didn't apply the third ID logic to the certificate chain veritification
> > in pkcs7_verify.c, the AKID extensions is still required there.
> > 
> > Signed-off-by: Andrew Zaborowski <andrew.zaborowski@intel.com>
> 
> "I'm not sure if these certificates are generated by
> openssl with a special setting or by some other software."
> 
> I'll give you a test case:
> 
> https://letsencrypt.org/docs/certificates-for-localhost/
> 
> As can been seen:
> 
> $ cat > get_x509_extensions.py 
> from OpenSSL import crypto
> import sys
> cert = crypto.load_certificate(crypto.FILETYPE_PEM, open(sys.argv[1]).read())
> for i in range(0, cert.get_extension_count()):
>     ext = cert.get_extension(i)
>     print(ext.get_short_name().decode('utf-8'))
> 
> $ python3 get_x509_extensions.py localhost.crt
> subjectAltName
> keyUsage
> extendedKeyUsage
> 
> You could remove the discussion about whether specs allow this
> or not, and just use this certificate to demonstrate it. And
> focus only on details how you've implemented your solution.
> 
> Just random example: the commit message lacks an explanation
> how you've changed ID matching.

I.e. you do make a case why something needs to be done, but you
are lacking details on why this is the right way to sort it out.

/Jarkko
Jarkko Sakkinen Jan. 15, 2021, 9:29 a.m. UTC | #4
On Thu, Jan 14, 2021 at 11:18:34AM -0500, Konstantin Ryabitsev wrote:
> On Thu, Jan 14, 2021 at 05:23:06AM +0200, Jarkko Sakkinen wrote:
> > David, Konstantin, any ideas why the patchwork is jammed, and how
> > to get it up and running again.
> > 
> > https://patchwork.kernel.org/project/keyrings/list/
> 
> Ah, sorry about that -- it got messed up during migration last month. The
> good news is that it's easy to backfill, so you should have all the missing
> patches there now.
> 
> > I mainly would like to have it working because of git-pw, which is
> > really nice tool for applying patches for testing.
> 
> Try b4, too. :)
> 
> -K

Thanks! Works now as expected.

/Jarkko
diff mbox series

Patch

diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
index 33e77d846ca..de2b21d9557 100644
--- a/crypto/asymmetric_keys/asymmetric_type.c
+++ b/crypto/asymmetric_keys/asymmetric_type.c
@@ -35,8 +35,9 @@  static DECLARE_RWSEM(asymmetric_key_parsers_sem);
 /**
  * find_asymmetric_key - Find a key by ID.
  * @keyring: The keys to search.
- * @id_0: The first ID to look for or NULL.
- * @id_1: The second ID to look for or NULL.
+ * @id_0: The primary ID to look for or NULL.
+ * @id_1: The first fallback ID to look for or NULL.
+ * @id_2: The second fallback ID to look for or NULL.
  * @partial: Use partial match if true, exact if false.
  *
  * Find a key in the given keyring by identifier.  The preferred identifier is
@@ -46,6 +47,7 @@  static DECLARE_RWSEM(asymmetric_key_parsers_sem);
 struct key *find_asymmetric_key(struct key *keyring,
 				const struct asymmetric_key_id *id_0,
 				const struct asymmetric_key_id *id_1,
+				const struct asymmetric_key_id *id_2,
 				bool partial)
 {
 	struct key *key;
@@ -54,22 +56,30 @@  struct key *find_asymmetric_key(struct key *keyring,
 	char *req, *p;
 	int len;
 
-	BUG_ON(!id_0 && !id_1);
+	BUG_ON(!id_0 && !id_1 && !id_2);
 
 	if (id_0) {
 		lookup = id_0->data;
 		len = id_0->len;
-	} else {
+	} else if (id_1) {
 		lookup = id_1->data;
 		len = id_1->len;
+	} else {
+		lookup = id_2->data;
+		len = id_2->len;
 	}
 
 	/* Construct an identifier "id:<keyid>". */
-	p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL);
+	p = req = kmalloc(4 + 1 + len * 2 + 1, GFP_KERNEL);
 	if (!req)
 		return ERR_PTR(-ENOMEM);
 
-	if (partial) {
+	if (!id_0 && !id_1) {
+		*p++ = 'n';
+		*p++ = 'a';
+		*p++ = 'm';
+		*p++ = 'e';
+	} else if (partial) {
 		*p++ = 'i';
 		*p++ = 'd';
 	} else {
@@ -183,8 +193,8 @@  bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1,
 EXPORT_SYMBOL_GPL(asymmetric_key_id_partial);
 
 /**
- * asymmetric_match_key_ids - Search asymmetric key IDs
- * @kids: The list of key IDs to check
+ * asymmetric_match_key_ids - Search asymmetric key IDs 1 & 2
+ * @kids: The pair of key IDs to check
  * @match_id: The key ID we're looking for
  * @match: The match function to use
  */
@@ -198,7 +208,7 @@  static bool asymmetric_match_key_ids(
 
 	if (!kids || !match_id)
 		return false;
-	for (i = 0; i < ARRAY_SIZE(kids->id); i++)
+	for (i = 0; i < 2; i++)
 		if (match(kids->id[i], match_id))
 			return true;
 	return false;
@@ -242,7 +252,7 @@  struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id)
 }
 
 /*
- * Match asymmetric keys by an exact match on an ID.
+ * Match asymmetric keys by an exact match on one of the first two IDs.
  */
 static bool asymmetric_key_cmp(const struct key *key,
 			       const struct key_match_data *match_data)
@@ -255,7 +265,7 @@  static bool asymmetric_key_cmp(const struct key *key,
 }
 
 /*
- * Match asymmetric keys by a partial match on an IDs.
+ * Match asymmetric keys by a partial match on one of the first two IDs.
  */
 static bool asymmetric_key_cmp_partial(const struct key *key,
 				       const struct key_match_data *match_data)
@@ -267,6 +277,18 @@  static bool asymmetric_key_cmp_partial(const struct key *key,
 					asymmetric_key_id_partial);
 }
 
+/*
+ * Match asymmetric keys by an exact match on the third IDs.
+ */
+static bool asymmetric_key_cmp_name(const struct key *key,
+				    const struct key_match_data *match_data)
+{
+	const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
+	const struct asymmetric_key_id *match_id = match_data->preparsed;
+
+	return kids && asymmetric_key_id_same(kids->id[2], match_id);
+}
+
 /*
  * Preparse the match criterion.  If we don't set lookup_type and cmp,
  * the default will be an exact match on the key description.
@@ -274,8 +296,9 @@  static bool asymmetric_key_cmp_partial(const struct key *key,
  * There are some specifiers for matching key IDs rather than by the key
  * description:
  *
- *	"id:<id>" - find a key by partial match on any available ID
- *	"ex:<id>" - find a key by exact match on any available ID
+ *	"id:<id>" - find a key by partial match on one of the first two IDs
+ *	"ex:<id>" - find a key by exact match on one of the first two IDs
+ *	"name:<id>" - find a key by exact match on the third ID
  *
  * These have to be searched by iteration rather than by direct lookup because
  * the key is hashed according to its description.
@@ -299,6 +322,13 @@  static int asymmetric_key_match_preparse(struct key_match_data *match_data)
 		   spec[1] == 'x' &&
 		   spec[2] == ':') {
 		id = spec + 3;
+	} else if (spec[0] == 'n' &&
+		   spec[1] == 'a' &&
+		   spec[2] == 'm' &&
+		   spec[3] == 'e' &&
+		   spec[4] == ':') {
+		id = spec + 5;
+		cmp = asymmetric_key_cmp_name;
 	} else {
 		goto default_match;
 	}
diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c
index 61af3c4d82c..998ba0e2ffb 100644
--- a/crypto/asymmetric_keys/pkcs7_trust.c
+++ b/crypto/asymmetric_keys/pkcs7_trust.c
@@ -48,7 +48,7 @@  static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
 		 * keys.
 		 */
 		key = find_asymmetric_key(trust_keyring,
-					  x509->id, x509->skid, false);
+					  x509->id, x509->skid, NULL, false);
 		if (!IS_ERR(key)) {
 			/* One of the X.509 certificates in the PKCS#7 message
 			 * is apparently the same as one we already trust.
@@ -82,7 +82,7 @@  static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
 		key = find_asymmetric_key(trust_keyring,
 					  last->sig->auth_ids[0],
 					  last->sig->auth_ids[1],
-					  false);
+					  NULL, false);
 		if (!IS_ERR(key)) {
 			x509 = last;
 			pr_devel("sinfo %u: Root cert %u signer is key %x\n",
@@ -97,7 +97,7 @@  static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
 	 * the signed info directly.
 	 */
 	key = find_asymmetric_key(trust_keyring,
-				  sinfo->sig->auth_ids[0], NULL, false);
+				  sinfo->sig->auth_ids[0], NULL, NULL, false);
 	if (!IS_ERR(key)) {
 		pr_devel("sinfo %u: Direct signer is key %x\n",
 			 sinfo->index, key_serial(key));
diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c
index 77ebebada29..f77875ec942 100644
--- a/crypto/asymmetric_keys/restrict.c
+++ b/crypto/asymmetric_keys/restrict.c
@@ -87,7 +87,7 @@  int restrict_link_by_signature(struct key *dest_keyring,
 	sig = payload->data[asym_auth];
 	if (!sig)
 		return -ENOPKG;
-	if (!sig->auth_ids[0] && !sig->auth_ids[1])
+	if (!sig->auth_ids[0] && !sig->auth_ids[1] && !sig->auth_ids[2])
 		return -ENOKEY;
 
 	if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid))
@@ -96,7 +96,7 @@  int restrict_link_by_signature(struct key *dest_keyring,
 	/* See if we have a key that signed this one. */
 	key = find_asymmetric_key(trust_keyring,
 				  sig->auth_ids[0], sig->auth_ids[1],
-				  false);
+				  sig->auth_ids[2], false);
 	if (IS_ERR(key))
 		return -ENOKEY;
 
@@ -108,11 +108,11 @@  int restrict_link_by_signature(struct key *dest_keyring,
 	return ret;
 }
 
-static bool match_either_id(const struct asymmetric_key_ids *pair,
+static bool match_either_id(const struct asymmetric_key_id **pair,
 			    const struct asymmetric_key_id *single)
 {
-	return (asymmetric_key_id_same(pair->id[0], single) ||
-		asymmetric_key_id_same(pair->id[1], single));
+	return (asymmetric_key_id_same(pair[0], single) ||
+		asymmetric_key_id_same(pair[1], single));
 }
 
 static int key_or_keyring_common(struct key *dest_keyring,
@@ -140,20 +140,22 @@  static int key_or_keyring_common(struct key *dest_keyring,
 	sig = payload->data[asym_auth];
 	if (!sig)
 		return -ENOPKG;
-	if (!sig->auth_ids[0] && !sig->auth_ids[1])
+	if (!sig->auth_ids[0] && !sig->auth_ids[1] && !sig->auth_ids[2])
 		return -ENOKEY;
 
 	if (trusted) {
 		if (trusted->type == &key_type_keyring) {
 			/* See if we have a key that signed this one. */
 			key = find_asymmetric_key(trusted, sig->auth_ids[0],
-						  sig->auth_ids[1], false);
+						  sig->auth_ids[1],
+						  sig->auth_ids[2], false);
 			if (IS_ERR(key))
 				key = NULL;
 		} else if (trusted->type == &key_type_asymmetric) {
-			const struct asymmetric_key_ids *signer_ids;
+			const struct asymmetric_key_id **signer_ids;
 
-			signer_ids = asymmetric_key_ids(trusted);
+			signer_ids = (const struct asymmetric_key_id **)
+				asymmetric_key_ids(trusted)->id;
 
 			/*
 			 * The auth_ids come from the candidate key (the
@@ -164,22 +166,29 @@  static int key_or_keyring_common(struct key *dest_keyring,
 			 * The signer_ids are identifiers for the
 			 * signing key specified for dest_keyring.
 			 *
-			 * The first auth_id is the preferred id, and
-			 * the second is the fallback. If only one
-			 * auth_id is present, it may match against
-			 * either signer_id. If two auth_ids are
-			 * present, the first auth_id must match one
-			 * signer_id and the second auth_id must match
-			 * the second signer_id.
+			 * The first auth_id is the preferred id, 2nd and
+			 * 3rd are the fallbacks. If excatly one of
+			 * auth_ids[0] and auth_ids[1] is present, it may
+			 * match either signer_ids[0] or signed_ids[1].
+			 * If both are present the first one may match
+			 * either signed_id but the second one must match
+			 * the second signer_id. If neither of them is
+			 * available, auth_ids[2] is matched against
+			 * signer_ids[2] as a fallback.
 			 */
-			if (!sig->auth_ids[0] || !sig->auth_ids[1]) {
+			if (!sig->auth_ids[0] && !sig->auth_ids[1]) {
+				if (asymmetric_key_id_same(signer_ids[2],
+							   sig->auth_ids[2]))
+					key = __key_get(trusted);
+
+			} else if (!sig->auth_ids[0] || !sig->auth_ids[1]) {
 				const struct asymmetric_key_id *auth_id;
 
 				auth_id = sig->auth_ids[0] ?: sig->auth_ids[1];
 				if (match_either_id(signer_ids, auth_id))
 					key = __key_get(trusted);
 
-			} else if (asymmetric_key_id_same(signer_ids->id[1],
+			} else if (asymmetric_key_id_same(signer_ids[1],
 							  sig->auth_ids[1]) &&
 				   match_either_id(signer_ids,
 						   sig->auth_ids[0])) {
@@ -193,7 +202,8 @@  static int key_or_keyring_common(struct key *dest_keyring,
 	if (check_dest && !key) {
 		/* See if the destination has a key that signed this one. */
 		key = find_asymmetric_key(dest_keyring, sig->auth_ids[0],
-					  sig->auth_ids[1], false);
+					  sig->auth_ids[1], sig->auth_ids[2],
+					  false);
 		if (IS_ERR(key))
 			key = NULL;
 	}
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 52c9b455fc7..59dfffa77cf 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -415,8 +415,18 @@  int x509_note_issuer(void *context, size_t hdrlen,
 		     const void *value, size_t vlen)
 {
 	struct x509_parse_context *ctx = context;
+	struct asymmetric_key_id *kid;
+
 	ctx->cert->raw_issuer = value;
 	ctx->cert->raw_issuer_size = vlen;
+
+	if (!ctx->cert->sig->auth_ids[2]) {
+		kid = asymmetric_key_generate_id(value, vlen, "", 0);
+		if (IS_ERR(kid))
+			return PTR_ERR(kid);
+		ctx->cert->sig->auth_ids[2] = kid;
+	}
+
 	return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen);
 }
 
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
index ae450eb8be1..a95cc351518 100644
--- a/crypto/asymmetric_keys/x509_public_key.c
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -221,6 +221,13 @@  static int x509_key_preparse(struct key_preparsed_payload *prep)
 		goto error_free_desc;
 	kids->id[0] = cert->id;
 	kids->id[1] = cert->skid;
+	kids->id[2] = asymmetric_key_generate_id(cert->raw_subject,
+						 cert->raw_subject_size,
+						 "", 0);
+	if (IS_ERR(kids->id[2])) {
+		ret = PTR_ERR(kids->id[2]);
+		goto error_free_kids;
+	}
 
 	/* We're pinning the module by being linked against it */
 	__module_get(public_key_subtype.owner);
@@ -237,8 +244,11 @@  static int x509_key_preparse(struct key_preparsed_payload *prep)
 	cert->skid = NULL;
 	cert->sig = NULL;
 	desc = NULL;
+	kids = NULL;
 	ret = 0;
 
+error_free_kids:
+	kfree(kids);
 error_free_desc:
 	kfree(desc);
 error_free_cert:
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index 948c5203ca9..4819e63a772 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -37,7 +37,7 @@  extern void public_key_free(struct public_key *key);
  * Public key cryptography signature data
  */
 struct public_key_signature {
-	struct asymmetric_key_id *auth_ids[2];
+	struct asymmetric_key_id *auth_ids[3];
 	u8 *s;			/* Signature */
 	u32 s_size;		/* Number of bytes in signature */
 	u8 *digest;
diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h
index a29d3ff2e7e..344ab9f178d 100644
--- a/include/keys/asymmetric-type.h
+++ b/include/keys/asymmetric-type.h
@@ -53,7 +53,7 @@  struct asymmetric_key_id {
 };
 
 struct asymmetric_key_ids {
-	void		*id[2];
+	void		*id[3];
 };
 
 extern bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1,
@@ -75,6 +75,7 @@  const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key)
 extern struct key *find_asymmetric_key(struct key *keyring,
 				       const struct asymmetric_key_id *id_0,
 				       const struct asymmetric_key_id *id_1,
+				       const struct asymmetric_key_id *id_2,
 				       bool partial);
 
 /*