[java-oidfed-common] 02/02: Use trust anchor's entity configuration keys for trust mark validation
Codeberg
noreply at shibboleth.net
Thu Oct 8 05:32:19 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository java-oidfed-common.
View the commit online:
https://codeberg.org/Shibboleth/java-oidfed-common/commit/8100271d27a54a4eb1155b8b08d6127bc719543b
commit 8100271d27a54a4eb1155b8b08d6127bc719543b
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Thu Oct 8 08:30:45 2026 +0300
Use trust anchor's entity configuration keys for trust mark validation
- Previously the locally trusted anchor keys were used: this was too strict as they're needed solely for the trust anchor entity configuration validation
- Anchor may use another keys for signing trust marks
- Cover in flow tests
---
.../flow/TestTrustChainResolutionFlowTest.java | 105 +++++++++++++++++++++
...ustMarkIssuerConfigurationFetchingStrategy.java | 2 +-
.../oidfed/testing/TestFederationCredentials.java | 9 ++
.../oidfed/testing/fed-local-anchor2.jwk | 13 +++
4 files changed, 128 insertions(+), 1 deletion(-)
diff --git a/oidfed-common-conf-impl/src/test/java/net/shibboleth/oidfed/flow/TestTrustChainResolutionFlowTest.java b/oidfed-common-conf-impl/src/test/java/net/shibboleth/oidfed/flow/TestTrustChainResolutionFlowTest.java
index e18acbb..3aec485 100644
--- a/oidfed-common-conf-impl/src/test/java/net/shibboleth/oidfed/flow/TestTrustChainResolutionFlowTest.java
+++ b/oidfed-common-conf-impl/src/test/java/net/shibboleth/oidfed/flow/TestTrustChainResolutionFlowTest.java
@@ -26,6 +26,8 @@ import java.util.Map;
import javax.annotation.Nonnull;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.webflow.execution.FlowExecutionOutcome;
import org.springframework.webflow.executor.FlowExecutionResult;
import org.testng.Assert;
@@ -39,6 +41,8 @@ import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
+import net.shibboleth.oidc.metadata.cache.MetadataCache;
+import net.shibboleth.oidfed.metadata.cache.configuration.EntityConfigurationContainer;
import net.shibboleth.oidfed.testing.FederationJwtSupport;
import net.shibboleth.oidfed.testing.TestFederationCredentials;
import net.shibboleth.shared.collection.CollectionSupport;
@@ -51,6 +55,10 @@ public class TestTrustChainResolutionFlowTest extends AbstractFederationFlowTest
/** The flow id. */
@Nonnull public static final String FLOW_ID = "admin/oidfed/test-trust-chain-resolution";
+ @Autowired
+ @Qualifier("shibboleth.oidfed.EntityConfigurationMetadataCache")
+ MetadataCache<EntityConfigurationContainer> entityConfigurationCache;
+
/** Constructor. */
public TestTrustChainResolutionFlowTest() {
super(FLOW_ID, "ResponseView");
@@ -185,6 +193,103 @@ public class TestTrustChainResolutionFlowTest extends AbstractFederationFlowTest
assertTrustMarkDetails(0);
}
+ @Test
+ public void testTrustMarkWithAnchorAsIssuer_tmSignedWithLocallyTrustedKey() throws Exception {
+ final String entityId = super.uniqueClientId();
+ request.setMethod("GET");
+ request.addParameter("entityID", entityId);
+ request.addParameter("includeTrustMark", "true");
+
+ final OIDCClientMetadata metadata = new OIDCClientMetadata();
+ metadata.setRedirectionURI(new URI(redirectUri));
+ metadata.setJWKSet(new JWKSet(rpKey.toPublicJWK()));
+
+ final String trustMark = FederationJwtSupport.trustMark(JWSAlgorithm.RS256, trustedAnchorKey, anchorId,
+ entityId, "https://example.org/email-allowing-trust-mark", Instant.now().plusSeconds(300)).serialize();
+ final String rpEntityConfiguration = rpEntityConfiguration(entityId, metadata, List.of(Map.of(
+ "trust_mark_type", "https://example.org/email-allowing-trust-mark",
+ "trust_mark", trustMark)), leafKey);
+ final Map<String,List<String>> trustMarkIssuers =
+ Map.of("https://example.org/email-allowing-trust-mark", List.of(anchorId));
+ final String anchorEntityConfiguration =
+ trustedAnchorConfiguration(null, trustMarkIssuers, trustedAnchorKey, new JWKSet(trustedAnchorKey), defaultTrustMarkOwners());
+ entityConfigurationCache.clearAll();
+ rpConfigureMockHttpClient(entityId, rpEntityConfiguration, anchorEntityConfiguration);
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ entityConfigurationCache.clearAll();
+
+ final FlowExecutionOutcome outcome = result.getOutcome();
+ assertEquals(outcome.getId(), "ResponseView");
+ assertResponseArraySize(1);
+ assertTrustMarkDetails(1);
+ }
+
+ @Test
+ public void testTrustMarkWithAnchorAsIssuer_tmSignedWithAdditionalAnchorKey() throws Exception {
+ final String entityId = super.uniqueClientId();
+ request.setMethod("GET");
+ request.addParameter("entityID", entityId);
+ request.addParameter("includeTrustMark", "true");
+
+ final OIDCClientMetadata metadata = new OIDCClientMetadata();
+ metadata.setRedirectionURI(new URI(redirectUri));
+ metadata.setJWKSet(new JWKSet(rpKey.toPublicJWK()));
+
+ final JWK additionalKey = TestFederationCredentials.trustAnchorKeyUntrusted();
+ final JWKSet jwks = new JWKSet(List.of(trustedAnchorKey, additionalKey));
+ final Map<String,List<String>> trustMarkIssuers =
+ Map.of("https://example.org/email-allowing-trust-mark", List.of(anchorId));
+ final String anchorEntityConfiguration =
+ trustedAnchorConfiguration(null, trustMarkIssuers, trustedAnchorKey, jwks, defaultTrustMarkOwners());
+ final String trustMark = FederationJwtSupport.trustMark(JWSAlgorithm.RS256, additionalKey, anchorId,
+ entityId, "https://example.org/email-allowing-trust-mark", Instant.now().plusSeconds(300)).serialize();
+ final String rpEntityConfiguration = rpEntityConfiguration(entityId, metadata, List.of(Map.of(
+ "trust_mark_type", "https://example.org/email-allowing-trust-mark",
+ "trust_mark", trustMark)), leafKey);
+ entityConfigurationCache.clearAll();
+ rpConfigureMockHttpClient(entityId, rpEntityConfiguration, anchorEntityConfiguration);
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ entityConfigurationCache.clearAll();
+
+ final FlowExecutionOutcome outcome = result.getOutcome();
+ assertEquals(outcome.getId(), "ResponseView");
+ assertResponseArraySize(1);
+ assertTrustMarkDetails(1);
+ }
+
+ @Test
+ public void testTrustMarkWithAnchorAsIssuer_tmSignedWithAdditionalUnmappedAnchorKey() throws Exception {
+ final String entityId = super.uniqueClientId();
+ request.setMethod("GET");
+ request.addParameter("entityID", entityId);
+ request.addParameter("includeTrustMark", "true");
+
+ final OIDCClientMetadata metadata = new OIDCClientMetadata();
+ metadata.setRedirectionURI(new URI(redirectUri));
+ metadata.setJWKSet(new JWKSet(rpKey.toPublicJWK()));
+
+ final JWK additionalKey = TestFederationCredentials.trustAnchorKeyUntrusted();
+ final Map<String,List<String>> trustMarkIssuers =
+ Map.of("https://example.org/email-allowing-trust-mark", List.of(anchorId));
+ final String anchorEntityConfiguration =
+ trustedAnchorConfiguration(null, trustMarkIssuers, trustedAnchorKey, new JWKSet(trustedAnchorKey),
+ defaultTrustMarkOwners());
+ final String trustMark = FederationJwtSupport.trustMark(JWSAlgorithm.RS256, additionalKey, anchorId,
+ entityId, "https://example.org/email-allowing-trust-mark", Instant.now().plusSeconds(300)).serialize();
+ final String rpEntityConfiguration = rpEntityConfiguration(entityId, metadata, List.of(Map.of(
+ "trust_mark_type", "https://example.org/email-allowing-trust-mark",
+ "trust_mark", trustMark)), leafKey);
+ entityConfigurationCache.clearAll();
+ rpConfigureMockHttpClient(entityId, rpEntityConfiguration, anchorEntityConfiguration);
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ entityConfigurationCache.clearAll();
+
+ final FlowExecutionOutcome outcome = result.getOutcome();
+ assertEquals(outcome.getId(), "ResponseView");
+ assertResponseArraySize(1);
+ assertTrustMarkDetails(0);
+ }
+
@Test
public void testWithTrustMarkDetailsParameter_unmatchingTrustMarkType() throws Exception {
request.setMethod("GET");
diff --git a/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/cache/trustmark/DefaultTrustMarkIssuerConfigurationFetchingStrategy.java b/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/cache/trustmark/DefaultTrustMarkIssuerConfigurationFetchingStrategy.java
index 6ba1fbd..2b68bb8 100644
--- a/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/cache/trustmark/DefaultTrustMarkIssuerConfigurationFetchingStrategy.java
+++ b/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/cache/trustmark/DefaultTrustMarkIssuerConfigurationFetchingStrategy.java
@@ -127,7 +127,7 @@ public class DefaultTrustMarkIssuerConfigurationFetchingStrategy
if (issuerStatement != null) {
final var anchorCriteria = new CriteriaSet(
new SubjectEntityStatementCriterion(issuerStatement));
- if (validateJwt(wrapper.getJwt(), anchorCriteria, issuer)) {
+ if (validateJwt(issuerStatement.getJwt(), anchorCriteria, issuer)) {
return issuerStatement;
}
log.warn("Could not validate the issuer entity configuration against trust engine");
diff --git a/oidfed-common-testing/src/main/java/net/shibboleth/oidfed/testing/TestFederationCredentials.java b/oidfed-common-testing/src/main/java/net/shibboleth/oidfed/testing/TestFederationCredentials.java
index a089e7b..ca7e089 100644
--- a/oidfed-common-testing/src/main/java/net/shibboleth/oidfed/testing/TestFederationCredentials.java
+++ b/oidfed-common-testing/src/main/java/net/shibboleth/oidfed/testing/TestFederationCredentials.java
@@ -39,6 +39,15 @@ public class TestFederationCredentials {
.build();
}
+ public static JWK trustAnchorKeyUntrusted() {
+ final BasicJWKCredential localAnchor =
+ loadJWKCredential("/net/shibboleth/oidfed/testing/fed-local-anchor2.jwk");
+ return new RSAKey.Builder((RSAPublicKey) localAnchor.getPublicKey())
+ .privateKey(localAnchor.getPrivateKey())
+ .keyID("anotherAnchorKey")
+ .build();
+ }
+
public static JWK intermediateKey() {
final BasicJWKCredential localIntermediate =
loadJWKCredential("/net/shibboleth/oidfed/testing/fed-local-intermediate.jwk");
diff --git a/oidfed-common-testing/src/main/resources/net/shibboleth/oidfed/testing/fed-local-anchor2.jwk b/oidfed-common-testing/src/main/resources/net/shibboleth/oidfed/testing/fed-local-anchor2.jwk
new file mode 100644
index 0000000..cb5bfc8
--- /dev/null
+++ b/oidfed-common-testing/src/main/resources/net/shibboleth/oidfed/testing/fed-local-anchor2.jwk
@@ -0,0 +1,13 @@
+{
+ "p": "6kv5lprCgVS3iuSVZO3T1-W08GLrpS8Xnh6HkDJmBHTF1r_Dp2LrSt2KuW8fX8KyNDc0KKzpkjxqSg4THYUGO74RB6HBuPE0pZ7ZmyZGQzFAnbZYXxLElgXobTUnc4GRCI_R-oyLXkc-UJB7xNvGWReveqcFgSETDdBMlAALAI8",
+ "kty": "RSA",
+ "q": "94hxbDstRi-cbVCBs1LWx5MCs3fOxZNQs96TNr2Lmx3ZEpE78MYDuuQOyOnFKE4k4k0VeeU8722t99Wz4HfSWE9Xpuf6E6ptQBuQ3IPUv3b6kvt_F8MQ3NRVMYhzXZbtl6_kvhLu9dTEEvyQFXW55qeqLfvRR5nG1uJ4RCC9eG8",
+ "d": "NlLM1-ibXVXfFRZTKuxlwo1uwwlW3klN3L_4DIz4mRQ9nQ4YUXMXuOrMVXO4VDMDg_89d3hlP3jdGPzpIXv0248H0e1u8h1rBxvqywbajdplHIdacWgdLvecRyfxM9zGkE-7spgIIyMUJDy5XY-3dPpmSUzoMT531AZDxCdOEDsP1LqgJPUXxTn7Lal-DhznCCV7JMEaAvLs9lzyPrtqBYvYhH5JvRal2ZhylBCJONS3m9TCOjXWyg-9Pu-OK6yv11qjOhee97vAwCpBUYgcnpjQHrLOogjdREhCKljjP-V2f5z1W4E47zxbxDFXZCbaaE-k8djNcOo_phaULtI8jQ",
+ "e": "AQAB",
+ "use": "sig",
+ "kid": "anotherAnchorKey",
+ "qi": "vIX_D9z7mNzNlNWRw6yqNhFarNmjhMqLE9wx6P0972XD5_qSIhVoTekdb0vcQWOxfOkN-RvZSH-NMZ1pbHfkVM5fUWs0kuLwZ1qQ3WKSyThBP6lpJKmmA4uNrqssitmAoZWu6neo33S20oRCx3D3SirHgd2tH9iSBeZlFewgsZ4",
+ "dp": "ppXwVwGgTB51Bd8yzXk_x2C-vrBGxcLu8vDYCFGdMYvpYqAkN65gh3FvghsdXpPq6q9ZeZY_2Eb2d8L8GmALp4yjRR7gw2NUwHFqXSLgkEPDop8UxDxu5BA9SvChJUI5YbIIZTFqolTbXKLuLXDp3Uy26uk4Q7DFgGvbFtvU9sc",
+ "dq": "r_tIMBtOT8TPoIQJAfWWDyM8W12FGsBmMsq9vMA7PZeV2XZ5t-7_feJfhIV2jvYnkOAesZ_Bl7NRaI59KMvnNrOJ7HamO08CBv7SGYR8P3cWJGM68yjSlz06KFILOvFvj1UU-fE6BHo0wA7hXPBqr0pqWfDa093HGMS_nifsrG8",
+ "n": "4owt-361mhOFvdotqlbWdZG8qgF_RNyBkqhbCbWSmJZemhFSkUQg11T5PmA9HpDhTzs9FVqqnv5t28VTIpoLn491F9ICwxB86-37Qja6Z1Pkz3qqHUQmwXFA1MDQ95Pfb_eYK_qi2wZbknqgPBiEWkccY41oHOjWKJi7s78zhK0wyOiGVuS67gfmrGC0lTpi0vT6LgpEgssaN0JOlIzp2vJqdD9Gw5lNeDN0P5A34BEBtPPNl70wyDlQrQoC-elVQSNT5SbLRyWk06puNnPLVLwfThyvwCS0iW8JE5GfX-PHLwHqhvykCVRLvmQXAdzA8o0THdu1WzTd6SpedptGAQ"
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list