[java-idp-oidc] 05/31: JOIDC-222 - Support for OpenID Federation

Henri Mikkonen henri.mikkonen at iki.fi
Tue Jun 24 08:52:41 UTC 2025


This is an automated email from the git hooks/post-receive script.

hjmikkon pushed a commit to branch dev/JOIDC-222
in repository java-idp-oidc.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=fe06100259b5b9605ee802ad41d5169de61174d4

commit fe06100259b5b9605ee802ad41d5169de61174d4
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Thu Jan 16 18:03:57 2025 +0200

    JOIDC-222 - Support for OpenID Federation
    
    https://shibboleth.atlassian.net/browse/JOIDC-222
    
    Exploit local trust anchor configuration when fetching possible trust chains
    - The trust chains resolved via authority hints are iterated over for local anchors that are used as intermediate authorities
    - Such cases are added as new additional trust chains with local anchor as trust anchor
---
 .../DefaultTrustChainFetchingStrategy.java         | 102 ++++++++++++++++++++-
 .../META-INF/net.shibboleth.idp/postconfig.xml     |   3 +-
 2 files changed, 102 insertions(+), 3 deletions(-)

diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustChainFetchingStrategy.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustChainFetchingStrategy.java
index aa74b727..f20d3497 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustChainFetchingStrategy.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustChainFetchingStrategy.java
@@ -16,7 +16,9 @@ package net.shibboleth.idp.plugin.oidc.op.oidfed.metadata;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.function.Function;
+import java.util.stream.Stream;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -59,6 +61,9 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
     /** Cache for subordinate entity statements. */
     @NonnullAfterInit private MetadataCache<EntityStatement> subordinateStatementCache;
 
+    /** Cache containing local copies of trusted trust anchor keys. */
+    @NonnullAfterInit private MetadataCache<Map<String, LocalKeyContainer>> localTrustAnchorsCache;
+
     /**
      * Set the strategy for fetching entity ID from the criteria set.
      * 
@@ -90,6 +95,16 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
         subordinateStatementCache = Constraint.isNotNull(cache, "Subordinate statement cache cannot be null");
     }
 
+    /**
+     * Set the cache containing local copies of trusted trust anchor keys.
+     * 
+     * @param cache cache containing local copies of trusted trust anchor keys.
+     */
+    public void setLocalTrustAnchorsCache(@Nonnull final MetadataCache<Map<String, LocalKeyContainer>> cache) {
+        checkSetterPreconditions();
+        localTrustAnchorsCache = Constraint.isNotNull(cache, "Local Trust Anchor cache cannot be null");
+    }
+
     /** {@inheritDoc} */
     @Override
     protected void doInitialize() throws ComponentInitializationException {
@@ -103,6 +118,9 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
         if (subordinateStatementCache == null) {
             throw new ComponentInitializationException("Subordinate statement cache cannot be null");
         }
+        if (localTrustAnchorsCache == null) {
+            throw new ComponentInitializationException("Local Trust Anchor cache cannot be null");
+        }
     }
 
     /** {@inheritDoc} */
@@ -119,9 +137,11 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
             }
             final EntityStatement entityConfiguration = entityConfigurationCache.get(criteria).get(0);
             assert entityConfiguration != null;
-            final List<List<EntityStatement>> chain = populateChain(
+            final List<List<EntityStatement>> rawChains = populateChain(
                     CollectionSupport.listOf(CollectionSupport.listOf(entityConfiguration)));
-            return stripIntermediateConfigurations(entityConfiguration, chain);
+            final List<List<EntityStatement>> trustChains =
+                    stripIntermediateConfigurations(entityConfiguration, rawChains);
+            return Stream.concat(trustChains.stream(), resolveLocallyTrustedTrustChains(trustChains).stream()).toList();
         } catch (final MetadataCacheException e) {
             log.error("Could not fetch entity configuration for the trust chain", e);
         }
@@ -135,6 +155,7 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
      * @param chains the list of trust chains to be filtered
      * @return the filtered list of trust chains, intermediate configurations filtered out
      */
+    @Nonnull
     private List<List<EntityStatement>> stripIntermediateConfigurations(@Nonnull final EntityStatement leaf,
             @Nonnull @NonnullElements final List<List<EntityStatement>> chains) {
         final List<List<EntityStatement>> result = new ArrayList<>();
@@ -148,6 +169,83 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
         return result;
     }
 
