From 66d4fbfc70ed6a00af36d50942baef3909234fd8 Mon Sep 17 00:00:00 2001 From: Timur Rakhmatullin <174210871+TimurRakhmatullin86@users.noreply.github.com> Date: Sun, 19 Jul 2026 20:54:28 -0700 Subject: [PATCH] core: reject a JWK entry missing x5c when loading a SPIFFE trust bundle SpiffeUtil.extractCert did `break` when a keys[] entry had no x5c, silently returning only the certificates gathered before it and truncating the trust bundle. Every other error path in the method throws IllegalArgumentException, and the method contract states that an invalid element makes the whole bundle invalid. Throw instead of silently dropping the remaining certificates. Signed-off-by: Timur Rakhmatullin <174210871+TimurRakhmatullin86@users.noreply.github.com> --- core/src/main/java/io/grpc/internal/SpiffeUtil.java | 3 ++- .../test/java/io/grpc/internal/SpiffeUtilTest.java | 5 +++++ .../io/grpc/internal/spiffebundle_missing_x5c.json | 12 ++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 core/src/test/resources/io/grpc/internal/spiffebundle_missing_x5c.json diff --git a/core/src/main/java/io/grpc/internal/SpiffeUtil.java b/core/src/main/java/io/grpc/internal/SpiffeUtil.java index 9eafc9950e2..21232ad293b 100644 --- a/core/src/main/java/io/grpc/internal/SpiffeUtil.java +++ b/core/src/main/java/io/grpc/internal/SpiffeUtil.java @@ -232,7 +232,8 @@ private static List extractCert(List> keysNode, checkJwkEntry(keyNode, trustDomainName); List rawCerts = JsonUtil.getListOfStrings(keyNode, "x5c"); if (rawCerts == null) { - break; + throw new IllegalArgumentException(String.format("'x5c' parameter is required. Certificate " + + "loading for trust domain '%s' failed.", trustDomainName)); } if (rawCerts.size() != 1) { throw new IllegalArgumentException(String.format("Exactly 1 certificate is expected, but " diff --git a/core/src/test/java/io/grpc/internal/SpiffeUtilTest.java b/core/src/test/java/io/grpc/internal/SpiffeUtilTest.java index 57824cf207f..8888046c586 100644 --- a/core/src/test/java/io/grpc/internal/SpiffeUtilTest.java +++ b/core/src/test/java/io/grpc/internal/SpiffeUtilTest.java @@ -230,6 +230,7 @@ public static class CertificateApiTest { private static final String SPIFFE_TRUST_BUNDLE_DUPLICATES = "spiffebundle_duplicates.json"; private static final String SPIFFE_TRUST_BUNDLE_WRONG_ROOT = "spiffebundle_wrong_root.json"; private static final String SPIFFE_TRUST_BUNDLE_WRONG_SEQ = "spiffebundle_wrong_seq_type.json"; + private static final String SPIFFE_TRUST_BUNDLE_MISSING_X5C = "spiffebundle_missing_x5c.json"; private static final String DOMAIN_ERROR_MESSAGE = " Certificate loading for trust domain 'google.com' failed."; @@ -351,6 +352,10 @@ public void loadTrustBundleFromFileFailureTest() { iae = assertThrows(IllegalArgumentException.class, () -> SpiffeUtil .loadTrustBundleFromFile(copyFileToTmp(SPIFFE_TRUST_BUNDLE_CORRUPTED_CERT))); assertEquals("Certificate can't be parsed." + DOMAIN_ERROR_MESSAGE, iae.getMessage()); + // Check the exception if a key entry is missing the 'x5c' parameter + iae = assertThrows(IllegalArgumentException.class, () -> SpiffeUtil + .loadTrustBundleFromFile(copyFileToTmp(SPIFFE_TRUST_BUNDLE_MISSING_X5C))); + assertEquals("'x5c' parameter is required." + DOMAIN_ERROR_MESSAGE, iae.getMessage()); // Check the exception if 'kty' value differs from 'RSA' iae = assertThrows(IllegalArgumentException.class, () -> SpiffeUtil .loadTrustBundleFromFile(copyFileToTmp(SPIFFE_TRUST_BUNDLE_WRONG_KTY))); diff --git a/core/src/test/resources/io/grpc/internal/spiffebundle_missing_x5c.json b/core/src/test/resources/io/grpc/internal/spiffebundle_missing_x5c.json new file mode 100644 index 00000000000..53883a7bd67 --- /dev/null +++ b/core/src/test/resources/io/grpc/internal/spiffebundle_missing_x5c.json @@ -0,0 +1,12 @@ +{ + "trust_domains": { + "google.com": { + "keys": [ + { + "kty": "RSA", + "use": "x509-svid" + } + ] + } + } +}