[java-oidc-common] branch main updated: Allow passing the KeyId from the criterion to the remote JWK Set cache

Phil Smart philip.smart at jisc.ac.uk
Thu Jan 5 13:55:56 UTC 2023


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

philsmart pushed a commit to branch main
in repository java-oidc-common.

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

The following commit(s) were added to refs/heads/main by this push:
     new d140549  Allow passing the KeyId from the criterion to the remote JWK Set cache
d140549 is described below

commit d140549a2b49a8fe2f1d8f1b363cb57295fc3462
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Thu Jan 5 13:55:53 2023 +0000

    Allow passing the KeyId from the criterion to the remote JWK Set cache
    
     - This helps find keys during key roll over
---
 .../impl/ProviderMetadataCredentialResolver.java   | 41 +++++++++++++++++++---
 .../ProviderMetadataCredentialResolverTest.java    | 31 ++++++++++++++++
 ...er-resolver-remote-jwkset-response-rollover.jwk | 11 ++++++
 3 files changed, 78 insertions(+), 5 deletions(-)

diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolver.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolver.java
index 9244d4b..2f50a10 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolver.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolver.java
@@ -24,6 +24,7 @@ import java.util.Collections;
 import java.util.LinkedHashSet;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import org.opensaml.security.credential.Credential;
 import org.opensaml.security.credential.impl.AbstractCriteriaFilteringCredentialResolver;
@@ -38,6 +39,7 @@ import com.nimbusds.jose.jwk.RSAKey;
 import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
 
 import net.shibboleth.oidc.jwk.RemoteJwkSetCache;
+import net.shibboleth.oidc.security.criterion.KeyIdCriterion;
 import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
 import net.shibboleth.oidc.security.impl.CredentialConversionUtil;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
@@ -46,6 +48,7 @@ import net.shibboleth.utilities.java.support.component.ComponentInitializationEx
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.component.InitializableComponent;
 import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
 import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
 import net.shibboleth.utilities.java.support.resolver.ResolverException;
 
@@ -137,9 +140,18 @@ public class ProviderMetadataCredentialResolver extends BasicJOSEObjectCredentia
         
         final LinkedHashSet<Credential> credentials = new LinkedHashSet<>(1);
         
-        if (metadata.getJWKSetURI() != null) {
-            final JWKSet keySet = remoteJwkSetCache.fetch(metadata.getJWKSetURI(),
-                    Instant.now().plus(keyFetchInterval));
+        if (metadata.getJWKSetURI() != null) {            
+            
+            final String keyIdFromCriteria = extractKeyIdFromCriteria(criteriaSet);
+            
+            JWKSet keySet = null;
+            if (StringSupport.trimOrNull(keyIdFromCriteria) != null) {
+                keySet = remoteJwkSetCache.fetch(metadata.getJWKSetURI(),keyIdFromCriteria,
+                        Instant.now().plus(keyFetchInterval));
+            } else {            
+                keySet = remoteJwkSetCache.fetch(metadata.getJWKSetURI(),
+                        Instant.now().plus(keyFetchInterval));
+            }
             
             if (keySet == null) {
                 log.debug("Remote keys could not be fetched, unable to resolve credentials");
@@ -165,7 +177,26 @@ public class ProviderMetadataCredentialResolver extends BasicJOSEObjectCredentia
         } else {
             log.trace("No JWK Set available, no credentials returned");
         }
-        return credentials;
-   
+        return credentials;   
+    }
+    
+    /**
+     * Extract a KeyId from the criteria set if one exists. If not, return {@code null}. 
+     * 
+     * @param criteriaSet the criteria set to pull the keyId from
+     * 
+     * @return a KeyId if one exists, {@code null} otherwise
+     */
+    @Nullable private String extractKeyIdFromCriteria(@Nonnull final CriteriaSet criteriaSet) {
+        if (criteriaSet.contains(EvaluableKeyIDCredentialCriterion.class)) {
+            return criteriaSet.get(EvaluableKeyIDCredentialCriterion.class).getKeyId();
+        } else if (criteriaSet.contains(KeyIdCriterion.class)) {
+            return criteriaSet.get(KeyIdCriterion.class).getKeyId();
+        } else {
+            return null;
+        }
+            
     }
+
+    
 }
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolverTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolverTest.java
index 1ca8e86..439ea40 100644
--- a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolverTest.java
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolverTest.java
@@ -24,7 +24,10 @@ import static org.testng.Assert.fail;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.net.URI;
 import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -52,6 +55,7 @@ import org.testng.annotations.Test;
 import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
 
 import net.shibboleth.oidc.jwk.RemoteJwkSetCache;
