From c3431edbb26ea2dbeee937c6e3f34d9a0559704e Mon Sep 17 00:00:00 2001 From: Max Lambrecht Date: Wed, 18 Mar 2026 13:02:44 -0500 Subject: [PATCH] fix(jwt-bundle): honor jwt-svid use when loading authorities Signed-off-by: Max Lambrecht --- .../io/spiffe/bundle/jwtbundle/JwtBundle.java | 14 ++++++++++++++ .../spiffe/bundle/jwtbundle/JwtBundleTest.java | 18 ++++++++++-------- .../testdata/jwtbundle/jwks_missing_kid.json | 1 + .../testdata/jwtbundle/jwks_valid_EC_1.json | 1 + .../testdata/jwtbundle/jwks_valid_RSA_1.json | 2 +- .../testdata/jwtbundle/jwks_valid_RSA_EC.json | 3 ++- 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/java-spiffe-core/src/main/java/io/spiffe/bundle/jwtbundle/JwtBundle.java b/java-spiffe-core/src/main/java/io/spiffe/bundle/jwtbundle/JwtBundle.java index 7af75780..019d155a 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/bundle/jwtbundle/JwtBundle.java +++ b/java-spiffe-core/src/main/java/io/spiffe/bundle/jwtbundle/JwtBundle.java @@ -194,6 +194,14 @@ private static JwtBundle toJwtBundle(TrustDomain trustDomain, final Map authorities = new ConcurrentHashMap<>(); for (JWK jwk : jwkSet.getKeys()) { + String use = getUse(jwk); + + // Only JWT-SVID signing keys are considered JWT authorities. + // JWK entries without a supported or known use are ignored. + if (!"jwt-svid".equals(use)) { + continue; + } + String keyId = getKeyId(jwk); PublicKey publicKey = getPublicKey(jwk); authorities.put(keyId, publicKey); @@ -210,6 +218,12 @@ private static String getKeyId(final JWK jwk) throws JwtBundleException { return keyId; } + private static String getUse(final JWK jwk) { + Objects.requireNonNull(jwk, "jwk must not be null"); + Object use = jwk.toJSONObject().get("use"); + return use != null ? use.toString() : null; + } + private static PublicKey getPublicKey(final JWK jwk) throws JOSEException, ParseException, KeyException { Objects.requireNonNull(jwk, "jwk must not be null"); diff --git a/java-spiffe-core/src/test/java/io/spiffe/bundle/jwtbundle/JwtBundleTest.java b/java-spiffe-core/src/test/java/io/spiffe/bundle/jwtbundle/JwtBundleTest.java index 875304b6..937cc97d 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/bundle/jwtbundle/JwtBundleTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/bundle/jwtbundle/JwtBundleTest.java @@ -139,9 +139,8 @@ void testLoadFileWithRsaAndEc_Success() throws URISyntaxException { assertNotNull(jwtBundle); assertEquals(TrustDomain.parse("domain.test"), jwtBundle.getTrustDomain()); - assertEquals(2, jwtBundle.getJwtAuthorities().size()); + assertEquals(1, jwtBundle.getJwtAuthorities().size()); assertNotNull(jwtBundle.getJwtAuthorities().get("14cc39cd-838d-426d-9bb1-77f3468fba96")); - assertNotNull(jwtBundle.getJwtAuthorities().get("C6vs25welZOx6WksNYfbMfiw9l96pMnD")); } @Test @@ -158,16 +157,20 @@ void testLoadFile_MissingKid_ThrowsJwtBundleException() throws URISyntaxExceptio } @Test - void testLoadFile_InvalidKeyType_ThrowsKeyException() throws URISyntaxException, KeyException { + void testLoadFile_InvalidKeyType_IsIgnored() throws URISyntaxException, KeyException { Path path = Paths.get(toUri("testdata/jwtbundle/jwks_invalid_keytype.json")); TrustDomain trustDomain = TrustDomain.parse("domain.test"); + JwtBundle jwtBundle = null; try { - JwtBundle.load(trustDomain, path); - fail("should have thrown exception"); + jwtBundle = JwtBundle.load(trustDomain, path); } catch (JwtBundleException e) { - assertEquals("Unsupported JWT family algorithm: OKP", e.getCause().getMessage()); + fail(e); } + + assertNotNull(jwtBundle); + assertEquals(trustDomain, jwtBundle.getTrustDomain()); + assertTrue(jwtBundle.getJwtAuthorities().isEmpty()); } @Test @@ -215,9 +218,8 @@ void testParseJsonWithRsaAndEcKeys_Success() throws URISyntaxException, IOExcept assertNotNull(jwtBundle); assertEquals(TrustDomain.parse("domain.test"), jwtBundle.getTrustDomain()); - assertEquals(2, jwtBundle.getJwtAuthorities().size()); + assertEquals(1, jwtBundle.getJwtAuthorities().size()); assertNotNull(jwtBundle.getJwtAuthorities().get("14cc39cd-838d-426d-9bb1-77f3468fba96")); - assertNotNull(jwtBundle.getJwtAuthorities().get("C6vs25welZOx6WksNYfbMfiw9l96pMnD")); } @Test diff --git a/java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_missing_kid.json b/java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_missing_kid.json index 04808a38..b5bf1680 100644 --- a/java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_missing_kid.json +++ b/java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_missing_kid.json @@ -9,6 +9,7 @@ }, { "kty": "EC", + "use": "jwt-svid", "crv": "P-256", "x": "7MGOl06DP9df2u8oHY6lqYFIoQWzCj9UYlp-MFeEYeY", "y": "PSLLy5Pg0_kNGFFXq_eeq9kYcGDM3MPHJ6ncteNOr6w" diff --git a/java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_valid_EC_1.json b/java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_valid_EC_1.json index 9315a56f..1ad0588a 100644 --- a/java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_valid_EC_1.json +++ b/java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_valid_EC_1.json @@ -2,6 +2,7 @@ "keys": [ { "kty": "EC", + "use": "jwt-svid", "kid": "C6vs25welZOx6WksNYfbMfiw9l96pMnD", "crv": "P-256", "x": "ngLYQnlfF6GsojUwqtcEE3WgTNG2RUlsGhK73RNEl5k", diff --git a/java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_valid_RSA_1.json b/java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_valid_RSA_1.json index 2d519d90..8f5cfe83 100644 --- a/java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_valid_RSA_1.json +++ b/java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_valid_RSA_1.json @@ -6,7 +6,7 @@ "q": "o7Enkd4yxw9tl-jHQXw38GjItzdbFLmkeppHSIzv02Zs5XS0CQyb-jFrUXStpO3UiNrRXm1YUCSUwRDj81ATilrIpPTTH6qC2Fu_E5eEEDx79W5p0oj3SbN2BQS5-MkhVvMrrDGkIAmRmLcE9SH-eIreYgB6XW0yrbHTwbUWV98", "d": "EpfAjuifddpKISal0znpNhlfVkRDyEdd6_CBlh6lLJdU8dflcqyWFhmJ8pEXsnwC4-DBkkDKt57HcIq3MQ8Q_IKlhLPexugNr3QJxbA1DCbXagsIvh4-QcBQp-4LOZv1T3T4-lywkRzX0qp9yjySIGkT6OmAfN97Q-_NPhOdlHJn5feQfJhxWIMaWhQnomjmMP40FApRdk-gOmNx-w2pLWHVjnibbfI9SijUKg4ZqW3MvwNbSuM-EPyetRuaJRW892h7kxGE2wV_oqGRbqasPqVJN9SiNAEWwzcs645NwPEC48XUK0Q4eUyd9ra_YFv9HGgZ1Yo0gaD0BnRMLxcm-Q", "e": "AQAB", - "use": "sig", + "use": "jwt-svid", "kid": "14cc39cd-838d-426d-9bb1-77f3468fba96", "qi": "kly84cu6D3sy64HfRpXfIYuNZTcJxlfdkLcLY-ZHkY5oVUiDBb6VrKwkQq3e5UbxF0a6qzwCk_B0Bn6ChHXLorVLnlx8cUG2TP3C72J_iHpE8Bn3wend7ZqLfcceh19zPOe13352RurPXbewzf1tYVji-OPzJr05fUDrqmesXvI", "dp": "XTZnsbiFDvfNX6Y2mHDr9aDVBeGPTkOs_YAnCBrO7D7ZyI6WUUy8fHWjyxvMCjDS4IZQ5JOPEPRSQuk5BgeElGOoEE0w0-2AOS9Hkbs6G2vv_CdvYNrg2UqZqYA_aSZTMt6xV2JK1Sl59EO7wnJNQaeYe32nA9YeBwAqGoys_Rs", diff --git a/java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_valid_RSA_EC.json b/java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_valid_RSA_EC.json index ce082142..e2f510a0 100644 --- a/java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_valid_RSA_EC.json +++ b/java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_valid_RSA_EC.json @@ -6,7 +6,7 @@ "q": "o7Enkd4yxw9tl-jHQXw38GjItzdbFLmkeppHSIzv02Zs5XS0CQyb-jFrUXStpO3UiNrRXm1YUCSUwRDj81ATilrIpPTTH6qC2Fu_E5eEEDx79W5p0oj3SbN2BQS5-MkhVvMrrDGkIAmRmLcE9SH-eIreYgB6XW0yrbHTwbUWV98", "d": "EpfAjuifddpKISal0znpNhlfVkRDyEdd6_CBlh6lLJdU8dflcqyWFhmJ8pEXsnwC4-DBkkDKt57HcIq3MQ8Q_IKlhLPexugNr3QJxbA1DCbXagsIvh4-QcBQp-4LOZv1T3T4-lywkRzX0qp9yjySIGkT6OmAfN97Q-_NPhOdlHJn5feQfJhxWIMaWhQnomjmMP40FApRdk-gOmNx-w2pLWHVjnibbfI9SijUKg4ZqW3MvwNbSuM-EPyetRuaJRW892h7kxGE2wV_oqGRbqasPqVJN9SiNAEWwzcs645NwPEC48XUK0Q4eUyd9ra_YFv9HGgZ1Yo0gaD0BnRMLxcm-Q", "e": "AQAB", - "use": "sig", + "use": "jwt-svid", "kid": "14cc39cd-838d-426d-9bb1-77f3468fba96", "qi": "kly84cu6D3sy64HfRpXfIYuNZTcJxlfdkLcLY-ZHkY5oVUiDBb6VrKwkQq3e5UbxF0a6qzwCk_B0Bn6ChHXLorVLnlx8cUG2TP3C72J_iHpE8Bn3wend7ZqLfcceh19zPOe13352RurPXbewzf1tYVji-OPzJr05fUDrqmesXvI", "dp": "XTZnsbiFDvfNX6Y2mHDr9aDVBeGPTkOs_YAnCBrO7D7ZyI6WUUy8fHWjyxvMCjDS4IZQ5JOPEPRSQuk5BgeElGOoEE0w0-2AOS9Hkbs6G2vv_CdvYNrg2UqZqYA_aSZTMt6xV2JK1Sl59EO7wnJNQaeYe32nA9YeBwAqGoys_Rs", @@ -15,6 +15,7 @@ }, { "kty": "EC", + "use": "x509-svid", "kid": "C6vs25welZOx6WksNYfbMfiw9l96pMnD", "crv": "P-256", "x": "ngLYQnlfF6GsojUwqtcEE3WgTNG2RUlsGhK73RNEl5k",