[java-oidc-common] 25/35: Move provider metadata parameter resolver to commons from RP

Phil Smart philip.smart at jisc.ac.uk
Tue Sep 20 14:19:25 UTC 2022


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

philsmart pushed a commit to branch dev/JCOMOIDC-41
in repository java-oidc-common.

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

commit 70cc7f32716b036c0e8693daddecad284c384cf8
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Aug 19 09:39:35 2022 +0100

    Move provider metadata parameter resolver to commons from RP
---
 ...oviderMetadataEncryptionParametersResolver.java | 336 +++++++++++++++++
 .../BasicJWTEncryptionParametersResolverTest.java  |  97 +----
 ...erMetadataEncryptionParametersResolverTest.java | 407 +++++++++++++++++++++
 .../oidc/security/impl/TestCredentialHelper.java   | 182 +++++++++
 4 files changed, 941 insertions(+), 81 deletions(-)

diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataEncryptionParametersResolver.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataEncryptionParametersResolver.java
new file mode 100644
index 0000000..6b2184e
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataEncryptionParametersResolver.java
@@ -0,0 +1,336 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.security.impl;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jose.EncryptionMethod;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.jwk.ECKey;
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.JWKSet;
+import com.nimbusds.jose.jwk.KeyType;
+import com.nimbusds.jose.jwk.KeyUse;
+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.JWTEncryptionConfiguration;
+import net.shibboleth.oidc.security.JWTEncryptionParameters;
+import net.shibboleth.oidc.security.credential.BasicJWKCredential;
+import net.shibboleth.oidc.security.criterion.JWKSetCriterion;
+import net.shibboleth.oidc.security.criterion.JWTEncryptionConfigurationCriterion;
+import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
+import net.shibboleth.utilities.java.support.annotation.constraint.Positive;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.logic.FunctionSupport;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+/**
+ * An extension of {@link BasicJWTEncryptionParametersResolver} to support parameter resolution from an
+ * OpenID Provider's metadata (remote keyset), in addition to those resolved from local configuration by the 
+ * base class. 
+ * 
+ * <p>The set of supported and configured key transport ('alg') and encryption methods ('enc') are derived
+ * from the intersection of those supported by local configuration, and those supported by the downstream
+ * OpenID Provider. The order of those algorithms in the local configuration are preserved, and affect which 
+ * credential is chosen. As algorithm 'alg' and 'enc' support is optional in provider metadata, failure to 
+ * locate them will result in the default behaviour of the parent class being applied (resolve from local 
+ * configuration). If they are present, but are not compatible with the set configured in the encryption 
+ * configuration, no parameters are returned (a failure). </p>
+ * 
+ * <p>Generally, the following logic applies:</p>
+ * <ul>
+ * <li>Private KeyWrapping and direct encryption credentials are found in the local 
+ * {@link JWTEncryptionConfiguration}.</li>
+ * <li>Public KeyEncryption or KeyAgreement credentials are found in the OpenID Provider's KeySet. 
+ * The Provider's metadata must be contained inside a ProviderMetadataCriterion, otherwise only local 
+ * credentials can be resolved.</li>
+ * </ul>
+ * 
+ * 
+ */
+public class ProviderMetadataEncryptionParametersResolver extends BasicJWTEncryptionParametersResolver {
+    
+    /** Logger. */
+    private final Logger log = LoggerFactory.getLogger(ProviderMetadataEncryptionParametersResolver.class);
+    
+    /** A strategy to locate the encryption methods ('enc') appropriate for the JWT to be encrypted.*/
+    @Nonnull private Function<OIDCProviderMetadata, List<EncryptionMethod>> providerEncryptionMethodsLookupStrategy;
+    
+    /** A strategy to locate the algorithms ('alg') appropriate for the JWT to be encrypted.*/
+    @Nonnull private Function<OIDCProviderMetadata, List<JWEAlgorithm>> providerKeyTransportAlgorithmsLookupStrategy;
+    
+    /** The cache for remote JWK key sets. */
+    @Nullable private RemoteJwkSetCache remoteJwkSetCache;
+    
+    /** The remote key refresh interval. Default value: 30 minutes. */
+    @Positive
+    private Duration keyFetchInterval = Duration.ofMinutes(30);
+    
+    /** Constructor.*/
+    public ProviderMetadataEncryptionParametersResolver() {
+        super();
+        providerEncryptionMethodsLookupStrategy = FunctionSupport.constant(Collections.emptyList());
+        providerKeyTransportAlgorithmsLookupStrategy = FunctionSupport.constant(Collections.emptyList());
+    }
+    
+    /**
+     * Set the strategy used to locate the algorothms ('alg') from the OpenID Provider metadata
+     * appropriate for the JWT to be encrypted.
+     *     
+     * @param strategy the strategy
+     */
+    public void setProviderKeyTransportAlgorithmsLookupStrategy(
+            @Nonnull final Function<OIDCProviderMetadata, List<JWEAlgorithm>> strategy) {
+
+        providerKeyTransportAlgorithmsLookupStrategy = Constraint.isNotNull(strategy,
+                "ProviderAlgorithmsLookupStrategy can not be null");
+    }
+    
+    /**
+     * Set the strategy used to locate the encryption methods ('enc') from the OpenID Provider metadata
+     * appropriate for the JWT to be encrypted.
+     *     
+     * @param strategy the strategy
+     */
+    public void setProviderEncryptionMethodsLookupStrategy(
+            @Nonnull final Function<OIDCProviderMetadata, List<EncryptionMethod>> strategy) {
+
+        providerEncryptionMethodsLookupStrategy = Constraint.isNotNull(strategy,
+                "ProviderEncryptionMethodsLookupStrategy can not be null");
+    }
+    
+    /**
+     * Set the cache for remote JWK key sets.
+     * 
+     * @param jwkSetCache What to set.
+     */
+    public void setRemoteJwkSetCache(final RemoteJwkSetCache jwkSetCache) {
+        remoteJwkSetCache = Constraint.isNotNull(jwkSetCache, "The remote JWK set cache cannot be null");
+    }
+    
+    /**
+     * Set the remote key refresh interval.
+     * 
+     * @param interval What to set.
+     */
+    public void setKeyFetchInterval(@Positive final Duration interval) {
+        Constraint.isFalse(interval == null || interval.isNegative(), "Remote key refresh must be greater than 0");
+        keyFetchInterval = interval;
+    }
+    
+    @Override
+    protected void resolveAndPopulateCredentialsAndAlgorithms(@Nonnull final JWTEncryptionParameters params,
+            @Nonnull final CriteriaSet criteria, @Nonnull final Predicate<String> includeExcludePredicate) {
+        
+        if (remoteJwkSetCache == null) {
+            log.debug("OIDC Provider metadata encryption parameters resolver does not have a remote JWKSet cache set,"
+                    + "falling back to default local configuration");
+            super.resolveAndPopulateCredentialsAndAlgorithms(params, criteria, includeExcludePredicate);
+            return;
+        }
+        
+        if (!criteria.contains(JWTEncryptionConfigurationCriterion.class)) {
+            log.debug("No encryption configuration criterion, encryption parameters can not be resolved");
+            return;
+        }
+        
+        if (!criteria.contains(ProviderMetadataCriterion.class)) {
+            log.debug("No provider metadata criterion, falling back to local configuration");
+            super.resolveAndPopulateCredentialsAndAlgorithms(params, criteria, includeExcludePredicate);
+            return;
+        }    
+        final OIDCProviderMetadata metadata = criteria.get(ProviderMetadataCriterion.class).getMetadata();
+        
+        final List<JWTEncryptionConfiguration> encryptionConfigurations =
+                criteria.get(JWTEncryptionConfigurationCriterion.class).getConfigurations();
+        if (encryptionConfigurations.isEmpty()) {
+            log.debug("No encryption configuration, encryption parameters can not be resolved");
+            return;
+        }
+        
+        // We populate the parameters for the algorithms the provider has registered in metadata
+        final List<JWEAlgorithm> keyTransportAlgorithms =  
+                providerKeyTransportAlgorithmsLookupStrategy.apply(metadata);      
+        log.trace("Resolved effective key transport algorithms from provider metadata: {}", keyTransportAlgorithms);
+        if (keyTransportAlgorithms.isEmpty()) {
+            log.debug("No algorithm ('alg') information in provider metadata, "
+                    + "falling back to default local configuration");
+            super.resolveAndPopulateCredentialsAndAlgorithms(params, criteria, includeExcludePredicate);
+            return;
+        }        
+        
+        final List<EncryptionMethod> dataEncryptionMethods = 
+                providerEncryptionMethodsLookupStrategy.apply(metadata);        
+        log.trace("Resolved effective data encryption algorithms from provider metadata: {}", dataEncryptionMethods);
+        
+        final List<String> keyTransportAlgorithmSupported =
+                getEffectiveKeyTransportAlgorithms(criteria, includeExcludePredicate);
+        log.trace("Resolved supported key transport algorithms from config: {}", 
+                keyTransportAlgorithmSupported);        
+        
+        final List<String> dataEncryptionAlgorithmsSupported =
+                getEffectiveDataEncryptionAlgorithms(criteria, includeExcludePredicate);
+        log.trace("Resolved supported data encryption algorithms from config: {}", dataEncryptionAlgorithmsSupported);
+        
+        
+        final List<String> supportedAndConfiguredKeyTransportAlgorithms = 
+                findAlgorithmIntersection(keyTransportAlgorithms.stream().map(JWEAlgorithm::getName)
+                        .collect(Collectors.toList()),keyTransportAlgorithmSupported);
+        
+        final List<String> supportedAndConfiguredDataEncryptionAlgorithms = 
+                findAlgorithmIntersection(dataEncryptionMethods.stream().map(EncryptionMethod::getName)
+                        .collect(Collectors.toList()),dataEncryptionAlgorithmsSupported);
+        
+        log.debug("Supported and configured key transport algorithms: {}", 
+                supportedAndConfiguredKeyTransportAlgorithms);
+        log.debug("Supported and configured data encryption algorithms: {}", 
+                supportedAndConfiguredDataEncryptionAlgorithms);
+        
+        if (supportedAndConfiguredKeyTransportAlgorithms.isEmpty()) {
+            log.warn("No supported key transport algorithm. Provider metadata and configuration are not compatible");
+            return;
+        }
+        if (supportedAndConfiguredDataEncryptionAlgorithms.isEmpty()) {
+            log.warn("No supported data encryption method. Provider metadata and configuration are not compatible");
+            return;
+        }
+        
+        // Add JWKSet criterion so the callback methods can pull out the OP's JWK set.
+        criteria.add(new JWKSetCriterion(getProviderKeys(metadata)));
+        
+        // Now we have resolved the set of supported 'alg' and 'enc' algorithms, delegate back to the base
+        // class to check locally configured credentials, and call back to this class to resolve from 
+        // the providers key set.
+        super.resolveCredentialForSupportedAlgorithm(criteria, 
+                convertStringAlgorithmURIsToJwkAlgorithms(supportedAndConfiguredKeyTransportAlgorithms),
+                convertStringEncryptionMethodURIsToEncryptionMethods(supportedAndConfiguredDataEncryptionAlgorithms),
+                getEffectiveKeyTransportCredentials(criteria),
+                getEffectiveDataEncryptionCredentials(criteria),
+                params);
+        
+        if (params.getKeyTransportEncryptionCredential() == null && params.getDataEncryptionCredential() == null) {
+            log.debug("Unable to resolve either key transport or data encryption credential");
+        }
+    }
+    
+    /**
+     * {@inheritDoc}
+     * <p>Resolves key transport credentials compatible with the given algorithm from those defined in the 
+     * OP's JWKSet (stored in the criteria).</p>
+     */
+    @Override
+    protected void resolveKeyTransportCredentialForSupportedAlgorithmFromAdditionalSource(
+            @Nonnull final JWEAlgorithm algorithm, @Nonnull final EncryptionMethod encryptionMethod,
+            @Nonnull final CriteriaSet criteria, @Nonnull final JWTEncryptionParameters params) {
+        
+        final JWKSetCriterion jwkSetCriterion = criteria.get(JWKSetCriterion.class);
+        if (jwkSetCriterion == null) {
+            log.debug("Unable to find JWKSet criterion, can not resolver provider keys");
+            return;
+        }
+        final JWKSet providerKeySet = jwkSetCriterion.getJWKSet();
+        if (providerKeySet == null) {
+            log.debug("Unable to find keys in JWKSet criterion, can not resolver provider keys");
+            return;
+        }
+        
+        // All keys in provider metadata should be key transport, not direct data/content encryption, as that
+        // is a public document.
+        final JWK key = 
+                providerKeySet.getKeys().stream()
+                .filter(Objects::nonNull)
+                .filter(k -> KeyUse.ENCRYPTION == k.getKeyUse())
+                .filter(k -> k.getAlgorithm().equals(algorithm))
+                .findFirst().orElse(null);
+        
+        if (key != null) {
+            final BasicJWKCredential jwkCredential = new BasicJWKCredential();
+            jwkCredential.setAlgorithm(algorithm);
+            jwkCredential.setKid(key.getKeyID());
+            try {
+                if (key.getKeyType().equals(KeyType.RSA)) {
+                    jwkCredential.setPublicKey(((RSAKey) key).toPublicKey());
+                } else if (key.getKeyType().equals(KeyType.EC)){
+                    jwkCredential.setPublicKey(((ECKey) key).toPublicKey());
+                }
+            } catch (final JOSEException e) {
+                log.warn("Unable to parse keyset", e);
+                return;
+            }
+            if (checkKeyAlgorithmAndLength(jwkCredential, algorithm.getName())) {
+                log.debug("Selected key '{}' for alg {} and enc {}", key.getKeyID(), 
+                        algorithm.getName(), encryptionMethod.getName());
+                params.setKeyTransportEncryptionCredential(jwkCredential);
+                params.setKeyTransportEncryptionAlgorithm(algorithm.getName());
+                params.setDataEncryptionAlgorithm(encryptionMethod.getName()); 
+            }
+        }
+        
+    }
+    
+    /**
+     * Fetch the OpenID Provider's remote JWKSet.
+     * 
+     * @param metadata the OpenID Provider's metadata
+     * 
+     * @return the JSON Web Keys set. Or an empty key set if the fetch failed.
+     */
+    @Nonnull private JWKSet getProviderKeys(@Nonnull final OIDCProviderMetadata metadata) {
+            final JWKSet keys = remoteJwkSetCache.fetch(metadata.getJWKSetURI(),
+                    Instant.now().plus(keyFetchInterval));
+            if (keys == null) {
+                return new JWKSet();
+            } else {
+                return keys;
+            }
+    }
+    
+    /**
+     * Return a new list of algorithms that represents the set intersection of the two input algorithm lists.
+     * The original order of algorithms from the {@code configAlgorithms} list is preserved.
+     * 
+     * @param providerAlgorithms the set of algorithms specified by the OpenID Provider
+     * @param configAlgorithms the set of algorithms specified by the IdP's configuration
+     * 
+     * @return the intersection of both lists
+     */
+    @Nonnull private List<String> findAlgorithmIntersection(@Nonnull final List<String> providerAlgorithms,
+            @Nonnull final List<String> configAlgorithms){        
+        return configAlgorithms.stream().filter(providerAlgorithms::contains).collect(Collectors.toList());
+        
+    }
+    
+    
+
+}
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolverTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolverTest.java
index a0a5b15..ceb9014 100644
--- a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolverTest.java
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolverTest.java
@@ -97,7 +97,7 @@ public class BasicJWTEncryptionParametersResolverTest {
                 .keyUse(KeyUse.ENCRYPTION)
                 .keyID("mock-key")
                 .generate();
-        config.setKeyTransportEncryptionCredentials(List.of(createKeyEncryptionCredential(key)));
+        config.setKeyTransportEncryptionCredentials(List.of(TestCredentialHelper.createKeyEncryptionCredential(key)));
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
         assertNotNull(param);
@@ -120,7 +120,7 @@ public class BasicJWTEncryptionParametersResolverTest {
                 .algorithm(JWEAlgorithm.ECDH_ES)
                 .keyID("mock-key")
                 .generate();
-        config.setKeyTransportEncryptionCredentials(List.of(createKeyAgreementCredential(key)));
+        config.setKeyTransportEncryptionCredentials(List.of(TestCredentialHelper.createKeyAgreementCredential(key)));
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
         assertNotNull(param);
@@ -138,7 +138,7 @@ public class BasicJWTEncryptionParametersResolverTest {
         final CriteriaSet criteria =  buildBasicCriteriaSet();
         config.setKeyTransportEncryptionAlgorithms(
                 List.of(KeyManagementConstants.ALGO_ID_ALG_AES_256_KW));        
-        config.setKeyTransportEncryptionCredentials(List.of(createSharedSecretCredential("mock-key",
+        config.setKeyTransportEncryptionCredentials(List.of(TestCredentialHelper.createSharedSecretCredential("mock-key",
                 SYMMETRIC_KEY, JWEAlgorithm.A256KW)));
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
@@ -158,7 +158,7 @@ public class BasicJWTEncryptionParametersResolverTest {
         final CriteriaSet criteria =  buildBasicCriteriaSet();
         config.setKeyTransportEncryptionAlgorithms(
                 List.of(KeyManagementConstants.ALGO_ID_ALG_DIR));        
-        config.setDataEncryptionCredentials(List.of(createSharedSecretCredential("mock-key",
+        config.setDataEncryptionCredentials(List.of(TestCredentialHelper.createSharedSecretCredential("mock-key",
                 SYMMETRIC_KEY, JWEAlgorithm.DIR)));
         
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);            
@@ -178,7 +178,7 @@ public class BasicJWTEncryptionParametersResolverTest {
         final CriteriaSet criteria =  buildBasicCriteriaSet();
         config.setKeyTransportEncryptionAlgorithms(
                 List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256, KeyManagementConstants.ALGO_ID_ALG_DIR));        
-        config.setDataEncryptionCredentials(List.of(createSharedSecretCredential("mock-key",
+        config.setDataEncryptionCredentials(List.of(TestCredentialHelper.createSharedSecretCredential("mock-key",
                 SYMMETRIC_KEY, JWEAlgorithm.DIR)));
         
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);            
@@ -197,7 +197,7 @@ public class BasicJWTEncryptionParametersResolverTest {
         final CriteriaSet criteria =  buildBasicCriteriaSet();
         config.setKeyTransportEncryptionAlgorithms(
                 List.of(KeyManagementConstants.ALGO_ID_ALG_DIR));        
-        config.setDataEncryptionCredentials(List.of(createSharedSecretCredential("mock-key",
+        config.setDataEncryptionCredentials(List.of(TestCredentialHelper.createSharedSecretCredential("mock-key",
                 SYMMETRIC_KEY, JWEAlgorithm.DIR)));
         config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256));
         
@@ -216,8 +216,8 @@ public class BasicJWTEncryptionParametersResolverTest {
                 .keyUse(KeyUse.ENCRYPTION)
                 .keyID("mock-key")
                 .generate();
-        config.setKeyTransportEncryptionCredentials(List.of(createKeyEncryptionCredential(key)));
-        config.setDataEncryptionCredentials(List.of(createSharedSecretCredential("mock-key",
+        config.setKeyTransportEncryptionCredentials(List.of(TestCredentialHelper.createKeyEncryptionCredential(key)));
+        config.setDataEncryptionCredentials(List.of(TestCredentialHelper.createSharedSecretCredential("mock-key",
                 SYMMETRIC_KEY, JWEAlgorithm.DIR)));
         config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512));
         
@@ -241,8 +241,8 @@ public class BasicJWTEncryptionParametersResolverTest {
                 .keyUse(KeyUse.ENCRYPTION)
                 .keyID("mock-key")
                 .generate();
-        config.setKeyTransportEncryptionCredentials(List.of(createKeyEncryptionCredential(key)));
-        config.setDataEncryptionCredentials(List.of(createSharedSecretCredential("mock-key",
+        config.setKeyTransportEncryptionCredentials(List.of(TestCredentialHelper.createKeyEncryptionCredential(key)));
+        config.setDataEncryptionCredentials(List.of(TestCredentialHelper.createSharedSecretCredential("mock-key",
                 SYMMETRIC_KEY, JWEAlgorithm.DIR)));
         config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512));
         
@@ -265,7 +265,7 @@ public class BasicJWTEncryptionParametersResolverTest {
                 .keyUse(KeyUse.ENCRYPTION)
                 .keyID("mock-key")
                 .generate();
-        config.setKeyTransportEncryptionCredentials(List.of(createKeyEncryptionCredential(key)));
+        config.setKeyTransportEncryptionCredentials(List.of(TestCredentialHelper.createKeyEncryptionCredential(key)));
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
         assertNull(param);           
@@ -285,8 +285,8 @@ public class BasicJWTEncryptionParametersResolverTest {
                 .keyUse(KeyUse.ENCRYPTION)
                 .keyID("mock-key-correct-type")
                 .generate();
-        config.setKeyTransportEncryptionCredentials(List.of(createKeyEncryptionCredential(key),
-                createKeyEncryptionCredential(keyCorrect)));
+        config.setKeyTransportEncryptionCredentials(List.of(TestCredentialHelper.createKeyEncryptionCredential(key),
+                TestCredentialHelper.createKeyEncryptionCredential(keyCorrect)));
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
         assertNotNull(param);
@@ -309,8 +309,8 @@ public class BasicJWTEncryptionParametersResolverTest {
                 .keyUse(KeyUse.ENCRYPTION)
                 .keyID("mock-key-wrong-type")
                 .generate();        
-        config.setKeyTransportEncryptionCredentials(List.of(createKeyEncryptionCredential(key)));
-        config.setDataEncryptionCredentials(List.of(createSharedSecretCredential("mock-key",
+        config.setKeyTransportEncryptionCredentials(List.of(TestCredentialHelper.createKeyEncryptionCredential(key)));
+        config.setDataEncryptionCredentials(List.of(TestCredentialHelper.createSharedSecretCredential("mock-key",
                 SYMMETRIC_KEY, JWEAlgorithm.A128KW)));
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
@@ -326,75 +326,10 @@ public class BasicJWTEncryptionParametersResolverTest {
                 .keyUse(KeyUse.ENCRYPTION)
                 .keyID("mock-key")
                 .generate();
-        config.setKeyTransportEncryptionCredentials(List.of(createKeyEncryptionCredential(key)));
+        config.setKeyTransportEncryptionCredentials(List.of(TestCredentialHelper.createKeyEncryptionCredential(key)));
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
         assertNull(param);           
     }
-    
-    /**
-     * Create a key encryption {@link JWKCredential} from the given RSA key.
-     * 
-     * @param secret the RSAKey to convert to a {@link JWKCredential}.
-     * 
-     * @return the credential
-     * @throws JOSEException  on error
-     */
-    public JWKCredential createKeyEncryptionCredential(final RSAKey secret) throws JOSEException {
-        final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
-        jwkCredential.setPrivateKey(secret.toPrivateKey());
-        jwkCredential.setPublicKey(secret.toPublicKey());
-        jwkCredential.setCredentialExpiresAt(Duration.ZERO);
-        jwkCredential.setUsageType(UsageType.ENCRYPTION);
-        
-        jwkCredential.setKid(secret.getKeyID());
-        jwkCredential.getKeyNames().add(secret.getKeyID());
-        jwkCredential.setAlgorithm(secret.getAlgorithm());
-        return jwkCredential;
-    }
-    
-    /**
-     * Create a key agreement encryption {@link JWKCredential} from the given EC key.
-     * 
-     * @param secret the ECKey to convert to a {@link JWKCredential}.
-     * 
-     * @return the credential
-     * @throws JOSEException  on error
-     */
-    public JWKCredential createKeyAgreementCredential(final ECKey secret) throws JOSEException {
-        final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
-        jwkCredential.setPrivateKey(secret.toPrivateKey());
-        jwkCredential.setPublicKey(secret.toPublicKey());
-        jwkCredential.setCredentialExpiresAt(Duration.ZERO);
-        jwkCredential.setUsageType(UsageType.ENCRYPTION);
         
-        jwkCredential.setKid(secret.getKeyID());
-        jwkCredential.getKeyNames().add(secret.getKeyID());
-        jwkCredential.setAlgorithm(secret.getAlgorithm());
-        return jwkCredential;
-    }
-    
-    /**
-     * Create a simple symmetric key client credential from from the given shared secret.
-     * 
-     * @param kid the key ID
-     * @param secret the secret to convert to a {@link JWKCredential}.
-     * @param algorithm the JWA algorithm to set on the credential.
-     * 
-     * @return the credential
-     * @throws KeyException on error creating the key
-     */
-    public JWKCredential createSharedSecretCredential(final String kid, final String secret, 
-            final Algorithm algorithm) 
-            throws KeyException {
-        final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
-        jwkCredential.setSecretKey(KeySupport.decodeSecretKey(JWSAssemblyUtils.getSecretBytes(secret), "AES"));
-        jwkCredential.setCredentialExpiresAt(Duration.ZERO);
-        jwkCredential.setUsageType(UsageType.UNSPECIFIED);
-        jwkCredential.setKid(kid);
-        jwkCredential.setAlgorithm(algorithm);
-        jwkCredential.getKeyNames().add("mockKey");
-        return jwkCredential;
-    }
-    
 }
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ProviderMetadataEncryptionParametersResolverTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ProviderMetadataEncryptionParametersResolverTest.java
new file mode 100644
index 0000000..df2440a
--- /dev/null
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ProviderMetadataEncryptionParametersResolverTest.java
@@ -0,0 +1,407 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.security.impl;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.protocol.HttpContext;
+import org.mockito.Mockito;
+import org.opensaml.core.config.InitializationException;
+import org.opensaml.storage.StorageService;
+import org.opensaml.storage.impl.MemoryStorageService;
+import org.opensaml.xmlsec.config.GlobalAlgorithmRegistryInitializer;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.util.FileCopyUtils;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.jwk.KeyUse;
+import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.jwa.support.EncryptionConstants;
+import net.shibboleth.oidc.jwa.support.KeyManagementConstants;
+import net.shibboleth.oidc.jwk.RemoteJwkSetCache;
+import net.shibboleth.oidc.security.JWTEncryptionParameters;
+import net.shibboleth.oidc.security.credential.JWKCredential;
+import net.shibboleth.oidc.security.criterion.JWTEncryptionConfigurationCriterion;
+import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
+import net.shibboleth.oidc.security.criterion.StaticCredentialCriterion;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+/** Tests for the {@link ProviderMetadataEncryptionParametersResolver}.*/
+public class ProviderMetadataEncryptionParametersResolverTest {
+    
+    /**
+     * Example of good provider metadata that supports request_object_encryption.
+     */
+    private static final ClassPathResource GOOD_PROVIDER_CONFIGURATION_INFO = 
+            new ClassPathResource("/metadata/test-resolver-provider-encryption.json");
+    
+    /** A remote JWKSet.*/
+    private static final ClassPathResource REMOTE_JWKSET = 
+            new ClassPathResource("/conf/credentials/test-provider-resolver-remote-jwkset-response.jwk");    
+    
+    /** The client_secret.*/
+    private static final String CLIENT_SECRET = "Xp2s5v8y/B?E(H+MbQeThWmYq3t6w9z$";
+    
+    /** The mock symmetric key e.g. for keywrap.*/
+    private static final String SYMMETRIC_KEY = "/A?D(G+KbPdSgVkYp3s6v9y$B&E)H at Mc";
+    
+    /** A second mock symmetric key e.g. for keywrap.*/
+    private static final String SYMMETRIC_KEY_TWO = "/ArB(G+KbPdSgVkYp3s6v9y$B&E)H at Mc";
+    
+    /** The resolver to test.*/
+    private ProviderMetadataEncryptionParametersResolver resolver;
+    
+    /** The basic config.*/
+    private BasicJWTEncryptionConfiguration config;
+    
+    /**
+     * Read a file into a string.
+     * 
+     * @param location the location of the file to read
+     * 
+     * @return the file as a string
+     */
+    private String readJsonFromFile(@Nonnull final Resource location) {
+        try (Reader reader = new InputStreamReader(location.getInputStream(), StandardCharsets.UTF_8)) {
+            return FileCopyUtils.copyToString(reader);
+        } catch (final Exception ex) {
+            fail();
+            return null;
+        }
+    }
+    
+
+    @BeforeMethod
+    public void setup() throws ComponentInitializationException, ClientProtocolException, IOException {
+        //Create an algorithm registry here, as opensaml init will not take place for these tests      
+        try {
+            final GlobalAlgorithmRegistryInitializer gar = new GlobalAlgorithmRegistryInitializer();
+            gar.init();
+        } catch (final InitializationException e) {
+            fail();
+        }
+        
+        resolver = new ProviderMetadataEncryptionParametersResolver();
+        resolver.setProviderEncryptionMethodsLookupStrategy(OIDCProviderMetadata::getRequestObjectJWEEncs);
+        resolver.setProviderKeyTransportAlgorithmsLookupStrategy(OIDCProviderMetadata::getRequestObjectJWEAlgs);
+        final RemoteJwkSetCache cache = new RemoteJwkSetCache();
+        cache.setStorage(buildStorageService());
+        cache.setHttpClient(createMockHttpClient(readJsonFromFile(REMOTE_JWKSET)));
+        resolver.setRemoteJwkSetCache(cache);
+    }
+    
+    protected HttpClient createMockHttpClient(final String output) throws ClientProtocolException, IOException {
+        final HttpClient httpClient = Mockito.mock(HttpClient.class);
+        final HttpResponse httpResponse = Mockito.mock(HttpResponse.class);
+        Mockito.when(httpResponse.getEntity()).thenReturn(new StringEntity(output));
+        Mockito.when(httpClient.execute((HttpUriRequest) Mockito.any(), 
+                (HttpContext) Mockito.any())).thenReturn(httpResponse);
+        return httpClient;
+    }
+    
+    private StorageService buildStorageService() throws ComponentInitializationException {
+        final MemoryStorageService storageService = new MemoryStorageService();
+        storageService.setId("mockId");
+        storageService.initialize();
+        return storageService;
+    }
+    
+    private CriteriaSet buildBasicCriteriaSet() throws Exception {        
+        
+        config = new BasicJWTEncryptionConfiguration();
+        config.setKeyTransportEncryptionAlgorithms(
+                List.of(KeyManagementConstants.ALGO_ID_ALG_AES_128_KW, KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256,
+                KeyManagementConstants.ALGO_ID_ALG_ECDH_ES_AES_192_KW, KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP));
+        config.setDataEncryptionAlgorithms(
+                List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256,EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM,
+                        EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512));
+        final CriteriaSet criteria = new CriteriaSet(new JWTEncryptionConfigurationCriterion(List.of(config)));
+        criteria.add(new ProviderMetadataCriterion(
+                OIDCProviderMetadata.parse(readJsonFromFile(GOOD_PROVIDER_CONFIGURATION_INFO))));
+        criteria.add(
+                new StaticCredentialCriterion(TestCredentialHelper.createClientSecretCredential(CLIENT_SECRET)));
+        return criteria;
+    }
+    
+    @Test
+    public void testSuccessfulResolution() throws Exception {
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(buildBasicCriteriaSet());
+        assertNotNull(params);
+        assertTrue(params.iterator().hasNext());
+        final JWTEncryptionParameters param = params.iterator().next();
+        assertNotNull(param.getDataEncryptionAlgorithm());
+        assertNotNull(param.getKeyTransportEncryptionAlgorithm()); 
+        assertNotNull(param.getKeyTransportEncryptionCredential());
+        assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+    }
+    
+    /* Algorithms are know because they are limited by config.*/
+    @Test
+    public void testSuccessfulResolution_ForKeyEncryption() throws Exception {
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP));
+        config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512));
+        
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(criteria);
+        assertNotNull(params);
+        assertTrue(params.iterator().hasNext());
+        final JWTEncryptionParameters param = params.iterator().next();
+        assertNotNull(param.getDataEncryptionAlgorithm());
+        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP);
+        assertNotNull(param.getKeyTransportEncryptionCredential());
+        assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+    }
+    
+    /* Should chose key encryption creds as they are the only ones configured, and are first
+     * in the algorithm list*/
+    @Test
+    public void testSuccessfulResolution_ForKeyEncryption_WhenKeyWrapPossible() throws Exception {
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP, 
+                KeyManagementConstants.ALGO_ID_ALG_AES_128_KW));
+        config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512));
+        
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(criteria);
+        assertNotNull(params);
+        assertTrue(params.iterator().hasNext());
+        final JWTEncryptionParameters param = params.iterator().next();
+        assertNotNull(param.getDataEncryptionAlgorithm());
+        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP);
+        assertNotNull(param.getKeyTransportEncryptionCredential());
+        assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+    }
+    
+    /* Should chose key wrap creds first, as that algorithm is first in the list.*/
+    @Test
+    public void testSuccessfulResolution_ForKeyWrap_WhenKeyEncryptionPossible() throws Exception {
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_AES_256_KW,
+                KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP));
+        config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512));
+        config.setKeyTransportEncryptionCredentials(
+                List.of(TestCredentialHelper.createClientSecretCredential("mockKey", 
+                        SYMMETRIC_KEY, JWEAlgorithm.A256KW)));
+        
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(criteria);
+        assertNotNull(params);
+        assertTrue(params.iterator().hasNext());
+        final JWTEncryptionParameters param = params.iterator().next();
+        assertNotNull(param.getDataEncryptionAlgorithm());
+        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_AES_256_KW);
+        assertNotNull(param.getKeyTransportEncryptionCredential());
+        assertNotNull(param.getKeyTransportEncryptionCredential().getSecretKey());
+    }
+    
+    @Test
+    public void testSuccessfulResolution_ForKeyWrap() throws Exception {
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_AES_256_KW));
+        config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM));
+        config.setKeyTransportEncryptionCredentials(
+                List.of(TestCredentialHelper.createClientSecretCredential("mockKey", SYMMETRIC_KEY, 
+                        JWEAlgorithm.A256KW)));
+        
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(criteria);
+        assertNotNull(params);
+        assertTrue(params.iterator().hasNext());
+        final JWTEncryptionParameters param = params.iterator().next();
+        assertNotNull(param.getDataEncryptionAlgorithm());
+        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_AES_256_KW);
+        assertNotNull(param.getKeyTransportEncryptionCredential());
+        assertNotNull(param.getKeyTransportEncryptionCredential().getSecretKey());
+    }
+    
+    /* Runtime does not yet support 'dir' key transport, so disabled for now.*/
+    @Test(enabled = false)
+    public void testSuccessfulResolution_ForDirectEncryption() throws Exception {
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_DIR));
+        config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A256GCM));
+        config.setDataEncryptionCredentials(
+                List.of(TestCredentialHelper.createClientSecretCredential("mockKey", SYMMETRIC_KEY, 
+                        JWEAlgorithm.DIR)));
+        
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(criteria);
+        assertNotNull(params);
+        assertTrue(params.iterator().hasNext());
+        final JWTEncryptionParameters param = params.iterator().next();
+        assertNotNull(param.getDataEncryptionAlgorithm());
+        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A256GCM);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_DIR);
+        assertNull(param.getKeyTransportEncryptionCredential());
+        assertNotNull(param.getDataEncryptionCredential());
+        assertNotNull(param.getDataEncryptionCredential().getSecretKey());
+        assertTrue(param.getDataEncryptionCredential().getKeyNames().contains("mockKey"));
+    }
+    
+    /* 
+     * The local RSA key should be derived from the local config even if provider metadata is excluded from 
+     * the criteria set.
+     */
+    @Test
+    public void testSuccessfulResolution_NoProviderMetadata_FallBackToLocalBehaviour() throws Exception {
+        // Do not add ProviderMetadataCriterion
+        buildBasicCriteriaSet();
+        final CriteriaSet criteria = new CriteriaSet(new JWTEncryptionConfigurationCriterion(List.of(config)));
+        
+        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256));
+        config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM));
+        
+        final RSAKey key = new RSAKeyGenerator(2048)
+                .algorithm(JWEAlgorithm.RSA_OAEP_256)
+                .keyUse(KeyUse.ENCRYPTION)
+                .keyID("mock-key")
+                .generate();
+        
+        config.setKeyTransportEncryptionCredentials(
+                List.of(TestCredentialHelper.createKeyEncryptionCredential(key)));
+        
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(criteria);
+        assertNotNull(params);
+        assertTrue(params.iterator().hasNext());
+        final JWTEncryptionParameters param = params.iterator().next();
+        assertNotNull(param.getDataEncryptionAlgorithm());
+        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256);
+        assertNotNull(param.getKeyTransportEncryptionCredential());
+        assertTrue(param.getKeyTransportEncryptionCredential().getKeyNames().contains("mock-key"));
+        assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+    }
+    
+    /* The first mockKey in the list should be resolved.*/
+    @Test
+    public void testSuccessfulResolution_ForKeyWrap_MoreThanOneKeyWrapCred() throws Exception {
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_AES_256_KW));
+        config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM));
+        config.setKeyTransportEncryptionCredentials(
+                List.of(TestCredentialHelper.createClientSecretCredential("mockKey", SYMMETRIC_KEY, 
+                        JWEAlgorithm.A256KW),
+                        TestCredentialHelper.createClientSecretCredential("mockKeyTwo", SYMMETRIC_KEY_TWO, 
+                                JWEAlgorithm.A256KW)));
+        
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(criteria);
+        assertNotNull(params);
+        assertTrue(params.iterator().hasNext());
+        final JWTEncryptionParameters param = params.iterator().next();
+        assertNotNull(param.getDataEncryptionAlgorithm());
+        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_AES_256_KW);
+        // TODO this is not guaranteed, so should we be checking it?
+        assertEquals(((JWKCredential)param.getKeyTransportEncryptionCredential()).getKid(),"mockKey");
+        assertNotNull(param.getKeyTransportEncryptionCredential());
+        assertNotNull(param.getKeyTransportEncryptionCredential().getSecretKey());
+    }
+    
+    /* Do not provide a symmetric key in the params.*/
+    @Test
+    public void testUnSuccessfulResolution_ForKeyWrap() throws Exception {
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_AES_256_KW));
+        config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM));
+        
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(criteria);
+        assertNotNull(params);
+        assertFalse(params.iterator().hasNext());    
+    }
+    
+    @Test
+    public void testSuccessfulResolution_ForKeyAgreement() throws Exception {
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_ECDH_ES));
+        config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM));
+        
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(criteria);
+        assertNotNull(params);
+        assertTrue(params.iterator().hasNext());
+        final JWTEncryptionParameters param = params.iterator().next();
+        assertNotNull(param.getDataEncryptionAlgorithm());
+        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_ECDH_ES);
+        assertNotNull(param.getKeyTransportEncryptionCredential());
+        assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+    }
+    
+    @Test
+    public void testUnSuccessfulResolution_NoSupportedKeyTransportAlgorithm() throws Exception {
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(List.of("NOT-SUPPORTED"));
+        
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(criteria);
+        assertNotNull(params);
+        assertFalse(params.iterator().hasNext());
+    }
+    
+    @Test
+    public void testUnSuccessfulResolution_OnlyConfigCriterion() throws Exception {
+        buildBasicCriteriaSet();
+        final CriteriaSet criteria =  new CriteriaSet(new JWTEncryptionConfigurationCriterion(List.of(config)));
+        
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(criteria);
+        assertNotNull(params);
+        assertFalse(params.iterator().hasNext());
+    }
+    
+    @Test
+    public void testUnSuccessfulResolution_NoSupportedDataEncryptionMethod() throws Exception {
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setDataEncryptionAlgorithms(List.of("NOT-SUPPORTED"));
+        
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(criteria);
+        assertNotNull(params);
+        assertFalse(params.iterator().hasNext());
+    }
+
+}
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/TestCredentialHelper.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/TestCredentialHelper.java
new file mode 100644
index 0000000..3a50207
--- /dev/null
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/TestCredentialHelper.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package net.shibboleth.oidc.security.impl;
+
+import java.security.KeyException;
+import java.time.Duration;
+
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.security.crypto.KeySupport;
+
+import com.nimbusds.jose.Algorithm;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.jwk.AsymmetricJWK;
+import com.nimbusds.jose.jwk.ECKey;
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.RSAKey;
+
+import net.shibboleth.oidc.security.credential.BasicExpiringJWKCredential;
+import net.shibboleth.oidc.security.credential.JWKCredential;
+
+/** Helper that creates different credentials.*/
+public final class TestCredentialHelper {
+    
+    private TestCredentialHelper() {
+        
+    }
+    
+    /**
+     * Create a simple symmetric key client credential from from the given shared secret.
+     * 
+     * @param secret the secret to convert to a {@link JWKCredential}.
+     * 
+     * @return the credential
+     * @throws KeyException on error creating the key
+     */
+    public static JWKCredential createClientSecretCredential(final String secret) throws KeyException {        
+        return createClientSecretCredential("mockKey", secret, null);
+    }
+    
+    /**
+     * Create a simple symmetric key client credential from from the given shared secret.
+     * 
+     * @param kid the key ID
+     * @param secret the secret to convert to a {@link JWKCredential}.
+     * @param algorithm the JWA algorithm to set on the credential.
+     * 
+     * @return the credential
+     * @throws KeyException on error creating the key
+     */
+    public static JWKCredential createClientSecretCredential(final String kid, final String secret, 
+            final Algorithm algorithm) 
+            throws KeyException {
+        return createSharedSecretCredential(kid, secret, algorithm);
+    }
+    
+    /**
+     * Create a simple symmetric key client credential from from the given shared secret.
+     * 
+     * @param kid the key ID
+     * @param secret the secret to convert to a {@link JWKCredential}.
+     * @param algorithm the JWA algorithm to set on the credential.
+     * 
+     * @return the credential
+     * @throws KeyException on error creating the key
+     */
+    public static JWKCredential createSharedSecretCredential(final String kid, final String secret, 
+            final Algorithm algorithm) 
+            throws KeyException {
+        final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+        jwkCredential.setSecretKey(KeySupport.decodeSecretKey(JWSAssemblyUtils.getSecretBytes(secret), "AES"));
+        jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+        jwkCredential.setUsageType(UsageType.UNSPECIFIED);
+        jwkCredential.setKid(kid);
+        jwkCredential.setAlgorithm(algorithm);
+        jwkCredential.getKeyNames().add("mockKey");
+        return jwkCredential;
+    }
+    
+    /**
+     * Create a direct encryption {@link JWKCredential} from the given shared secret.
+     * 
+     * @param secret the secret to convert to a {@link JWKCredential}.
+     * 
+     * @return the credential
+     * @throws KeyException on error creating the key
+     */
+    public static JWKCredential createDirectEncryptionCredentialFromSharedSecret(final String secret) 
+            throws KeyException {
+        final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+        jwkCredential.setSecretKey(KeySupport.decodeSecretKey(
+                    JWSAssemblyUtils.getSecretBytes(secret), "AES"));
+        jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+        jwkCredential.setUsageType(UsageType.UNSPECIFIED);
+        
+        jwkCredential.setKid("mockKey");
+        jwkCredential.getKeyNames().add("mockKey");
+        jwkCredential.setAlgorithm(JWEAlgorithm.DIR);
+        return jwkCredential;
+    }
+    
+    /**
+     * Create an asymmetric signing credential.
+     * 
+     * @param key the key to convert to a {@link JWKCredential}.
+     * 
+     * @return the credential
+     * @throws JOSEException on error
+     */
+    public static JWKCredential createAsymmetricSigningCredential(final AsymmetricJWK key) throws JOSEException {
+        final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+        jwkCredential.setPrivateKey(key.toPrivateKey());
+        jwkCredential.setPublicKey(key.toPublicKey());
+        jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+        jwkCredential.setUsageType(UsageType.SIGNING);
+        
+        jwkCredential.setKid(((JWK)key).getKeyID());
+        jwkCredential.getKeyNames().add(((JWK)key).getKeyID());
+        jwkCredential.setAlgorithm(((JWK)key).getAlgorithm());
+        return jwkCredential;
+    }
+    
+    
+    /**
+     * Create a key encryption {@link JWKCredential} from the given RSA key.
+     * 
+     * @param secret the RSAKey to convert to a {@link JWKCredential}.
+     * 
+     * @return the credential
+     * @throws JOSEException  on error
+     */
+    public static JWKCredential createKeyEncryptionCredential(final RSAKey secret) throws JOSEException {
+        final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+        jwkCredential.setPrivateKey(secret.toPrivateKey());
+        jwkCredential.setPublicKey(secret.toPublicKey());
+        jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+        jwkCredential.setUsageType(UsageType.ENCRYPTION);
+        
+        jwkCredential.setKid(secret.getKeyID());
+        jwkCredential.getKeyNames().add(secret.getKeyID());
+        jwkCredential.setAlgorithm(secret.getAlgorithm());
+        return jwkCredential;
+    }
+    
+    /**
+     * Create a key agreement encryption {@link JWKCredential} from the given EC key.
+     * 
+     * @param secret the ECKey to convert to a {@link JWKCredential}.
+     * 
+     * @return the credential
+     * @throws JOSEException  on error
+     */
+    public static JWKCredential createKeyAgreementCredential(final ECKey secret) throws JOSEException {
+        final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+        jwkCredential.setPrivateKey(secret.toPrivateKey());
+        jwkCredential.setPublicKey(secret.toPublicKey());
+        jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+        jwkCredential.setUsageType(UsageType.ENCRYPTION);
+        
+        jwkCredential.setKid(secret.getKeyID());
+        jwkCredential.getKeyNames().add(secret.getKeyID());
+        jwkCredential.setAlgorithm(secret.getAlgorithm());
+        return jwkCredential;
+    }
+
+}

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


More information about the commits mailing list