+import net.shibboleth.oidc.security.criterion.KeyIdCriterion;
 import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
@@ -69,6 +73,10 @@ public class ProviderMetadataCredentialResolverTest {
     private static final ClassPathResource REMOTE_JWKSET_NO_KEYS = 
             new ClassPathResource("/credentials/test-provider-resolver-remote-jwkset-response-nokeys.jwk");
     
+    /** A remote JWKSet with a rolled over key.*/
+    private static final ClassPathResource REMOTE_JWKSET_ROLLOVER = 
+            new ClassPathResource("/credentials/test-provider-resolver-remote-jwkset-response-rollover.jwk");
+    
     /** A remote JWKSet with no keys array.*/
     private static final ClassPathResource REMOTE_JWKSET_NO_KEY_ARRAY = 
             new ClassPathResource("/credentials/test-provider-resolver-remote-jwkset-response-no-key-array.jwk");
@@ -186,6 +194,29 @@ public class ProviderMetadataCredentialResolverTest {
         assertEquals(credsList.size(), 0);
     }
     
+    /* Test refetching the keyset if the keyset is cached but the key ID did not exist.*/
+    @Test
+    public void testSuccess_KeyRotatedRefetch() throws Exception {
+        // Cache the keyset, then fetch a new keyID later
+        cache.fetch(new URI("https://localhost:9921/oauth2/v3/certs"), 
+                Instant.now().plus(Duration.ofMinutes(10)));
+        resolver.initialize();
+        
+        //Roll over the keyset document
+        cache.setHttpClient(createMockHttpClient(readJsonFromFile(REMOTE_JWKSET_ROLLOVER)));
+        
+        criteria.add(new UsageCriterion(UsageType.SIGNING));
+        criteria.add(new EvaluableKeyIDCredentialCriterion(new KeyIdCriterion("not-in-original-cache")));
+        
+        final Iterable<Credential> creds = resolver.resolve(criteria);
+        
+        assertNotNull(creds);
+        final List<Credential> credsList = new ArrayList<>();
+        creds.forEach(credsList::add);        
+        // A key
+        assertEquals(credsList.size(), 1);
+    }
+    
     @Test
     public void testSuccess_ForEncryptionAndKeyAlg() throws Exception {
         resolver.initialize();
diff --git a/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-rollover.jwk b/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-rollover.jwk
new file mode 100644
index 0000000..f2a69de
--- /dev/null
+++ b/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-rollover.jwk
@@ -0,0 +1,11 @@
+{
+"keys": [
+{
+"kty": "RSA",
+"e": "AQAB",
+"use": "sig",
+"kid": "not-in-original-cache",
+"n": "kk-3jeBmUPbpMk0fEdIn-APAdNOoOckA0e-SiALLxy5dWfG-GyF51g31zuM_iNiSiMSsmG2ZAVi48iItFpd-JW9IIT40TC147I6aKrel0Rf39Mwp-1tCzME6VYEgOmgI9qDg2e4edt1cvjQfiw3IZlXakwgYQn2BuoknoCBVjETVLHrnsvEqXhPffzML9O5Ze_nBOX6-pCAzVsimr-ljoln2GQz-ID5fGzlflXJV78v7QzlyyAAQovYQMxiEBgecHu44S0Iu_esLEOOobQkZyHc-OgcwEazfJUEUhKEnevVTJFlQF3Odxp1I6W9zd-zLUceqIMKF5Xs10AfmkPhboQ"
+}
+]
+}
\ No newline at end of file

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


More information about the commits mailing list