+    /**
+     * Iterates the given trust chains and checks whether they contain locally trusted trust anchors as intermediate
+     * authorities. Such trust chains are returned in a way that locally trusted anchors are the trust anchors (i.e.
+     * the final items in the chain).
+     * 
+     * @param chains trust chains to be iterated over
+     * @return list of trust chains containing locally trusted trust anchors
+     */
+    @Nonnull private List<List<EntityStatement>> resolveLocallyTrustedTrustChains(
+            @Nonnull @NonnullElements final List<List<EntityStatement>> chains) {
+        final List<List<EntityStatement>> result = new ArrayList<>();
+        for (final List<EntityStatement> chain : chains) {
+            for (int i = 0; i < chain.size() - 1; i++) {
+                final EntityStatement statement = chain.get(i);
+                assert statement != null;
+                final String entityId = chain.get(i).getEntityID().getValue();
+                assert entityId != null;
+                if (isLocallyTrusted(statement) && !isTrustAnchor(entityId, result)) {
+                    try {
+                        final CriteriaSet criteria = new CriteriaSet(new SubjectEntityIDCriterion(entityId));
+                        final EntityStatement localAnchorConfiguration =
+                                getFirstIfFound(entityConfigurationCache.get(criteria));
+                        if (localAnchorConfiguration != null) {
+                            final List<EntityStatement> localTrustChain =
+                                    new ArrayList<>(chain.subList(0, chain.indexOf(statement)));
+                            localTrustChain.add(localAnchorConfiguration);
+                            result.add(CollectionSupport.copyToList(localTrustChain));
+                            log.debug("Included a trust chain based on a local trust anchor {}", entityId);
+                        }
+                    } catch (final MetadataCacheException e) {
+                        log.error("Could not resolve entity configuration for {}", entityId);
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Checks whether the given entity is locally trusted via local trust anchors cache.
+     * 
+     * @param statement the entity to be verified
+     * @return true if the entity is locally trusted, false otherwise
+     */
+    private boolean isLocallyTrusted(@Nonnull final EntityStatement statement) {
+        final CriteriaSet criteria = new CriteriaSet(new SubjectEntityStatementCriterion(statement));
+        final String entityId = statement.getEntityID().getValue();
+        assert entityId != null;
+        final List<Map<String, LocalKeyContainer>> keyContainers;
+        try {
+            keyContainers = localTrustAnchorsCache.get(criteria);
+        } catch (final MetadataCacheException e) {
+            log.debug("Could not resolve local trust anchor keys from the cache for {}", entityId, e);
+            return false;
+        }
+        if (keyContainers.isEmpty() || !keyContainers.get(0).containsKey(entityId)) {
+            log.trace("No locally trusted keys found for {}", entityId);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Checks whether the given entity is already a trust anchor in any of the given trust chains.
+     * 
+     * @param entityId the entity ID to be verified
+     * @param trustChains trust chains to be checked
+     * @return true if the entity is already a trust anchor, false otherwise
+     */
+    private boolean isTrustAnchor(@Nonnull final String entityId,
+            @Nonnull final List<List<EntityStatement>> trustChains) {
+        return trustChains.stream()
+                .filter(chain -> entityId.equals(chain.get(chain.size() - 1).getEntityID().getValue()))
+                .findAny()
+                .isPresent();
+    }
+
     /**
      * Recursively populates the given list of trust chains until the last entity statement in each chain doesn't
      * contain any authority hints.
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index acded3d1..0cbfdee9 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -1047,7 +1047,8 @@
             <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultTrustChainFetchingStrategy"
                 p:criteriaToSubjectEntityIdStrategy-ref="shibboleth.oidfed.DefaultSubjectEntityIDCriteriaToIdentifierStrategy"
                 p:entityConfigurationCache-ref="shibboleth.oidfed.EntityConfigurationMetadataCache"
-                p:subordinateStatementCache-ref="shibboleth.oidfed.SubordinateEntityStatementMetadataCache"/>
+                p:subordinateStatementCache-ref="shibboleth.oidfed.SubordinateEntityStatementMetadataCache"
+                p:localTrustAnchorsCache-ref="shibboleth.oidfed.LocalTrustAnchorsMetadataCache" />
         </property>
     </bean>
 

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list