[java-oidc-common] branch dev/JCOMOIDC-41 updated: JOIDCRP-17 - Add JWT Encryption Parameter Resolver Support

Phil Smart philip.smart at jisc.ac.uk
Mon Sep 5 15:45:41 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=60565d11c7226c75c440dafa8bc63e13313a9dbb

The following commit(s) were added to refs/heads/dev/JCOMOIDC-41 by this push:
     new 60565d1  JOIDCRP-17 - Add JWT Encryption Parameter Resolver Support
60565d1 is described below

commit 60565d11c7226c75c440dafa8bc63e13313a9dbb
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Mon Sep 5 16:45:34 2022 +0100

    JOIDCRP-17 - Add JWT Encryption Parameter Resolver Support
    
     - Change encryption parameter resolver to use various algorithm lookup
    strategies and credential resolvers.
    
    https://shibboleth.atlassian.net/browse/JOIDCRP-17
---
 .../oidc/security/JWTEncryptionConfiguration.java  |  47 +-
 ...va => JWTDecryptionConfigurationCriterion.java} |  10 +-
 .../criterion/ProviderMetadataCriterion.java       |   2 -
 .../criterion/StaticCredentialCriterion.java       |   1 -
 .../impl/CriterionCredentialResolver.java          |  19 +-
 .../impl/ProviderMetadataCredentialResolver.java   |  96 ++--
 ...AbstractEncryptionAlgorithmsLookupStrategy.java |  93 ++++
 .../impl/BasicJWTEncryptionConfiguration.java      |  71 ++-
 .../security/impl/CredentialConversionUtil.java    |  51 ++-
 ...aultDataEncryptionAlgorithmsLookupStrategy.java |  79 ++++
 .../DefaultJWTDecryptionParametersResolver.java    |  27 +-
 ...=> DefaultJWTEncryptionParametersResolver.java} | 450 +++++++++++--------
 ...ransportEncryptionAlgorithmsLookupStrategy.java |  84 ++++
 ...dataDataEncryptionAlgorithmsLookupStrategy.java | 137 ++++++
 ...oviderMetadataEncryptionParametersResolver.java | 414 -----------------
 ...ransportEncryptionAlgorithmsLookupStrategy.java | 138 ++++++
 .../ProviderMetadataCredentialResolverTest.java    | 220 +++++++++
 ...DataEncryptionAlgorithmsLookupStrategyTest.java |  96 ++++
 ...efaultJWTEncryptionParametersResolverTest.java} | 425 +++++++++++-------
 ...portEncryptionAlgorithmsLookupStrategyTest.java | 103 +++++
 ...DataEncryptionAlgorithmsLookupStrategyTest.java | 161 +++++++
 ...erMetadataEncryptionParametersResolverTest.java | 496 ---------------------
 ...portEncryptionAlgorithmsLookupStrategyTest.java | 161 +++++++
 .../oidc/security/impl/TestCredentialHelper.java   |   8 +-
 ...esolver-remote-jwkset-response-no-key-array.jwk |   3 +
 ...r-resolver-remote-jwkset-response-no-keyuse.jwk |  63 ---
 ...ider-resolver-remote-jwkset-response-nokeys.jwk |   5 +
 ...solver-remote-jwkset-response-wrong-key-use.jwk |  64 ---
 .../test-resolver-provider-encryption.json         |   5 +-
 .../impl/DateBasedJWTClaimAuditExtractor.java      |   4 +
 30 files changed, 2010 insertions(+), 1523 deletions(-)

diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTEncryptionConfiguration.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTEncryptionConfiguration.java
index 53ca21e..c1528f4 100644
--- a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTEncryptionConfiguration.java
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTEncryptionConfiguration.java
@@ -1,44 +1,65 @@
+/*
+ * 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;
 
 import java.util.List;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
-import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.CredentialResolver;
 import org.opensaml.xmlsec.AlgorithmPolicyConfiguration;
 
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
 import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
 
+/**
+ * The configuration information to use when generating an encrypted JWT.
+ */
 public interface JWTEncryptionConfiguration extends AlgorithmPolicyConfiguration {
 
     /**
-     * Get the list of data encryption credentials to use, in preference order.
+     * Get the list of preferred data encryption algorithm URIs, in preference order.
      * 
-     * @return the list of encryption credentials, may be empty
+     * @return the list of algorithm URIs, may be empty
      */
-    @Nonnull @NonnullElements @Unmodifiable @NotLive public List<Credential> getDataEncryptionCredentials();
-    
+    @Nonnull @NonnullElements @Unmodifiable @NotLive public List<String> getDataEncryptionAlgorithms();
+        
     /**
-     * Get the list of preferred data encryption algorithm URIs, in preference order.
+     * Get the list of preferred key transport encryption algorithm URIs, in preference order.
      * 
      * @return the list of algorithm URIs, may be empty
      */
-    @Nonnull @NonnullElements @Unmodifiable @NotLive public List<String> getDataEncryptionAlgorithms();
+    @Nonnull @NonnullElements @Unmodifiable @NotLive public List<String> getKeyTransportEncryptionAlgorithms();
     
     /**
-     * Get the list of key transport encryption credentials to use, in preference order.
+     * Get the CredentialResolver resolve the content encryption key.
      * 
-     * @return the list of encryption credentials, may be empty
+     * @return the KeyInfoCredentialResolver instance
      */
-    @Nonnull @NonnullElements @Unmodifiable @NotLive public List<Credential> getKeyTransportEncryptionCredentials();
+    @Nullable CredentialResolver getContentEncryptionKeyCredentialResolver();
     
     /**
-     * Get the list of preferred key transport encryption algorithm URIs, in preference order.
+     * Get the CredentialResolver to resolve the key encryption or key wrapping key.
      * 
-     * @return the list of algorithm URIs, may be empty
+     * @return the CredentialResolver instance
      */
-    @Nonnull @NonnullElements @Unmodifiable @NotLive public List<String> getKeyTransportEncryptionAlgorithms();
+    @Nullable CredentialResolver getKEKCredentialResolver();
 
 }
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/DecryptionConfigurationCriterion.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/JWTDecryptionConfigurationCriterion.java
similarity index 89%
rename from oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/DecryptionConfigurationCriterion.java
rename to oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/JWTDecryptionConfigurationCriterion.java
index 368c588..e021023 100644
--- a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/DecryptionConfigurationCriterion.java
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/JWTDecryptionConfigurationCriterion.java
@@ -32,7 +32,7 @@ import net.shibboleth.utilities.java.support.resolver.Criterion;
 /**
  * Criterion which holds one or more instances of {@link JWTDecryptionConfiguration}.
  */
-public class DecryptionConfigurationCriterion implements Criterion {
+public class JWTDecryptionConfigurationCriterion implements Criterion {
     
     /** The list of configuration instances. */
     @Nonnull @NonnullElements private final List<JWTDecryptionConfiguration> configs;
@@ -42,7 +42,7 @@ public class DecryptionConfigurationCriterion implements Criterion {
      *
      * @param configurations list of configuration instances
      */
-    public DecryptionConfigurationCriterion(@Nonnull @NonnullElements @NotEmpty final
+    public JWTDecryptionConfigurationCriterion(@Nonnull @NonnullElements @NotEmpty final
             List<JWTDecryptionConfiguration> configurations) {
         configs = List.copyOf(Constraint.isNotNull(configurations, "List of configurations cannot be null"));
         Constraint.isNotEmpty(configs, "At least one configuration is required");
@@ -54,7 +54,7 @@ public class DecryptionConfigurationCriterion implements Criterion {
      *
      * @param configurations varargs array of configuration instances
      */
-    public DecryptionConfigurationCriterion(@Nonnull @NonnullElements @NotEmpty final
+    public JWTDecryptionConfigurationCriterion(@Nonnull @NonnullElements @NotEmpty final
             JWTDecryptionConfiguration... configurations) {
         configs = List.of(Constraint.isNotNull(configurations, "List of configurations cannot be null"));
         Constraint.isNotEmpty(configs, "At least one configuration is required");
@@ -97,8 +97,8 @@ public class DecryptionConfigurationCriterion implements Criterion {
             return false;
         }
 
-        if (obj instanceof DecryptionConfigurationCriterion) {
-            return configs.equals(((DecryptionConfigurationCriterion) obj).getConfigurations());
+        if (obj instanceof JWTDecryptionConfigurationCriterion) {
+            return configs.equals(((JWTDecryptionConfigurationCriterion) obj).getConfigurations());
         }
 
         return false;
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/ProviderMetadataCriterion.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/ProviderMetadataCriterion.java
index 1a3359d..e0adc18 100644
--- a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/ProviderMetadataCriterion.java
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/ProviderMetadataCriterion.java
@@ -19,8 +19,6 @@ package net.shibboleth.oidc.security.criterion;
 
 import javax.annotation.Nonnull;
 
-import org.opensaml.saml.criterion.RoleDescriptorCriterion;
-
 import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
 
 import net.shibboleth.utilities.java.support.logic.Constraint;
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/StaticCredentialCriterion.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/StaticCredentialCriterion.java
index 4ccafa5..5f638da 100644
--- a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/StaticCredentialCriterion.java
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/StaticCredentialCriterion.java
@@ -2,7 +2,6 @@ package net.shibboleth.oidc.security.criterion;
 
 import javax.annotation.Nonnull;
 
-import org.opensaml.saml.criterion.RoleDescriptorCriterion;
 import org.opensaml.security.credential.Credential;
 
 import net.shibboleth.utilities.java.support.logic.Constraint;
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/CriterionCredentialResolver.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/CriterionCredentialResolver.java
index 64b7db0..9a8eaa2 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/CriterionCredentialResolver.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/CriterionCredentialResolver.java
@@ -26,28 +26,37 @@ import org.opensaml.security.credential.Credential;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import net.shibboleth.oidc.security.credential.JWKCredential;
 import net.shibboleth.oidc.security.criterion.StaticCredentialCriterion;
+import net.shibboleth.oidc.security.impl.CredentialConversionUtil;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
 import net.shibboleth.utilities.java.support.resolver.ResolverException;
 
 /**
- * Extracts the credential from the {@link StaticCredentialCriterion} inside the given criteria set.
+ * Extracts a credential held inside the {@link StaticCredentialCriterion} from the given criteria set.
  */
-//TODO Docs
 public class CriterionCredentialResolver extends BasicJOSEObjectCredentialResolver {
     
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(CriterionCredentialResolver.class);
     
     @Override
-    protected Iterable<Credential> resolveFromSource(@Nonnull final CriteriaSet criteriaSet) throws ResolverException {        
+    @Nonnull protected Iterable<Credential> resolveFromSource(@Nonnull final CriteriaSet criteriaSet) 
+            throws ResolverException {        
         Constraint.isNotNull(criteriaSet, "CriteriaSet was null");       
         
         if (criteriaSet.contains(StaticCredentialCriterion.class)) {
             final StaticCredentialCriterion credentialCriterion = criteriaSet.get(StaticCredentialCriterion.class);
-            final Credential credential = credentialCriterion.getCredential();          
-            log.debug("Extracted key '{}', from criterion", credential.getKeyNames());
+            final Credential credential = credentialCriterion.getCredential();      
+            if (credential instanceof JWKCredential && log.isDebugEnabled()) {
+                log.debug("Found credential '{}' with usage '{}' and alg '{}' from criterion", 
+                        CredentialConversionUtil.resolveKid(credential),
+                        credential.getUsageType(), ((JWKCredential)credential).getAlgorithm());
+            } else {
+                log.debug("Found credential '{}' with usage '{}' from criterion", 
+                        credential.getKeyNames(), credential.getUsageType());
+            }
             return List.of(credential);               
             
         } else {
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 ec50a34..936480d 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
@@ -20,31 +20,26 @@ package net.shibboleth.oidc.security.credential.impl;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.LinkedHashSet;
 
 import javax.annotation.Nonnull;
 
 import org.opensaml.security.credential.Credential;
-import org.opensaml.security.credential.UsageType;
 import org.opensaml.security.credential.impl.AbstractCriteriaFilteringCredentialResolver;
-import org.opensaml.security.criteria.KeyAlgorithmCriterion;
-import org.opensaml.security.criteria.UsageCriterion;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.nimbusds.jose.JOSEException;
-import com.nimbusds.jose.jwk.AsymmetricJWK;
 import com.nimbusds.jose.jwk.ECKey;
 import com.nimbusds.jose.jwk.JWK;
 import com.nimbusds.jose.jwk.JWKSet;
-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.credential.BasicJWKCredential;
-import net.shibboleth.oidc.security.credential.JOSEObjectCredentialResolver;
 import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
+import net.shibboleth.oidc.security.impl.CredentialConversionUtil;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.utilities.java.support.annotation.constraint.Positive;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
@@ -54,9 +49,14 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
 import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
 import net.shibboleth.utilities.java.support.resolver.ResolverException;
 
-
-//TODO Docs
-//TODO Algorithm filters?
+/**
+ * A {@link LocalJOSEObjectCredentialResolver} that resolves credentials from jwks_uri of an
+ * OpenID Provider. Further filtering of credentials is provided by the {@link AbstractCriteriaFilteringCredentialResolver}
+ * parent class.
+ * 
+ * <p>Note, only RSA or EC keys are resolved as these are the only key types which should be exposed in public JWKS 
+ * documents.</p>
+ */
 public class ProviderMetadataCredentialResolver extends BasicJOSEObjectCredentialResolver 
         implements InitializableComponent {
     
@@ -113,11 +113,10 @@ public class ProviderMetadataCredentialResolver extends BasicJOSEObjectCredentia
         
         ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
         Constraint.isNotNull(criteriaSet, "CriteriaSet was null");
-        
-        final UsageType usage = getEffectiveUsageInput(criteriaSet);
+
         if (criteriaSet.contains(ProviderMetadataCriterion.class)) {
             final OIDCProviderMetadata metadata = criteriaSet.get(ProviderMetadataCriterion.class).getMetadata();
-            return resolveFromMetadata(criteriaSet, usage, metadata);
+            return resolveFromMetadata(criteriaSet, metadata);
             
         } else {
             throw new ResolverException("Criteria did not contain a ProviderMetadataCriterion " 
@@ -126,7 +125,7 @@ public class ProviderMetadataCredentialResolver extends BasicJOSEObjectCredentia
     }
     
     @Nonnull protected Collection<Credential> resolveFromMetadata(@Nonnull final CriteriaSet criteriaSet, 
-            @Nonnull final UsageType usage, @Nonnull final OIDCProviderMetadata metadata) {
+            @Nonnull final OIDCProviderMetadata metadata) {
         
         final LinkedHashSet<Credential> credentials = new LinkedHashSet<>(1);
         
@@ -134,66 +133,31 @@ public class ProviderMetadataCredentialResolver extends BasicJOSEObjectCredentia
             final JWKSet keySet = remoteJwkSetCache.fetch(metadata.getJWKSetURI(),
                     Instant.now().plus(keyFetchInterval));
             
-            for (final JWK key : keySet.getKeys()) {
-                if (!matchUsage(key.getKeyUse(), usage)) {
-                    continue;
-                }
-                // Check against supported algo's here? although OP should only have keys for algo's it supports?
-                //TODO needs to support the JWA alg names first?
-                final KeyAlgorithmCriterion keyAlg = criteriaSet.get(KeyAlgorithmCriterion.class);
-                
+            if (keySet == null) {
+                log.debug("Remote keys could not be fetched, unable to resolve credentials");
+                return Collections.emptyList();
+            }
+            
+            //TODO maybe the remote cache could cache the converted keys?
+            for (final JWK key : keySet.getKeys()) {       
+                // Only RSA or EC public keys supplied by an OP
                 if (key instanceof RSAKey || key instanceof ECKey) {
-                    final BasicJWKCredential jwkCredential = new BasicJWKCredential();
-                    jwkCredential.setAlgorithm(key.getAlgorithm());
-                    jwkCredential.setKid(key.getKeyID());
-                    jwkCredential.getKeyNames().add(key.getKeyID());
                     try {
-                        jwkCredential.setPublicKey(((AsymmetricJWK) key).toPublicKey());
+                        final Credential cred = CredentialConversionUtil.keyToCredential(key);
+                        if (cred != null) {
+                            log.trace("Found key '{}' of type '{}' with usage '{}' and alg '{}'", 
+                                    key.getKeyID(), key.getKeyType(), key.getKeyUse(), key.getAlgorithm());
+                            credentials.add(cred); 
+                        }
                     } catch (final JOSEException e) {
-                        log.warn("Unable to parse key '{}' from keyset", key.getKeyID());
-                        continue;
-                    }
-                    log.debug("Selected key '{}', of type '{}', matched to alg '{}'", 
-                            key.getKeyID(), key.getKeyType(), keyAlg);
-                    credentials.add(jwkCredential);
+                        log.trace("Unable to convert key '{}' to credential", key.getKeyID(), e);
+                    }                    
                 }                
             }            
         } else {
-            log.debug("No JWK Set available, no credentials returned");
+            log.trace("No JWK Set available, no credentials returned");
         }
         return credentials;
    
     }
-    
-    /**
-     * Match the JWK key usage matches that in the criteria set.
-     * 
-     * @param metadataUsage the value from the 'use' parameter of the JWK.
-     * @param criteriaUsage the value from specified criteria
-     * @return true if the two usage specifiers match for purposes of resolving validation information, false otherwise
-     */
-    private boolean matchUsage(@Nonnull final KeyUse metadataUsage, @Nonnull final UsageType criteriaUsage) {
-        if (KeyUse.SIGNATURE.equals(metadataUsage) && criteriaUsage == UsageType.SIGNING) {
-            return true;
-        } else if (KeyUse.ENCRYPTION.equals(metadataUsage) && criteriaUsage == UsageType.ENCRYPTION) {
-            return true;
-        }
-        //TODO support unspecified in some way?
-        return false;
-    }
-
-    /**
-     * Get the effective {@link UsageType} input to use.
-     * 
-     * @param criteriaSet the criteria set being processed
-     * @return the effective usage value
-     */
-    @Nonnull private UsageType getEffectiveUsageInput(@Nonnull final CriteriaSet criteriaSet) {
-        final UsageCriterion usageCriteria = criteriaSet.get(UsageCriterion.class);
-        if (usageCriteria != null) {
-            return usageCriteria.getUsage();
-        }
-        return UsageType.UNSPECIFIED; 
-    }
-
 }
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/AbstractEncryptionAlgorithmsLookupStrategy.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/AbstractEncryptionAlgorithmsLookupStrategy.java
new file mode 100644
index 0000000..05c0667
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/AbstractEncryptionAlgorithmsLookupStrategy.java
@@ -0,0 +1,93 @@
+/*
+ * 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.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.xmlsec.algorithm.AlgorithmRegistry;
+import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
+import org.opensaml.xmlsec.impl.AlgorithmRuntimeSupportedPredicate;
+
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+/** 
+ * A base class for lookup strategies that return encryption algorithms and make use
+ * of the {@link AlgorithmRegistry}.
+ */
+public abstract class AbstractEncryptionAlgorithmsLookupStrategy 
+                    implements BiFunction<CriteriaSet, Predicate<String>, List<String>> {
+    
+    /** The AlgorithmRegistry used when processing algorithm URIs. */
+    @Nonnull private final AlgorithmRegistry algorithmRegistry;
+    
+    /**
+     * Constructor.
+     *
+     * @param registry the algorithm registry to used when resolving algorithm URIs. Defaults to
+     *              the registry resolved via {@link AlgorithmSupport#getGlobalAlgorithmRegistry()}
+     */
+    protected AbstractEncryptionAlgorithmsLookupStrategy(@Nullable final AlgorithmRegistry registry) {
+        if (registry == null) {
+            algorithmRegistry = AlgorithmSupport.getGlobalAlgorithmRegistry();
+        } else {
+            algorithmRegistry = registry;
+        }        
+    }
+    
+    /**
+     * Get a predicate which evaluates whether a cryptographic algorithm is supported
+     * by the runtime environment.
+     * 
+     * @return the predicate
+     */
+    @Nonnull protected Predicate<String> getAlgorithmRuntimeSupportedPredicate() {
+        return new AlgorithmRuntimeSupportedPredicate(getAlgorithmRegistry());
+    }
+    
+   /**
+    * Get the algorithm registry.
+    * 
+    * @return the algorithm registry
+    */
+   protected AlgorithmRegistry getAlgorithmRegistry() {
+       return algorithmRegistry;
+   }
+   
+   
+   /**
+    * 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 protected 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/main/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionConfiguration.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionConfiguration.java
index 5b58806..4ebc71b 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionConfiguration.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionConfiguration.java
@@ -23,7 +23,7 @@ import java.util.List;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.CredentialResolver;
 import org.opensaml.xmlsec.impl.BasicAlgorithmPolicyConfiguration;
 
 import net.shibboleth.oidc.security.JWTEncryptionConfiguration;
@@ -32,84 +32,77 @@ import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
 import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
 import net.shibboleth.utilities.java.support.primitive.StringSupport;
 
+/**
+ * Basic implementation of {@link JWTEncryptionConfiguration}.
+ */
 public class BasicJWTEncryptionConfiguration extends BasicAlgorithmPolicyConfiguration 
     implements JWTEncryptionConfiguration {
-
-    /** Data encryption credentials. */
-    @Nonnull @NonnullElements private List<Credential> dataEncryptionCredentials;
     
     /** Data encryption algorithm URIs. */
     @Nonnull @NonnullElements private List<String> dataEncryptionAlgorithms;
-
-    /** Key transport encryption credentials. */
-    @Nonnull @NonnullElements private List<Credential> keyTransportEncryptionCredentials;
     
     /** Key transport encryption algorithm URIs. */
     @Nonnull @NonnullElements private List<String> keyTransportEncryptionAlgorithms;
     
+    /** The EncryptedKey's credential resolver. */ 
+    @Nullable private CredentialResolver kekKeyCredentialResolver;
+    
+    /** The content encryption key (CEK) resolver.*/
+    @Nullable private CredentialResolver contentEncryptionKeyCredentialResolver;
+    
     /** Constructor. */
     public BasicJWTEncryptionConfiguration() {
-        dataEncryptionCredentials = Collections.emptyList();
         dataEncryptionAlgorithms = Collections.emptyList();
-        keyTransportEncryptionCredentials = Collections.emptyList();
         keyTransportEncryptionAlgorithms = Collections.emptyList();
     }
     
     @Override
-    @Nonnull @NonnullElements @Unmodifiable @NotLive public List<Credential> getDataEncryptionCredentials() {
-        return dataEncryptionCredentials;
+    @Nullable public CredentialResolver getContentEncryptionKeyCredentialResolver() {
+        return contentEncryptionKeyCredentialResolver;
     }
     
     /**
-     * Set the data encryption credentials to use.
+     * Set the CredentialResolver to resolve the content encryption key.
      * 
-     * @param credentials the list of data encryption credentials
+     * @param resolver the CredentialResolver instance
      */
-    public void setDataEncryptionCredentials(@Nullable @NonnullElements final List<Credential> credentials) {
-        if (credentials == null) {
-            dataEncryptionCredentials  = Collections.emptyList();
-        } else {
-            dataEncryptionCredentials = List.copyOf(credentials);
-        }
+    public void setContentEncryptionKeyCredentialResolver(@Nullable final CredentialResolver resolver) {
+        contentEncryptionKeyCredentialResolver = resolver;
     }
     
-    /** {@inheritDoc} */
     @Override
-    @Nonnull @NonnullElements @Unmodifiable @NotLive public List<String> getDataEncryptionAlgorithms() {
-        return dataEncryptionAlgorithms;
+    @Nullable public CredentialResolver getKEKCredentialResolver() {
+       return kekKeyCredentialResolver; 
     }
     
     /**
-     * Set the data encryption algorithms to use.
+     * Set the CredentialResolver to resolve the key encryption or key wrapping key.
      * 
-     * @param algorithms the list of algorithms
+     * @param resolver the CredentialResolver instance
      */
-    public void setDataEncryptionAlgorithms(@Nullable @NonnullElements final List<String> algorithms) {
-        if (algorithms == null) {
-            dataEncryptionAlgorithms = Collections.emptyList();
-        } else {
-            dataEncryptionAlgorithms = List.copyOf(StringSupport.normalizeStringCollection(algorithms));
-        }
+    public void setKEKCredentialResolver(@Nullable final CredentialResolver resolver) {
+        kekKeyCredentialResolver = resolver; 
     }
     
+      
     /** {@inheritDoc} */
     @Override
-    @Nonnull @NonnullElements @Unmodifiable @NotLive public List<Credential> getKeyTransportEncryptionCredentials() {
-        return keyTransportEncryptionCredentials;
+    @Nonnull @NonnullElements @Unmodifiable @NotLive public List<String> getDataEncryptionAlgorithms() {
+        return dataEncryptionAlgorithms;
     }
     
     /**
-     * Set the key transport encryption credentials to use.
+     * Set the data encryption algorithms to use.
      * 
-     * @param credentials the list of key transport encryption credentials
+     * @param algorithms the list of algorithms
      */
-    public void setKeyTransportEncryptionCredentials(@Nullable @NonnullElements final List<Credential> credentials) {
-        if (credentials == null) {
-            keyTransportEncryptionCredentials  = Collections.emptyList();
+    public void setDataEncryptionAlgorithms(@Nullable @NonnullElements final List<String> algorithms) {
+        if (algorithms == null) {
+            dataEncryptionAlgorithms = Collections.emptyList();
         } else {
-            keyTransportEncryptionCredentials = List.copyOf(credentials);
+            dataEncryptionAlgorithms = List.copyOf(StringSupport.normalizeStringCollection(algorithms));
         }
-    }
+    }    
     
     /** {@inheritDoc} */
     @Override
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/CredentialConversionUtil.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/CredentialConversionUtil.java
index 8272e2a..5cfed4d 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/CredentialConversionUtil.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/CredentialConversionUtil.java
@@ -21,16 +21,21 @@ import java.security.interfaces.ECPublicKey;
 import java.security.interfaces.RSAPublicKey;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.crypto.SecretKey;
 
 import org.opensaml.security.credential.Credential;
 import org.opensaml.security.credential.UsageType;
 
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.jwk.AsymmetricJWK;
 import com.nimbusds.jose.jwk.Curve;
 import com.nimbusds.jose.jwk.ECKey;
 import com.nimbusds.jose.jwk.JWK;
 import com.nimbusds.jose.jwk.KeyUse;
 import com.nimbusds.jose.jwk.RSAKey;
 
+import net.shibboleth.oidc.security.credential.BasicJWKCredential;
 import net.shibboleth.oidc.security.credential.JWKCredential;
 
 /**
@@ -104,6 +109,51 @@ public final class CredentialConversionUtil {
         return null;
     }
     
+    /**
+     * Create a shallow copy of the input credential, replacing the secrete key and keyID with those
+     * supplied. Only supports symmetric keys.
+     * 
+     * @param credential the credential to copy and replace the secret key
+     * @param newKid the new keyID to use
+     * @param derivedKey the secret key to use
+     * @return the copied credential with the supplied secret key
+     */
+    public static JWKCredential copySymmetricCredentialWithNewSecret(
+            final JWKCredential credential, final String newKid, final SecretKey derivedKey) {
+        final BasicJWKCredential jwkCredential = new BasicJWKCredential();
+        jwkCredential.setAlgorithm(credential.getAlgorithm());
+        jwkCredential.setKid(newKid);
+        credential.getKeyNames().add(newKid);
+        jwkCredential.setUsageType(credential.getUsageType());            
+        jwkCredential.setSecretKey(derivedKey);
+        jwkCredential.setEntityId(credential.getEntityId());
+        return jwkCredential;    
+    }
+    
+    /**
+     * Converts a JWK to an internal Credential. Only RSA and EC keys supported.
+     * 
+     * @param key to convert.
+     * @return credential as Credential.
+     * @throws JOSEException on error converting the key 
+     * 
+     */
+    @Nullable public static Credential keyToCredential(@Nullable final JWK key) throws JOSEException {
+        if (key == null) {
+            return null;
+        }        
+        if (key instanceof RSAKey || key instanceof ECKey) {
+            final BasicJWKCredential jwkCredential = new BasicJWKCredential();
+            jwkCredential.setAlgorithm(key.getAlgorithm());
+            jwkCredential.setKid(key.getKeyID());
+            jwkCredential.getKeyNames().add(key.getKeyID());
+            jwkCredential.setUsageType(CredentialConversionUtil.getUsageType(key));            
+            jwkCredential.setPublicKey(((AsymmetricJWK) key).toPublicKey()); 
+            return jwkCredential;
+        }     
+        return null;
+    }
+    
 
     /**
      * Converts JWK key usage type to OpenSAML usage type.
@@ -120,5 +170,4 @@ public final class CredentialConversionUtil {
         }
         return UsageType.UNSPECIFIED;
     }
-
 }
\ No newline at end of file
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/DefaultDataEncryptionAlgorithmsLookupStrategy.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/DefaultDataEncryptionAlgorithmsLookupStrategy.java
new file mode 100644
index 0000000..7cf6f4e
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/DefaultDataEncryptionAlgorithmsLookupStrategy.java
@@ -0,0 +1,79 @@
+/*
+ * 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.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.xmlsec.algorithm.AlgorithmRegistry;
+
+import net.shibboleth.oidc.security.JWTEncryptionConfiguration;
+import net.shibboleth.oidc.security.criterion.JWTEncryptionConfigurationCriterion;
+import net.shibboleth.utilities.java.support.logic.PredicateSupport;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+/**
+ * A BiFunction which pulls data encryption (content encryption) algorithms to use from the 
+ * encryption configuration and filters them. Algorithms are removed (filtered) if not supported
+ * by the runtime or are explicitly excluded.
+ */
+public class DefaultDataEncryptionAlgorithmsLookupStrategy extends AbstractEncryptionAlgorithmsLookupStrategy {
+
+    /**
+     * Constructor.
+     *
+     * @param registry the algorithm registry to use when resolving algorithm URIs. Can be {@code null}.
+     */
+    protected DefaultDataEncryptionAlgorithmsLookupStrategy(@Nullable final AlgorithmRegistry registry) {
+        super(registry);
+    }
+
+    @Override
+    @Nonnull public List<String> apply(final CriteriaSet criteria, final Predicate<String> includeExcludePredicate) {
+        return getDataEncryptionAlgorithmsFromConfiguration(criteria, includeExcludePredicate);
+    }
+    
+    /**
+     * Get the data encryption 'enc' algorithms from the {@link JWTEncryptionConfiguration} inside
+     * the criteria set.
+     * 
+     * @param criteria to pull out the {@link JWTEncryptionConfiguration}
+     * @param includeExcludePredicate the include and exclude predicates to filter the credentials with
+     * 
+     * @return the data/content encryption algorithms from the configuration, an empty list otherwise
+     */
+    @Nonnull protected List<String> getDataEncryptionAlgorithmsFromConfiguration(final CriteriaSet criteria,
+            final Predicate<String> includeExcludePredicate){
+        final ArrayList<String> accumulator = new ArrayList<>();
+        for (final JWTEncryptionConfiguration config
+                : criteria.get(JWTEncryptionConfigurationCriterion.class).getConfigurations()) {
+            
+            config.getDataEncryptionAlgorithms()
+                .stream()
+                .filter(PredicateSupport.and(getAlgorithmRuntimeSupportedPredicate(), includeExcludePredicate))
+                .forEach(accumulator::add);
+        }
+        return accumulator;
+    }
+
+
+}
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/DefaultJWTDecryptionParametersResolver.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/DefaultJWTDecryptionParametersResolver.java
index 148d894..45c7ea0 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/DefaultJWTDecryptionParametersResolver.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/DefaultJWTDecryptionParametersResolver.java
@@ -1,3 +1,20 @@
+/*
+ * 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.util.Collections;
@@ -14,7 +31,7 @@ import net.shibboleth.oidc.security.JWTDecryptionConfiguration;
 import net.shibboleth.oidc.security.JWTDecryptionParameters;
 import net.shibboleth.oidc.security.JWTDecryptionParametersResolver;
 import net.shibboleth.oidc.security.credential.JOSEObjectCredentialResolver;
-import net.shibboleth.oidc.security.criterion.DecryptionConfigurationCriterion;
+import net.shibboleth.oidc.security.criterion.JWTDecryptionConfigurationCriterion;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
 import net.shibboleth.utilities.java.support.resolver.ResolverException;
@@ -37,13 +54,13 @@ public class DefaultJWTDecryptionParametersResolver extends AbstractSecurityPara
     @Override
     public JWTDecryptionParameters resolveSingle(final CriteriaSet criteria) throws ResolverException {
         Constraint.isNotNull(criteria, "CriteriaSet was null");
-        Constraint.isNotNull(criteria.get(DecryptionConfigurationCriterion.class), 
+        Constraint.isNotNull(criteria.get(JWTDecryptionConfigurationCriterion.class), 
                 "Resolver requires an instance of DecryptionConfigurationCriterion");
 
         final JWTDecryptionParameters params = new JWTDecryptionParameters();
         
         resolveAndPopulateIncludesExcludes(params, criteria, 
-                criteria.get(DecryptionConfigurationCriterion.class).getConfigurations());
+                criteria.get(JWTDecryptionConfigurationCriterion.class).getConfigurations());
         
         params.setContentEncryptionKeyCredentialResolver(resolveContentEncryptionKeyCredentialResolver(criteria));
         params.setKEKCredentialResolver(resolveKEKCredentialResolver(criteria));
@@ -68,7 +85,7 @@ public class DefaultJWTDecryptionParametersResolver extends AbstractSecurityPara
     @Nullable protected JOSEObjectCredentialResolver resolveKEKCredentialResolver(
             @Nonnull final CriteriaSet criteria) {
         
-        for (final JWTDecryptionConfiguration config : criteria.get(DecryptionConfigurationCriterion.class)
+        for (final JWTDecryptionConfiguration config : criteria.get(JWTDecryptionConfigurationCriterion.class)
                 .getConfigurations()) {
             if (config.getKEKCredentialResolver() != null) {
                 return config.getKEKCredentialResolver();
@@ -87,7 +104,7 @@ public class DefaultJWTDecryptionParametersResolver extends AbstractSecurityPara
     @Nullable protected JOSEObjectCredentialResolver resolveContentEncryptionKeyCredentialResolver(
             @Nonnull final CriteriaSet criteria) {
         
-        for (final JWTDecryptionConfiguration config : criteria.get(DecryptionConfigurationCriterion.class)
+        for (final JWTDecryptionConfiguration config : criteria.get(JWTDecryptionConfigurationCriterion.class)
                 .getConfigurations()) {
             if (config.getContentEncryptionKeyCredentialResolver() != null) {
                 return config.getContentEncryptionKeyCredentialResolver();
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolver.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/DefaultJWTEncryptionParametersResolver.java
similarity index 63%
rename from oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolver.java
rename to oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/DefaultJWTEncryptionParametersResolver.java
index 2f648fc..a77ab90 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolver.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/DefaultJWTEncryptionParametersResolver.java
@@ -22,15 +22,20 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.function.BiFunction;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
+import javax.crypto.SecretKey;
 
 import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.CredentialResolver;
 import org.opensaml.security.credential.CredentialSupport;
 import org.opensaml.security.credential.UsageType;
+import org.opensaml.security.credential.criteria.impl.EvaluableUsageCredentialCriterion;
+import org.opensaml.security.criteria.UsageCriterion;
 import org.opensaml.xmlsec.EncryptionParametersResolver;
 import org.opensaml.xmlsec.algorithm.AlgorithmRegistry;
 import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
@@ -42,45 +47,102 @@ import org.slf4j.LoggerFactory;
 
 import com.nimbusds.jose.Algorithm;
 import com.nimbusds.jose.EncryptionMethod;
+import com.nimbusds.jose.JOSEException;
 import com.nimbusds.jose.JWEAlgorithm;
-import com.nimbusds.jose.jwk.KeyType;
+import com.nimbusds.jose.util.StandardCharset;
+import com.nimbusds.oauth2.sdk.auth.Secret;
+import com.nimbusds.oauth2.sdk.jose.SecretKeyDerivation;
 
 import net.shibboleth.oidc.security.JWTEncryptionConfiguration;
 import net.shibboleth.oidc.security.JWTEncryptionParameters;
 import net.shibboleth.oidc.security.JWTEncryptionParametersResolver;
 import net.shibboleth.oidc.security.credential.JWKCredential;
 import net.shibboleth.oidc.security.criterion.JWTEncryptionConfigurationCriterion;
+import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.logic.Constraint;
-import net.shibboleth.utilities.java.support.logic.PredicateSupport;
 import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
 import net.shibboleth.utilities.java.support.resolver.ResolverException;
 
 /**
- * Basic implementation of an {@link EncryptionParametersResolver}. The resolver takes the first credential from 
- * the local encryption configuration which matches a configured key transport algorithm. The key transport
- * algorithms are tried in the order they are specified, until a suitable credential is found.
+ * Basic implementation of an {@link EncryptionParametersResolver}. The resolver delegates much of its functionality
+ * to various lookup strategies and credential resolvers. Its main purpose is to compose the results of each strategy
+ * and resolver.
+ * 
+ * <p>The set of usable key transport and data encryption algorithms are determined by injected strategies. The set of
+ * usable credentials are resolved by {@link CredentialResolver}s found from the {@link JWTEncryptionConfiguration}
+ * inside the input {@link CriteriaSet}. Therefore, the {@link JWTEncryptionConfigurationCriterion} must be present
+ * inside the criteria set, and the resolvers must be configured for any parameters to be returned.</p>
+ * 
+ * <p>Once the set of algorithms and credentials have been looked up and resolved. The key transport algorithms are 
+ * tried in the order they are specified until a suitable, compatible, credential is found. It is expected asymmetric
+ * encryption keys will be resolved from the OpenID Providers keyset document, and symmetric keys will be derived from
+ * the client_secret (although any resolvers could be supplied, and hence this behaviour is not guranteed). </p>
+ * 
+ * <p>Symmetric keys are expected to be returned in their original format. The resolver will derive the correct symmetric
+ * key to add to the correct parameters e.g. for key wrapping and direct content encryption.</p>
+ * 
+ * <p>Note: the criteria supplied to the credential resolvers should be relatively light on evaluable criterion. For example,
+ * only containing the {@link EvaluableUsageCredentialCriterion} - alongside any non-evaluable criterion needed by the 
+ * resolvers. Most of the specific filtering e.g. algorithm and key length, is performed by this resolver itself based
+ * on the logic described by the OpenID specifications.</p>
  * 
  * <p>
  * The following {@link net.shibboleth.utilities.java.support.resolver.Criterion} inputs are supported:
  * </p>
  * <ul>
  * <li>{@link JWTEncryptionConfigurationCriterion} - required</li> 
- * <li>{@link EncryptionOptionalCriterion} - optional</li> 
+ * <li>{@link ProviderMetadataCriterion} - likely required for some downstream resolvers</li> 
  * </ul>
  */
-public class BasicJWTEncryptionParametersResolver extends AbstractSecurityParametersResolver<JWTEncryptionParameters> 
+public class DefaultJWTEncryptionParametersResolver extends AbstractSecurityParametersResolver<JWTEncryptionParameters> 
         implements JWTEncryptionParametersResolver {
     
     /** Logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(BasicJWTEncryptionParametersResolver.class);
+    @Nonnull private final Logger log = LoggerFactory.getLogger(DefaultJWTEncryptionParametersResolver.class);
     
     /** The AlgorithmRegistry used when processing algorithm URIs. */
     @Nonnull private AlgorithmRegistry algorithmRegistry;
+        
+    /** A lookup strategy to locate data encryption algorithms from the given criteria set and includesExcludes predicate.*/
+    @Nonnull 
+    private BiFunction<CriteriaSet, Predicate<String>, List<String>> dataEncryptionAlgorithmsLookupStrategy;
+    
+    /** A lookup strategy to locate key encryption algorithms from the given criteria set and includesExcludes predicate.*/
+    @Nonnull 
+    private BiFunction<CriteriaSet, Predicate<String>, List<String>> keyTransportEncryptionAlgorithmsLookupStrategy;
     
     /** Constructor. */
-    public BasicJWTEncryptionParametersResolver() {
+    public DefaultJWTEncryptionParametersResolver() {
         algorithmRegistry = AlgorithmSupport.getGlobalAlgorithmRegistry();
+        keyTransportEncryptionAlgorithmsLookupStrategy = 
+                new DefaultKeyTransportEncryptionAlgorithmsLookupStrategy(null);
+        dataEncryptionAlgorithmsLookupStrategy = new DefaultDataEncryptionAlgorithmsLookupStrategy(null);
+    }
+    
+    /**
+     * Set the data encryption algorithms lookup strategy to use.
+     * 
+     * @param strategy the strategy to use.
+     */
+    public void setDataEncryptionAlgorithmsLookupStrategy(
+            final BiFunction<CriteriaSet, Predicate<String>, List<String>> strategy) {
+
+        dataEncryptionAlgorithmsLookupStrategy = Constraint.isNotNull(strategy,
+                "dataEncryptionAlgorithmsLookupStrategy can not be null");
+    }
+    
+    /**
+     * Set the key transport encryption algorithms lookup strategy to use.
+     * 
+     * @param strategy the strategy to use.
+     */
+    public void setKeyTransportEncryptionAlgorithmsLookupStrategy(
+            final BiFunction<CriteriaSet, Predicate<String>, List<String>> strategy) {
+
+        keyTransportEncryptionAlgorithmsLookupStrategy =
+                Constraint.isNotNull(strategy,
+                        "keyTransportEncryptionAlgorithmsLookupStrategy can not be null");
     }
     
     /**
@@ -144,6 +206,42 @@ public class BasicJWTEncryptionParametersResolver extends AbstractSecurityParame
         return null;
         
     }
+    
+    /**
+     * Resolve and return the effective {@link CredentialResolver} used to encrypt content encryption keys.
+     * 
+     * @param criteria the input criteria being evaluated
+     * @return the effective resolver, or null
+     */
+    @Nullable protected CredentialResolver resolveKEKCredentialResolver(
+            @Nonnull final CriteriaSet criteria) {
+        
+        for (final JWTEncryptionConfiguration config : criteria.get(JWTEncryptionConfigurationCriterion.class)
+                .getConfigurations()) {
+            if (config.getKEKCredentialResolver() != null) {
+                return config.getKEKCredentialResolver();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Resolve and return the effective {@link CredentialResolver} used to resolve the content encryption key.
+     * 
+     * @param criteria the input criteria being evaluated
+     * @return the effective resolver, or null
+     */
+    @Nullable protected CredentialResolver resolveContentEncryptionKeyCredentialResolver(
+            @Nonnull final CriteriaSet criteria) {
+        
+        for (final JWTEncryptionConfiguration config : criteria.get(JWTEncryptionConfigurationCriterion.class)
+                .getConfigurations()) {
+            if (config.getContentEncryptionKeyCredentialResolver() != null) {
+                return config.getContentEncryptionKeyCredentialResolver();
+            }
+        }
+        return null;
+    }
 
     /**
      * Log the resolved parameters.
@@ -259,16 +357,23 @@ public class BasicJWTEncryptionParametersResolver extends AbstractSecurityParame
             log.debug("No encryption configuration criterion, encryption parameters can not be resolved");
             return;
         }
+        // Add usage criterion if not already supplied
+        if (!criteria.contains(UsageCriterion.class)) {
+            criteria.add(new UsageCriterion(UsageType.ENCRYPTION));
+        }
+        
         // Pre-resolve these for efficiency
         final List<Credential> keyTransportCredentials = getEffectiveKeyTransportCredentials(criteria);
+        log.trace("Resolved {} key transport credentials", keyTransportCredentials.size());
         final List<String> keyTransportAlgorithms =
-                getEffectiveKeyTransportAlgorithms(criteria, includeExcludePredicate);
+                keyTransportEncryptionAlgorithmsLookupStrategy.apply(criteria, includeExcludePredicate);
         log.trace("Resolved effective key transport algorithms: {}", keyTransportAlgorithms);
         
-        final List<String> dataEncryptionAlgorithms = getEffectiveDataEncryptionAlgorithms(criteria, 
+        final List<String> dataEncryptionAlgorithms = dataEncryptionAlgorithmsLookupStrategy.apply(criteria, 
                 includeExcludePredicate);
-        final List<Credential> dataEncryptionCredentials = getEffectiveDataEncryptionCredentials(criteria);
         log.trace("Resolved effective data encryption algorithms: {}", dataEncryptionAlgorithms);
+        final List<Credential> dataEncryptionCredentials = getEffectiveDataEncryptionCredentials(criteria);
+        log.trace("Resolved {} data encryption credentials", dataEncryptionCredentials.size());   
         
         resolveCredentialForSupportedAlgorithm(criteria,
                 convertStringAlgorithmURIsToJwkAlgorithms(keyTransportAlgorithms), 
@@ -277,18 +382,17 @@ public class BasicJWTEncryptionParametersResolver extends AbstractSecurityParame
     }
     
     /**
-     * Resolve a credential compatible with one-of the supported algorithms. Algorithms are tried in the order
+     * Resolve a credential (key) compatible with one-of the supported algorithms. Algorithms are tried in the order
      * they appear in the list of keyTransportAlgorithms.  
      * 
-     * <p>For each algorithm (in order) locally obtained credentials are matched first, followed by those fetched 
-     * from any additional sources - implemented by subclasses.</p>
+     * <p>Symmetric key algorithms e.g. either for key wrapping or direct content encryption, are passed through a 
+     * key derivation function. It is therefore assumed the input shared secret is the original key e.g. the
+     * raw client_secret.</p>
+     * 
+     * <p>Keys for asymmetric key algorithms are added to the parameter context as is. It is assumed such keys
+     * are taken from the OpenID Provider's keyset document, however any resolver can be used and hence there is
+     * no guarantee of this.</p> 
      * 
-     * <p>In the normal case, it would be expected that direct encryption and key wrapping credentials will be 
-     * resolved from locally configured private keys. Whereas key encryption or key agreement credentials will be 
-     * resolved from those fetched from additional sources e.g. a subclass that resolves the OP's remote key set. 
-     * Although it is permissible that any key type is found in any of the sources. By default, no additional sources
-     * are configured, and so only locally configured credentials will be resolved.
-     * </p>
      * <p>The first key that is compatible with the key transport algorithm is returned. That is, if two or more 
      * keys support the same algorithm, the first key resolved will be returned. There is no guarantee which 
      * key that is.</p>
@@ -309,112 +413,130 @@ public class BasicJWTEncryptionParametersResolver extends AbstractSecurityParame
             @Nonnull final List<Credential> dataEncryptionCredentials,
             @Nonnull final JWTEncryptionParameters params) {
         
-        for (final JWEAlgorithm algorithm : keyTransportAlgorithms) {   
+        for (final JWEAlgorithm algorithm : keyTransportAlgorithms) {  
             
             if (JWEAlgorithm.DIR.equals(algorithm)) {  
-                // Is a direct encryption type, so need to populate the data enc. creds.
-                final Credential localCred = 
-                        findCredentialThatSupportsAlgorithm(dataEncryptionCredentials, algorithm);
+                // Direct content encryption requires a derived key
                 
-                if (localCred != null) {
-                    final EncryptionMethod encryptionMethod = 
-                            findEncryptionMethodThatSupportsCredential(dataEncryptionAlgorithms, localCred);
+                final JWKCredential credential = 
+                        findCredentialThatSupportsAlgorithm(dataEncryptionCredentials, algorithm);               
                     
-                    if (encryptionMethod != null) {
-                        
-                        if (log.isDebugEnabled()) {
-                            log.debug("Selected key '{}' for alg 'dir' and enc '{}'", 
-                                    CredentialConversionUtil.resolveKid(localCred),
-                                    encryptionMethod.getName());
-                        }
-                        params.setKeyTransportEncryptionAlgorithm(JWEAlgorithm.DIR.getName());
-                        params.setDataEncryptionCredential(localCred);
-                        params.setDataEncryptionAlgorithm(encryptionMethod.getName());
-                        return;
-                    } 
-                } else {
-                    // If a local direct encryption credential can not be found, delegate to 
-                    // specialised implementation specific logic - if any.
-                    resolveDataEncryptionCredentialForSupportedAlgorithmFromAdditionalSource(
-                            dataEncryptionAlgorithms, criteria, params);                
-                    if (params.getDataEncryptionCredential() != null) {
-                        return;
+                final EncryptionMethod encryptionMethod = 
+                        findEncryptionMethodThatSupportsCredential(dataEncryptionAlgorithms, credential);
+                
+                final JWKCredential derivedCredential = 
+                        deriveSymmetricKeyForAlgAndEnc(credential, algorithm, encryptionMethod);
+                
+                if (derivedCredential != null) {                      
+                    if (log.isDebugEnabled()) {
+                        log.debug("Derived key from '{}' for alg '{}' and enc '{}'", 
+                                CredentialConversionUtil.resolveKid(credential),
+                                algorithm.getName(), encryptionMethod.getName());
                     }
-                }
+                    
+                    params.setKeyTransportEncryptionAlgorithm(JWEAlgorithm.DIR.getName());
+                    params.setDataEncryptionCredential(derivedCredential);
+                    params.setDataEncryptionAlgorithm(encryptionMethod.getName());
+                    return;                                        
+                } 
+                                 
+            } else if (JWEAlgorithm.Family.AES_GCM_KW.contains(algorithm) 
+                    || JWEAlgorithm.Family.AES_KW.contains(algorithm)) { 
+                // Key agreement requires a derived key
                 
-            } else {
-                // Supports key encryption, key wrapping, or key agreement.
-                final Credential localCred = 
+                final JWKCredential credential = 
+                        findCredentialThatSupportsAlgorithm(keyTransportCredentials, algorithm);
+                
+                final EncryptionMethod encryptionMethod = 
+                        resolveEncryptionMethod(dataEncryptionAlgorithms);
+                
+                final JWKCredential derivedCredential = 
+                        deriveSymmetricKeyForAlgAndEnc(credential, algorithm, encryptionMethod);
+                
+                if (derivedCredential != null) {                   
+                    if (log.isDebugEnabled()) {
+                        log.debug("Derived key from '{}' for alg '{}' and enc '{}'", 
+                                CredentialConversionUtil.resolveKid(credential),
+                                algorithm.getName(), encryptionMethod.getName());
+                    }
+                    params.setKeyTransportEncryptionCredential(derivedCredential);
+                    params.setKeyTransportEncryptionAlgorithm(algorithm.getName());
+                    params.setDataEncryptionAlgorithm(encryptionMethod.getName());
+                    return;                                       
+               }               
+            } else if (JWEAlgorithm.Family.ASYMMETRIC.contains(algorithm)){
+                // Supports key encryption, or key agreement.
+                final JWKCredential credential = 
                         findCredentialThatSupportsAlgorithm(keyTransportCredentials, algorithm); 
                 
                 final EncryptionMethod encryptionMethod = 
                         resolveEncryptionMethod(dataEncryptionAlgorithms);
                 
-                if (localCred != null) {                    
+                if (credential != null) {                    
                
                     if (log.isDebugEnabled()) {
                         log.debug("Selected key '{}' for alg '{}' and enc '{}'", 
-                                CredentialConversionUtil.resolveKid(localCred),
+                                CredentialConversionUtil.resolveKid(credential),
                                 algorithm.getName(), encryptionMethod.getName());
                     }
-                    params.setKeyTransportEncryptionCredential(localCred);
+                    params.setKeyTransportEncryptionCredential(credential);
                     params.setKeyTransportEncryptionAlgorithm(algorithm.getName());
                     params.setDataEncryptionAlgorithm(encryptionMethod.getName());
                     return;
-                } else {                
-                    // If a local credential can not be found, delegate to 
-                    // specialised implementation specific logic - if any.
-                    resolveKeyTransportCredentialForSupportedAlgorithmFromAdditionalSource(
-                            algorithm, encryptionMethod, criteria, params);                
-                    if (params.getKeyTransportEncryptionCredential() != null) {
-                        return;
-                    }
-                }
-                
-               
-            }
-        }
+                } 
+            } else {
+                log.trace("Algorithm '{}' is not supported, moving on", algorithm);
+            }            
+        }        
     }
     
     /**
-     * Resolve data encryption credentials from an additional source. This is the 'hook' which a 
-     * specialised implementation class can use to provide additional behaviour to the base behaviour
-     * of this class. That is, to find credentials from a source other than the local configuration.
-     * 
-     * <p>
-     * The algorithm 'alg' will be always be 'dir' or direct encryption when this method is called.
-     * </p>
+     * Derive a *new* symmetric key credential suitable for the given 'alg' and 'enc' algorithms. The
+     * new credential is a copy of the given credential but replacing the keyID and secret. If any of 
+     * the inputs are {@code null} then {@code null} is returned. 
      * 
-     * <p>Implementation classes should override this method, the default behaviour does nothing.</p>
+     * @param credential the credential that contains a secret key to derive a new credential from
+     * @param alg the key management mode algorithm
+     * @param enc the content encryption algorithm
      * 
-     * @param dataEncryptionAlgorithms the list of supported data encryption algorithms the 
-     *                                      resolved credential must support one-of
-     * @param criteria any criterion that supports the resolution process
-     * @param params the encryption parameters to store the result
+     * @return a *new* derived credential, or {@code null} if an error occurs
      */
-    protected void resolveDataEncryptionCredentialForSupportedAlgorithmFromAdditionalSource(
-            @Nonnull final List<EncryptionMethod> dataEncryptionAlgorithms,
-            @Nonnull final CriteriaSet criteria, @Nonnull final JWTEncryptionParameters params) {
-        // Default method does nothing        
+    @Nullable private JWKCredential deriveSymmetricKeyForAlgAndEnc(
+            @Nullable final JWKCredential credential, @Nullable final JWEAlgorithm alg, @Nullable final EncryptionMethod enc) {
+        
+        if (credential == null || credential.getSecretKey() == null || alg == null || enc == null) {
+            return null;
+        }
+        try {
+            final SecretKey derivedKey = generateSymmetricKey(credential.getSecretKey().getEncoded(), alg, enc);
+            //build a new credential so the old is left in its original state
+            return CredentialConversionUtil.copySymmetricCredentialWithNewSecret(
+                    credential, "derived-"+CredentialConversionUtil.resolveKid(credential), derivedKey);
+        } catch (final JOSEException e) {
+            log.warn("Unable to generate secret key: {}", e.getMessage());
+            return null;
+        }
     }
+   
     
     /**
-     * Resolve key transport credentials from an additional source. This is the 'hook' which a 
-     * specialised implementation class can use to provide additional behaviour to the base behaviour
-     * of this class. That is, to find credentials from a source other than the local configuration.
+     * Generate symmetric key from client_secret using the algorithms supplied.
      * 
-     * <p>Implementation classes should override this method, the default behaviour does nothing.</p>
+     * @param clientSecret client secret to derive a key from
+     * @param alg the key management mode or key transport algorithm
+     * @param enc the content encryption algorithm
      * 
-     * @param algorithm the key transport algorithm to find a suitable credential for
-     * @param encryptionMethod the chosen content encryption method to use
-     * @param criteriaa ny criterion that supports the resolution process
-     * @param params the encryption parameters to store the result.
+     * @return key derived from client secret.
+
+     * @throws JOSEException on error 
      */
-    protected void resolveKeyTransportCredentialForSupportedAlgorithmFromAdditionalSource(
-            @Nonnull final JWEAlgorithm algorithm, @Nonnull final EncryptionMethod encryptionMethod,
-            @Nonnull final CriteriaSet criteria, @Nonnull final JWTEncryptionParameters params) {
-        // Default method does nothing        
+    private final SecretKey generateSymmetricKey(final byte[] clientSecret, final JWEAlgorithm alg,
+            final EncryptionMethod enc) throws JOSEException {
+        
+        return SecretKeyDerivation.deriveSecretKey(
+                new Secret(new String(clientSecret, StandardCharset.UTF_8)), alg, enc);        
     }
+    
 
     /**
      * Determine if any of the given credentials match the given algorithm.
@@ -438,7 +560,9 @@ public class BasicJWTEncryptionParametersResolver extends AbstractSecurityParame
      * 
      * @return the first credential that matches the algorithm, {@literal null} otherwise.
      */
-    @Nullable protected Credential findCredentialThatSupportsAlgorithm(@Nonnull final  List<Credential> credentials,
+    //TODO the keys should already match the ENC usage, so that should be a redundant check
+    //TODO maybe this logic could be build into EvaluableCriterion for the resolvers.
+    @Nullable protected JWKCredential findCredentialThatSupportsAlgorithm(@Nonnull final  List<Credential> credentials,
             @Nonnull final JWEAlgorithm algorithm) {
         return credentials.stream()
                 .filter(Objects::nonNull)
@@ -451,8 +575,8 @@ public class BasicJWTEncryptionParametersResolver extends AbstractSecurityParame
     }
     
     /**
-     * Check the credential supports the algorithm specified. If the algorithm is present, check that is 
-     * identical to the algorithm supplied. If not, check key algorithm is compatible with the algorithm 
+     * Check the credential supports the algorithm given. If the algorithm is present, check that is 
+     * identical to the algorithm supplied. If not, check the key algorithm is compatible with the algorithm 
      * family.
      * 
      * @param credential the credential to check
@@ -503,7 +627,53 @@ public class BasicJWTEncryptionParametersResolver extends AbstractSecurityParame
         return null;
         
     }
+
+    /**
+     * Get the effective list of data encryption credentials to consider.
+     * 
+     * @param criteria the input criteria being evaluated
+     * 
+     * @return the list of credentials. 
+     */
+    @Nonnull protected List<Credential> getEffectiveDataEncryptionCredentials(@Nonnull final CriteriaSet criteria) {
+        final CredentialResolver resolver = resolveContentEncryptionKeyCredentialResolver(criteria);
+        if (resolver == null) {
+            return Collections.emptyList();
+        }
+        try { 
+            final List<Credential> credentialsAsList = new ArrayList<>();
+            final Iterable<Credential> credentials = resolver.resolve(criteria);
+            credentials.forEach(credentialsAsList::add);
+            return credentialsAsList;
+        } catch (final ResolverException e) {
+            log.warn("Unable to resolve data encryption credentials", e);
+            return Collections.emptyList();
+        }
+        
+    }
     
+    /**
+     * Get the effective list of key transport credentials to consider.
+     * 
+     * @param criteria the input criteria being evaluated
+     * 
+     * @return the list of credentials
+     */
+    @Nonnull protected List<Credential> getEffectiveKeyTransportCredentials(@Nonnull final CriteriaSet criteria) {
+        final CredentialResolver resolver = resolveKEKCredentialResolver(criteria);
+        if (resolver == null) {
+            return Collections.emptyList();
+        }
+        try { 
+            final List<Credential> credentialsAsList = new ArrayList<>();
+            final Iterable<Credential> credentials = resolver.resolve(criteria);
+            credentials.forEach(credentialsAsList::add);
+            return credentialsAsList;
+        } catch (final ResolverException e) {
+            log.warn("Unable to resolve data encryption credentials", e);
+            return Collections.emptyList();
+        }
+    }
     
     /**
      * Return the first encryption method in the supported list, or null otherwise.
@@ -545,89 +715,6 @@ public class BasicJWTEncryptionParametersResolver extends AbstractSecurityParame
         return encMethods.stream().filter(Objects::nonNull).map(EncryptionMethod::parse).collect(Collectors.toList());
     }
         
-    /**
-     * Get the effective list of data encryption credentials to consider.
-     * 
-     * @param criteria the input criteria being evaluated
-     * 
-     * @return the list of credentials
-     */
-    @Nonnull protected List<Credential> getEffectiveDataEncryptionCredentials(@Nonnull final CriteriaSet criteria) {
-        final ArrayList<Credential> accumulator = new ArrayList<>();
-        for (final JWTEncryptionConfiguration config : criteria.get(JWTEncryptionConfigurationCriterion.class)
-                .getConfigurations()) {
-            
-            accumulator.addAll(config.getDataEncryptionCredentials());
-            
-        }
-        return accumulator;
-    }
-    
-    /**
-     * Get the effective list of data encryption algorithm URIs to consider, including application of 
-     * include/exclude policy.
-     * 
-     * @param criteria the input criteria being evaluated
-     * @param includeExcludePredicate  the include/exclude predicate to use
-     * 
-     * @return the list of effective algorithm URIs
-     */
-    @Nonnull protected List<String> getEffectiveDataEncryptionAlgorithms(@Nonnull final CriteriaSet criteria, 
-            @Nonnull final Predicate<String> includeExcludePredicate) {
-        
-        final ArrayList<String> accumulator = new ArrayList<>();
-        for (final JWTEncryptionConfiguration config
-                : criteria.get(JWTEncryptionConfigurationCriterion.class).getConfigurations()) {
-            
-            config.getDataEncryptionAlgorithms()
-                .stream()
-                .filter(PredicateSupport.and(getAlgorithmRuntimeSupportedPredicate(), includeExcludePredicate))
-                .forEach(accumulator::add);
-        }
-        return accumulator;
-    }
-    
-    /**
-     * Get the effective list of key transport credentials to consider.
-     * 
-     * @param criteria the input criteria being evaluated
-     * 
-     * @return the list of credentials
-     */
-    @Nonnull protected List<Credential> getEffectiveKeyTransportCredentials(@Nonnull final CriteriaSet criteria) {
-        final ArrayList<Credential> accumulator = new ArrayList<>();
-        for (final JWTEncryptionConfiguration config : criteria.get(JWTEncryptionConfigurationCriterion.class)
-                .getConfigurations()) {
-
-            accumulator.addAll(config.getKeyTransportEncryptionCredentials());
-            
-        }
-        return accumulator;
-    }
-    
-    /**
-     * Get the effective list of key transport algorithm URIs to consider, including application of 
-     * include/exclude policy.
-     * 
-     * @param criteria the input criteria being evaluated
-     * @param includeExcludePredicate  the include/exclude predicate to use
-     * 
-     * @return the list of effective algorithm URIs
-     */
-    @Nonnull protected List<String> getEffectiveKeyTransportAlgorithms(@Nonnull final CriteriaSet criteria, 
-            @Nonnull final Predicate<String> includeExcludePredicate) {
-        
-        final ArrayList<String> accumulator = new ArrayList<>();
-        for (final JWTEncryptionConfiguration config
-                : criteria.get(JWTEncryptionConfigurationCriterion.class).getConfigurations()) {
-            
-            config.getKeyTransportEncryptionAlgorithms()
-                .stream()                
-                .filter(PredicateSupport.and(getAlgorithmRuntimeSupportedPredicate(), includeExcludePredicate))
-                .forEach(accumulator::add);
-        }
-        return accumulator;
-    }
     
     /**
      * Get a predicate which evaluates whether a cryptographic algorithm is supported
@@ -699,5 +786,4 @@ public class BasicJWTEncryptionParametersResolver extends AbstractSecurityParame
         
         return AlgorithmSupport.isDataEncryptionAlgorithm(getAlgorithmRegistry().get(algorithm));
     }
-
 }
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/DefaultKeyTransportEncryptionAlgorithmsLookupStrategy.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/DefaultKeyTransportEncryptionAlgorithmsLookupStrategy.java
new file mode 100644
index 0000000..e5e0bac
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/DefaultKeyTransportEncryptionAlgorithmsLookupStrategy.java
@@ -0,0 +1,84 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.xmlsec.algorithm.AlgorithmRegistry;
+
+import net.shibboleth.oidc.security.JWTEncryptionConfiguration;
+import net.shibboleth.oidc.security.criterion.JWTEncryptionConfigurationCriterion;
+import net.shibboleth.utilities.java.support.logic.PredicateSupport;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+/** 
+ * A BiFunction that retrieves and filters the key transport encryption algorithms from the 
+ * {@link JWTEncryptionConfiguration} inside the criteria set. Algorithms are removed (filtered) if not supported
+ * by the runtime or are explicitly excluded.
+ */
+public class DefaultKeyTransportEncryptionAlgorithmsLookupStrategy extends AbstractEncryptionAlgorithmsLookupStrategy {
+
+    /**
+     * Constructor.
+     *
+     * @param registry the algorithm registry to used when resolving algorithm URIs. Can be {@code null}.
+     */
+    protected DefaultKeyTransportEncryptionAlgorithmsLookupStrategy(@Nullable final AlgorithmRegistry registry) {
+        super(registry);      
+    }
+    
+    @Override
+    @Nonnull public List<String> apply(
+            final CriteriaSet criteria, final Predicate<String> includeExcludePredicate) {
+        return getKeyTransportAlgorithmsFromConfiguration(criteria, includeExcludePredicate);
+    }
+    
+    
+    /**
+     * Get the key transport algorithms from the {@link JWTEncryptionConfiguration} inside
+     * the criteria set.
+     * 
+     * @param criteria to pull out the {@link JWTEncryptionConfiguration}
+     * @param includeExcludePredicate the include and exclude predicates to filter the credentials with
+     * 
+     * @return the key transport algorithms from the configuration, an empty list otherwise
+     */
+    @Nonnull protected List<String> getKeyTransportAlgorithmsFromConfiguration(final CriteriaSet criteria,
+            final Predicate<String> includeExcludePredicate){
+        if (!criteria.contains(JWTEncryptionConfigurationCriterion.class)) {
+            return Collections.emptyList();
+        }
+        final ArrayList<String> accumulator = new ArrayList<>();
+        for (final JWTEncryptionConfiguration config
+                : criteria.get(JWTEncryptionConfigurationCriterion.class).getConfigurations()) {
+            
+            config.getKeyTransportEncryptionAlgorithms()
+                .stream() 
+                .filter(PredicateSupport.and(getAlgorithmRuntimeSupportedPredicate(), includeExcludePredicate))
+                .forEach(accumulator::add);
+        }
+        return accumulator;
+    }
+
+}
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataDataEncryptionAlgorithmsLookupStrategy.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataDataEncryptionAlgorithmsLookupStrategy.java
new file mode 100644
index 0000000..1440832
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataDataEncryptionAlgorithmsLookupStrategy.java
@@ -0,0 +1,137 @@
+/*
+ * 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.util.Collections;
+import java.util.List;
+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.opensaml.xmlsec.algorithm.AlgorithmRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jose.EncryptionMethod;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
+import net.shibboleth.utilities.java.support.annotation.ParameterName;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+/**
+ * A lookup strategy that finds data/content encryption algorithms from local configuration that are compatible with those 
+ * advertised by the downstream OpenID Provider.
+ * 
+ * <p>The set of supported and configured 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. As encryption method '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 encryption methods are returned (a failure). </p>
+ */
+public class ProviderMetadataDataEncryptionAlgorithmsLookupStrategy 
+                                    extends DefaultDataEncryptionAlgorithmsLookupStrategy {
+    
+    /** Logger. */
+    @Nonnull private final Logger log = 
+            LoggerFactory.getLogger(ProviderMetadataDataEncryptionAlgorithmsLookupStrategy.class);
+    
+    /** 
+     * A strategy to locate the encryption methods ('enc') appropriate for the JWT to be encrypted.
+     * Can return {@code null} if the metadata does not describe its support algorithms (which is optional).
+     */
+    @Nonnull 
+    private final Function<OIDCProviderMetadata, List<EncryptionMethod>> providerEncryptionMethodsLookupStrategy;
+    
+    /**
+     * Constructor.
+     *
+     * @param strategy the strategy used to locate the encryption methods ('enc') from the OpenID Provider metadata
+     *                  appropriate for the JWT to be encrypted.
+     * @param registry the algorithm registry to used when resolving algorithm URIs. Can be {@code null}.
+     */
+    public ProviderMetadataDataEncryptionAlgorithmsLookupStrategy(
+            @Nonnull @ParameterName(name="providerEncryptionMethodsLookupStrategy")
+            final Function<OIDCProviderMetadata, List<EncryptionMethod>> strategy,
+            @Nullable @ParameterName(name = "AlgorithmRegistry") final AlgorithmRegistry registry){
+        super(registry);
+        providerEncryptionMethodsLookupStrategy = Constraint.isNotNull(strategy, "The provider key transport "
+                + "lookup strategy can not be null");
+    }
+    
+    /**
+     * Constructor.
+     *
+     * @param strategy the strategy used to locate the encryption methods ('enc') from the OpenID Provider metadata
+     *                  appropriate for the JWT to be encrypted.
+     */
+    public ProviderMetadataDataEncryptionAlgorithmsLookupStrategy(
+            @Nonnull @ParameterName(name="providerKeyTransportAlgorithmsLookupStrategy")
+            final Function<OIDCProviderMetadata, List<EncryptionMethod>> strategy){
+        super(null);
+        providerEncryptionMethodsLookupStrategy = Constraint.isNotNull(strategy, "The provider key transport "
+                + "lookup strategy can not be null");
+    }
+
+    @Override
+    public List<String> apply(final CriteriaSet criteria, final Predicate<String> includeExcludePredicate) {
+        
+        OIDCProviderMetadata metadata = null;
+        if (criteria.contains(ProviderMetadataCriterion.class)) {
+            metadata = criteria.get(ProviderMetadataCriterion.class).getMetadata();
+        }
+        if (metadata == null) {
+            log.debug("No provider metadata, falling back to default local behaviour");
+            return super.apply(criteria, includeExcludePredicate);
+        }
+        
+        final List<EncryptionMethod> dataEncryptionMethods = 
+                providerEncryptionMethodsLookupStrategy.apply(metadata);        
+        log.trace("Resolved effective data encryption algorithms from provider metadata: {}", dataEncryptionMethods);
+        if (dataEncryptionMethods == null) {
+            log.debug("Provider metadata does not contain supported 'enc' algorithms, "
+                    + "falling back to default local behaviour");
+            return super.apply(criteria, includeExcludePredicate);
+        }
+        
+        final List<String> dataEncryptionAlgorithmsSupported =
+                getDataEncryptionAlgorithmsFromConfiguration(criteria, includeExcludePredicate);
+        log.trace("Resolved supported data encryption algorithms from config: {}", dataEncryptionAlgorithmsSupported); 
+        
+        final List<String> supportedAndConfiguredDataEncryptionAlgorithms = 
+        findAlgorithmIntersection(dataEncryptionMethods.stream().map(EncryptionMethod::getName)
+                .collect(Collectors.toList()),dataEncryptionAlgorithmsSupported);
+        
+        log.debug("Supported and configured data encryption algorithms: {}", 
+                supportedAndConfiguredDataEncryptionAlgorithms);
+        
+        if (supportedAndConfiguredDataEncryptionAlgorithms.isEmpty()) {
+            log.warn("No supported data encryption method. Provider metadata and configuration are not compatible");
+            return Collections.emptyList();
+        }
+        return supportedAndConfiguredDataEncryptionAlgorithms;
+    }
+    
+    
+
+}
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
deleted file mode 100644
index d4df4ee..0000000
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataEncryptionParametersResolver.java
+++ /dev/null
@@ -1,414 +0,0 @@
-/*
- * 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.Key;
-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.opensaml.security.credential.UsageType;
-import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
-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.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.NotEmpty;
-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 ->  UsageType.ENCRYPTION == CredentialConversionUtil.getUsageType(k) 
-                           || UsageType.UNSPECIFIED == CredentialConversionUtil.getUsageType(k))
-                .filter(k -> checkKeyAlgorithmAndLength(k, algorithm.getName()))
-                .filter(k -> checkKeyTypeWithAlgorithm(k, 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;
-            }
-            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()); 
-            
-        }
-        
-    }
-    
-    /**
-     * Check the JWK supports the algorithm specified. If 'alg' is present, check that is identical to the 
-     * supplied algorithm. If not, check the 'kty' or key type parameter of the JWK is compatible with the 
-     * algorithm family.
-     * 
-     * @param jwk the JSON web key
-     * @param algorithm the algorithm to check compatibility with
-     * 
-     * @return true of the key is compatible with the algorithm, false otherwise
-     */
-    private boolean checkKeyTypeWithAlgorithm(@Nonnull final JWK jwk, @Nonnull final JWEAlgorithm algorithm) {
-        if (jwk.getAlgorithm() != null) {
-            // Have algorithm, so that must match exactly
-            return algorithm.equals(jwk.getAlgorithm());
-        }
-        if (JWEAlgorithm.Family.RSA.contains(algorithm) && jwk.getKeyType().equals(KeyType.RSA)) {
-            return true;
-        } else if (JWEAlgorithm.Family.ECDH_ES.contains(algorithm) && jwk.getKeyType().equals(KeyType.EC)) {
-            return true;
-        } else if (JWEAlgorithm.Family.SYMMETRIC.contains(algorithm) && jwk.getKeyType().equals(KeyType.OCT)) {
-            return true;
-        }  
-        return false;
-    }
-    
-    /**
-     * Evaluate whether the specified JWK key is supported for use with the specified algorithm URI
-     * and the key length matches.
-     * 
-     * @param jwkKey the JWK to evaluate
-     * @param algorithm the algorithm URI to evaluate against
-     * 
-     * @return true if credential may be used with the supplied algorithm URI and the key length matches, 
-     *          false otherwise
-     */
-    private boolean checkKeyAlgorithmAndLength(@Nonnull final JWK jwk, 
-            @Nonnull @NotEmpty final String algorithm) {
-        
-        final Key key = extractEncryptionKeyFromJWK(jwk);
-        if (key == null) {
-            return false;
-        }
-        
-        return AlgorithmSupport.checkKeyAlgorithmAndLength(key, getAlgorithmRegistry().get(algorithm));
-    }
-    
-    /**
-     * Extract the encryption key from the JWK.
-     * 
-     * @param jwk the JWK containing the encryption key
-     * @return the encryption key (either a public key or a secret (symmetric) key
-     */
-    @Nullable private Key extractEncryptionKeyFromJWK(@Nullable final JWK jwk) {
-        if (jwk == null) {
-            return null;
-        }
-        try {
-            if (jwk.getKeyType() == KeyType.EC) {
-                return jwk.toECKey().toPublicKey();
-            } else if (jwk.getKeyType() == KeyType.RSA) {
-                return jwk.toRSAKey().toPublicKey();
-            } else if (jwk.getKeyType() == KeyType.OCT) {
-                return jwk.toOctetSequenceKey().toSecretKey();
-            } else {
-                return null;
-            }
-            //TODO support for OKP
-        } catch (final JOSEException e) {
-            log.trace("Unable to extract encryption key from JWK '{}'", jwk.getKeyID());
-            return null;
-        }
-        
-    }
-    
-    /**
-     * 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/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataKeyTransportEncryptionAlgorithmsLookupStrategy.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataKeyTransportEncryptionAlgorithmsLookupStrategy.java
new file mode 100644
index 0000000..0b88e00
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataKeyTransportEncryptionAlgorithmsLookupStrategy.java
@@ -0,0 +1,138 @@
+/*
+ * 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.util.Collections;
+import java.util.List;
+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.opensaml.xmlsec.algorithm.AlgorithmRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
+import net.shibboleth.utilities.java.support.annotation.ParameterName;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+/**
+ * A lookup strategy that finds key transport algorithms from local configuration that are compatible with those 
+ * advertised by the downstream OpenID Provider.
+ * 
+ * <p>The set of supported and configured key transport ('alg') algorithms 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. As algorithm 'alg' 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 algorithms are returned (a failure). </p>
+ */
+public class ProviderMetadataKeyTransportEncryptionAlgorithmsLookupStrategy 
+                                    extends DefaultKeyTransportEncryptionAlgorithmsLookupStrategy {
+    
+    /** Logger. */
+    @Nonnull private final Logger log = 
+            LoggerFactory.getLogger(ProviderMetadataKeyTransportEncryptionAlgorithmsLookupStrategy.class);
+    
+    /** 
+     * A strategy to locate the algorithms ('alg') appropriate for the JWT to be encrypted.
+     * Can return {@code null} if the metadata does not describe its support algorithms (which is optional).
+     */
+    @Nonnull 
+    private final Function<OIDCProviderMetadata, List<JWEAlgorithm>> providerKeyTransportAlgorithmsLookupStrategy;
+    
+    /**
+     * Constructor.
+     *
+     * @param strategy the strategy used to locate the algorithms ('alg') from the OpenID Provider metadata
+     *                  appropriate for the JWT to be encrypted.
+     * @param registry the algorithm registry to used when resolving algorithm URIs. Can be {@code null}.
+     */
+    public ProviderMetadataKeyTransportEncryptionAlgorithmsLookupStrategy(
+            @Nonnull @ParameterName(name="providerKeyTransportAlgorithmsLookupStrategy")
+            final Function<OIDCProviderMetadata, List<JWEAlgorithm>> strategy,
+            @Nullable @ParameterName(name = "AlgorithmRegistry") final AlgorithmRegistry registry){
+        super(registry);
+        providerKeyTransportAlgorithmsLookupStrategy = Constraint.isNotNull(strategy, "The provider key transport "
+                + "lookup strategy can not be null");
+    }
+    
+    /**
+     * Constructor.
+     *
+     * @param strategy the strategy used to locate the algorithms ('alg') from the OpenID Provider metadata
+     *                  appropriate for the JWT to be encrypted.
+     */
+    public ProviderMetadataKeyTransportEncryptionAlgorithmsLookupStrategy(
+            @Nonnull @ParameterName(name="providerKeyTransportAlgorithmsLookupStrategy")
+            final Function<OIDCProviderMetadata, List<JWEAlgorithm>> strategy){
+        super(null);
+        providerKeyTransportAlgorithmsLookupStrategy = Constraint.isNotNull(strategy, "The provider key transport "
+                + "lookup strategy can not be null");
+    }
+
+    @Override
+    public List<String> apply(final CriteriaSet criteria, final Predicate<String> includeExcludePredicate) {
+        
+        OIDCProviderMetadata metadata = null;
+        if (criteria.contains(ProviderMetadataCriterion.class)) {
+            metadata = criteria.get(ProviderMetadataCriterion.class).getMetadata();
+        }
+        if (metadata == null) {
+            log.debug("No provider metadata, falling back to default local behaviour");
+            return super.apply(criteria, includeExcludePredicate);
+        }
+        
+        // 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 == null) {
+            log.debug("Provider metadata does not contain supported 'alg' algorithms, "
+                    + "falling back to default local behaviour");
+            return super.apply(criteria, includeExcludePredicate);
+        }
+        
+        final List<String> keyTransportAlgorithmSupported =
+                getKeyTransportAlgorithmsFromConfiguration(criteria, includeExcludePredicate);
+        log.trace("Resolved supported key transport algorithms from config: {}", 
+                keyTransportAlgorithmSupported); 
+        
+        final List<String> supportedAndConfiguredKeyTransportAlgorithms = 
+                findAlgorithmIntersection(keyTransportAlgorithms.stream().map(JWEAlgorithm::getName)
+                        .collect(Collectors.toList()),keyTransportAlgorithmSupported);
+        
+        log.debug("Supported and configured key transport algorithms: {}", 
+                supportedAndConfiguredKeyTransportAlgorithms);
+        
+        if (supportedAndConfiguredKeyTransportAlgorithms.isEmpty()) {
+            log.warn("No supported key transport algorithm. Provider metadata and configuration are not compatible");
+            return Collections.emptyList();
+        }
+        return supportedAndConfiguredKeyTransportAlgorithms;
+    }
+
+
+}
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
new file mode 100644
index 0000000..1ca8e86
--- /dev/null
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolverTest.java
@@ -0,0 +1,220 @@
+/*
+ * 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.credential.impl;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+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.ArrayList;
+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.security.credential.Credential;
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.security.criteria.KeyAlgorithmCriterion;
+import org.opensaml.security.criteria.UsageCriterion;
+import org.opensaml.storage.StorageService;
+import org.opensaml.storage.impl.MemoryStorageService;
+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.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.jwk.RemoteJwkSetCache;
+import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+public class ProviderMetadataCredentialResolverTest {
+    
+    private ProviderMetadataCredentialResolver resolver;
+    
+    /** A remote JWKSet.*/
+    private static final ClassPathResource REMOTE_JWKSET = 
+            new ClassPathResource("/credentials/test-provider-resolver-remote-jwkset-response.jwk"); 
+    
+    /** A remote JWKSet with no keys.*/
+    private static final ClassPathResource REMOTE_JWKSET_NO_KEYS = 
+            new ClassPathResource("/credentials/test-provider-resolver-remote-jwkset-response-nokeys.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");
+    
+    /**
+     * 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");
+    
+    private CriteriaSet criteria;
+    
+    /** The cache being used by the resolver.*/
+    private RemoteJwkSetCache cache;
+    
+    /**
+     * 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;
+        }
+    }
+    
+    
+    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;
+    }
+    
+    
+    @BeforeMethod
+    public void setup() throws Exception {
+        resolver = new ProviderMetadataCredentialResolver();
+        cache = new RemoteJwkSetCache();
+        cache.setStorage(buildStorageService());
+        cache.setHttpClient(createMockHttpClient(readJsonFromFile(REMOTE_JWKSET)));
+        resolver.setRemoteJwkSetCache(cache);
+        criteria = new CriteriaSet();
+        
+        criteria.add(new ProviderMetadataCriterion(
+                OIDCProviderMetadata.parse(readJsonFromFile(GOOD_PROVIDER_CONFIGURATION_INFO))));
+    }
+    
+    @Test(expectedExceptions = ResolverException.class)
+    public void testFail_IncorrectCriteria() throws Exception {
+        resolver.initialize();
+        // Needs ProviderMetadataCriterion
+        criteria = new CriteriaSet();        
+        final Iterable<Credential> creds = resolver.resolve(criteria);
+    }
+    
+    @Test
+    public void testSuccess_ForEncryption() throws Exception {
+        resolver.initialize();
+        
+        criteria.add(new UsageCriterion(UsageType.ENCRYPTION));
+        
+        final Iterable<Credential> creds = resolver.resolve(criteria);
+        
+        assertNotNull(creds);
+        final List<Credential> credsList = new ArrayList<>();
+        creds.forEach(credsList::add);        
+        // There are two 'enc' keys in the keyset, which should match the usage criterion
+        assertEquals(credsList.size(), 2);
+    }
+    
+    @Test
+    public void testSuccess_NoKeyArray() throws Exception {
+        cache.setHttpClient(createMockHttpClient(readJsonFromFile(REMOTE_JWKSET_NO_KEY_ARRAY)));
+        resolver.initialize();
+        
+        criteria.add(new UsageCriterion(UsageType.ENCRYPTION));
+        
+        final Iterable<Credential> creds = resolver.resolve(criteria);
+        
+        assertNotNull(creds);
+        final List<Credential> credsList = new ArrayList<>();
+        creds.forEach(credsList::add);        
+        // No keys
+        assertEquals(credsList.size(), 0);
+    }
+    
+    @Test
+    public void testSuccess_NoKeys() throws Exception {
+        cache.setHttpClient(createMockHttpClient(readJsonFromFile(REMOTE_JWKSET_NO_KEYS)));
+        resolver.initialize();
+        
+        criteria.add(new UsageCriterion(UsageType.ENCRYPTION));
+        
+        final Iterable<Credential> creds = resolver.resolve(criteria);
+        
+        assertNotNull(creds);
+        final List<Credential> credsList = new ArrayList<>();
+        creds.forEach(credsList::add);        
+        // No keys
+        assertEquals(credsList.size(), 0);
+    }
+    
+    @Test
+    public void testSuccess_ForEncryptionAndKeyAlg() throws Exception {
+        resolver.initialize();
+        
+        criteria.add(new UsageCriterion(UsageType.ENCRYPTION));
+        criteria.add(new KeyAlgorithmCriterion("RSA"));
+        
+        final Iterable<Credential> creds = resolver.resolve(criteria);
+        
+        assertNotNull(creds);
+        final List<Credential> credsList = new ArrayList<>();
+        creds.forEach(credsList::add);        
+        // There is one 'enc' RSA-OAEP in the keyset.
+        assertEquals(credsList.size(), 1);
+    }
+    
+    @Test
+    public void testSuccess_ForSigning() throws Exception {
+        resolver.initialize();
+        
+        criteria.add(new UsageCriterion(UsageType.SIGNING));
+        
+        final Iterable<Credential> creds = resolver.resolve(criteria);
+        
+        assertNotNull(creds);
+        final List<Credential> credsList = new ArrayList<>();
+        creds.forEach(credsList::add);        
+        // There are 5 'sig' keys in the keyset, which should match the usage criterion
+        assertEquals(credsList.size(), 5);
+    }
+
+}
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/DefaultDataEncryptionAlgorithmsLookupStrategyTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/DefaultDataEncryptionAlgorithmsLookupStrategyTest.java
new file mode 100644
index 0000000..2f2fdcd
--- /dev/null
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/DefaultDataEncryptionAlgorithmsLookupStrategyTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.assertNotNull;
+
+import java.util.List;
+
+import org.opensaml.xmlsec.algorithm.AlgorithmRegistry;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+
+import net.shibboleth.oidc.jwa.algorithm.descriptors.EncryptionA128CBCHS256;
+import net.shibboleth.oidc.jwa.algorithm.descriptors.EncryptionA128GCM;
+import net.shibboleth.oidc.jwa.algorithm.descriptors.EncryptionA192CBCHS384;
+import net.shibboleth.oidc.jwa.algorithm.descriptors.EncryptionA256CBCHS512;
+import net.shibboleth.oidc.jwa.support.EncryptionConstants;
+import net.shibboleth.oidc.security.criterion.JWTEncryptionConfigurationCriterion;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+public class DefaultDataEncryptionAlgorithmsLookupStrategyTest {
+    
+    private DefaultDataEncryptionAlgorithmsLookupStrategy strategy;
+    
+    private BasicJWTEncryptionConfiguration config;
+    
+    private CriteriaSet criteria;
+    
+    
+    @BeforeMethod
+    public void setup() {
+        
+        //FIXME should be mocked really. 
+        final AlgorithmRegistry algorithmRegistry = new AlgorithmRegistry();
+        algorithmRegistry.register(new EncryptionA128GCM());
+        algorithmRegistry.register(new EncryptionA128CBCHS256());
+        algorithmRegistry.register(new EncryptionA256CBCHS512());
+        algorithmRegistry.register(new EncryptionA192CBCHS384());
+        
+        config = new BasicJWTEncryptionConfiguration();
+        config.setDataEncryptionAlgorithms(
+                List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256,EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM,
+                        EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512, EncryptionConstants.ALGO_ID_ENC_ALG_A192CBC_HS384));
+
+        criteria = new CriteriaSet(new JWTEncryptionConfigurationCriterion(List.of(config)));   
+        
+        strategy = new DefaultDataEncryptionAlgorithmsLookupStrategy(algorithmRegistry);
+    }
+    
+    @Test
+    public void testSuccess_AllAlgs() {                
+        final List<String> keyAlgs = strategy.apply(criteria, Predicates.alwaysTrue());        
+        assertNotNull(keyAlgs);
+        assertEquals(keyAlgs.size(), 4);
+    }    
+    
+    @Test
+    public void testSuccess_SomeAlgsNotSupported() {      
+        final AlgorithmRegistry algorithmRegistry = new AlgorithmRegistry();
+        algorithmRegistry.register(new EncryptionA128GCM());
+        algorithmRegistry.register(new EncryptionA128CBCHS256());
+        
+        strategy = new DefaultDataEncryptionAlgorithmsLookupStrategy(algorithmRegistry);
+        
+        final List<String> keyAlgs = strategy.apply(criteria, Predicates.alwaysTrue());        
+        assertNotNull(keyAlgs);
+        assertEquals(keyAlgs.size(), 2);
+    }
+    
+    @Test
+    public void testSuccess_SomeAlgsEcluded() {                
+        final List<String> keyAlgs = strategy.apply(
+                criteria, alg -> !EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256.equals(alg));        
+        assertNotNull(keyAlgs);
+        assertEquals(keyAlgs.size(), 3);
+    }
+
+}
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/DefaultJWTEncryptionParametersResolverTest.java
similarity index 61%
rename from oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolverTest.java
rename to oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/DefaultJWTEncryptionParametersResolverTest.java
index 25c83b1..8f5ecca 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/DefaultJWTEncryptionParametersResolverTest.java
@@ -24,9 +24,15 @@ import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import org.opensaml.core.config.InitializationException;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.security.credential.impl.AbstractCriteriaFilteringCredentialResolver;
+import org.opensaml.security.criteria.UsageCriterion;
 import org.opensaml.xmlsec.config.GlobalAlgorithmRegistryInitializer;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -43,16 +49,19 @@ import net.shibboleth.oidc.jwa.support.EncryptionConstants;
 import net.shibboleth.oidc.jwa.support.KeyManagementConstants;
 import net.shibboleth.oidc.security.JWTEncryptionParameters;
 import net.shibboleth.oidc.security.credential.BasicJWKCredential;
+import net.shibboleth.oidc.security.credential.JOSEObjectCredentialResolver;
 import net.shibboleth.oidc.security.credential.JWKCredential;
 import net.shibboleth.oidc.security.criterion.JWTEncryptionConfigurationCriterion;
+import net.shibboleth.oidc.security.criterion.StaticCredentialCriterion;
 import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
 
-public class BasicJWTEncryptionParametersResolverTest {
+public class DefaultJWTEncryptionParametersResolverTest {
     
     /** The mock symmetric key e.g. for keywrap.*/
     private static final String SYMMETRIC_KEY = "/A?D(G+KbPdSgVkYp3s6v9y$B&E)H at Mc";
     
-    private BasicJWTEncryptionParametersResolver resolver;
+    private DefaultJWTEncryptionParametersResolver resolver;
     
     private BasicJWTEncryptionConfiguration config;
             
@@ -67,9 +76,48 @@ public class BasicJWTEncryptionParametersResolverTest {
             fail();
         }
         
-        resolver = new BasicJWTEncryptionParametersResolver();
+        resolver = new DefaultJWTEncryptionParametersResolver();        
     }
     
+    /** Mock static credential resolver that is filterable.*/
+    private static class MockStaticCriteriaFilteringCredentialResolver extends AbstractCriteriaFilteringCredentialResolver
+        implements JOSEObjectCredentialResolver {
+
+
+        @Override
+        protected Iterable<Credential> resolveFromSource(final CriteriaSet criteriaSet) throws ResolverException {
+            if (criteriaSet.contains(StaticCredentialCriterion.class)) {
+                final StaticCredentialCriterion credentialCriterion = 
+                        criteriaSet.get(StaticCredentialCriterion.class);
+                final Credential credential = credentialCriterion.getCredential();          
+                return List.of(credential);               
+                
+            } else {
+                return Collections.emptyList();
+            }
+        }
+    
+    }
+    
+    /** Mock static credential resolver.*/
+    private static class MockStaticFilteringCredentialResolver 
+                    extends AbstractCriteriaFilteringCredentialResolver implements JOSEObjectCredentialResolver {
+
+        private final List<Credential> creds;
+        
+        public MockStaticFilteringCredentialResolver(final List<Credential> otherCredentials) {
+            creds = otherCredentials;
+        }
+
+        @Override
+        protected Iterable<Credential> resolveFromSource(final CriteriaSet criteriaSet) throws ResolverException {
+           return creds;
+        }
+    
+    }
+    
+    
+    
     private CriteriaSet buildBasicCriteriaSet() throws Exception {        
         
         config = new BasicJWTEncryptionConfiguration();
@@ -79,19 +127,30 @@ public class BasicJWTEncryptionParametersResolverTest {
         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)));       
+        final CriteriaSet criteria = new CriteriaSet(new JWTEncryptionConfigurationCriterion(List.of(config)));   
+        criteria.add(new UsageCriterion(UsageType.ENCRYPTION));
         return criteria;
     }
-
+    
     @Test
     public void testBasicRSA() throws Exception {
         final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(
+                List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256));        
+        
+        final List<Credential> creds = new ArrayList<>();
         final RSAKey key = new RSAKeyGenerator(2048)
                 .algorithm(JWEAlgorithm.RSA_OAEP_256)
                 .keyUse(KeyUse.ENCRYPTION)
-                .keyID("mock-key")
+                .keyID("mock-key-rsa")
                 .generate();
-        config.setKeyTransportEncryptionCredentials(List.of(TestCredentialHelper.createKeyEncryptionCredential(key)));
+        creds.add(TestCredentialHelper.createKeyEncryptionCredential(key));
+        creds.add(TestCredentialHelper.createSharedSecretCredential("mock-key-client-secret",
+                SYMMETRIC_KEY, JWEAlgorithm.A256KW, UsageType.UNSPECIFIED));
+
+        
+        config.setKEKCredentialResolver(new MockStaticFilteringCredentialResolver(creds));
+        
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
         assertNotNull(param);
@@ -105,18 +164,49 @@ public class BasicJWTEncryptionParametersResolverTest {
        
     }
     
+    @Test
+    public void testBasicRSA_WrongAlgorithmInConfig() throws Exception {
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(
+                List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP));        
+        
+        final List<Credential> creds = new ArrayList<>();
+        final RSAKey key = new RSAKeyGenerator(2048)
+                .algorithm(JWEAlgorithm.RSA_OAEP_256)
+                .keyUse(KeyUse.ENCRYPTION)
+                .keyID("mock-key-rsa")
+                .generate();
+        creds.add(TestCredentialHelper.createKeyEncryptionCredential(key));
+        creds.add(TestCredentialHelper.createSharedSecretCredential("mock-key-client-secret",
+                SYMMETRIC_KEY, JWEAlgorithm.A256KW, UsageType.UNSPECIFIED));
+
+        
+        config.setKEKCredentialResolver(new MockStaticFilteringCredentialResolver(creds));
+        
+        final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
+        
+        assertNull(param);
+
+       
+    }
+    
     @Test
     public void testBasicRSA_NoAlgorithmSpecifiedInJWKCredential() throws Exception {
         final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(
+                List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256));        
+        
+        final List<Credential> creds = new ArrayList<>();
         final RSAKey key = new RSAKeyGenerator(2048)
                 .algorithm(JWEAlgorithm.RSA_OAEP_256)
                 .keyUse(KeyUse.ENCRYPTION)
-                .keyID("mock-key")
+                .keyID("mock-key-rsa")
                 .generate();
         final JWKCredential cred = TestCredentialHelper.createKeyEncryptionCredential(key);
-        // Blank algorithm, this should work on the 'key algorithm' alone 
         ((BasicJWKCredential)cred).setAlgorithm(null);
-        config.setKeyTransportEncryptionCredentials(List.of(cred));
+        creds.add(cred);
+        config.setKEKCredentialResolver(new MockStaticFilteringCredentialResolver(creds));
+        
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
         assertNotNull(param);
@@ -131,45 +221,54 @@ public class BasicJWTEncryptionParametersResolverTest {
     }
     
     @Test
-    public void testBasicEC() throws Exception {
+    public void testBasicRSA_WrongAlgorithmInFirstKey_ChooseSecondKey() throws Exception {
         final CriteriaSet criteria =  buildBasicCriteriaSet();
-        config.setKeyTransportEncryptionAlgorithms(
-                List.of(KeyManagementConstants.ALGO_ID_ALG_ECDH_ES));
-        final ECKey key = new ECKeyGenerator(Curve.P_256)                
+        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256));
+        final RSAKey key = new RSAKeyGenerator(2048)
+                .algorithm(JWEAlgorithm.RSA_OAEP)
                 .keyUse(KeyUse.ENCRYPTION)
-                .algorithm(JWEAlgorithm.ECDH_ES)
-                .keyID("mock-key")
+                .keyID("mock-key-wrong-type")
                 .generate();
-        config.setKeyTransportEncryptionCredentials(List.of(TestCredentialHelper.createKeyAgreementCredential(key)));
+        final RSAKey keyCorrect = new RSAKeyGenerator(2048)
+                .algorithm(JWEAlgorithm.RSA_OAEP_256)
+                .keyUse(KeyUse.ENCRYPTION)
+                .keyID("mock-key-correct-type")
+                .generate();
+        final List<Credential> creds = new ArrayList<>();
+        creds.add(TestCredentialHelper.createKeyEncryptionCredential(key)); 
+        creds.add(TestCredentialHelper.createKeyEncryptionCredential(keyCorrect));
+        config.setKEKCredentialResolver(new MockStaticFilteringCredentialResolver(creds));
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
         assertNotNull(param);
         assertNotNull(param.getDataEncryptionAlgorithm());
         assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
         assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256);
-        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_ECDH_ES);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256);
         assertNotNull(param.getKeyTransportEncryptionCredential());
         assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
-        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"EC");
-       
+        assertTrue(param.getKeyTransportEncryptionCredential().getKeyNames().contains("mock-key-correct-type"));
     }
     
     @Test
-    public void testBasicEC_NoAlgorithmSpecifiedInJWKCredential() throws Exception {
+    public void testBasicEC() throws Exception {
         final CriteriaSet criteria =  buildBasicCriteriaSet();
         config.setKeyTransportEncryptionAlgorithms(
-                List.of(KeyManagementConstants.ALGO_ID_ALG_ECDH_ES));
+                List.of(KeyManagementConstants.ALGO_ID_ALG_ECDH_ES));        
+        
+        final List<Credential> creds = new ArrayList<>();
         final ECKey key = new ECKeyGenerator(Curve.P_256)                
                 .keyUse(KeyUse.ENCRYPTION)
                 .algorithm(JWEAlgorithm.ECDH_ES)
                 .keyID("mock-key")
                 .generate();
+        creds.add(TestCredentialHelper.createKeyAgreementCredential(key));
+        creds.add(TestCredentialHelper.createSharedSecretCredential("mock-key-client-secret",
+                SYMMETRIC_KEY, JWEAlgorithm.A256KW, UsageType.UNSPECIFIED));
+
         
-        final JWKCredential cred = TestCredentialHelper.createKeyAgreementCredential(key);
-        ((BasicJWKCredential)cred).setAlgorithm(null);
-        config.setKeyTransportEncryptionCredentials(List.of(cred));
+        config.setKEKCredentialResolver(new MockStaticFilteringCredentialResolver(creds));
         
-        config.setKeyTransportEncryptionCredentials(List.of(cred));
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
         assertNotNull(param);
@@ -182,39 +281,19 @@ public class BasicJWTEncryptionParametersResolverTest {
         assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"EC");
        
     }
+ 
     
     @Test
     public void testBasicAESKeyWrap() throws Exception {
         final CriteriaSet criteria =  buildBasicCriteriaSet();
         config.setKeyTransportEncryptionAlgorithms(
                 List.of(KeyManagementConstants.ALGO_ID_ALG_AES_256_KW));        
-        config.setKeyTransportEncryptionCredentials(List.of(TestCredentialHelper.createSharedSecretCredential("mock-key",
-                SYMMETRIC_KEY, JWEAlgorithm.A256KW)));
-        final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
-        assertNotNull(param);
-        assertNotNull(param.getDataEncryptionAlgorithm());
-        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
-        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256);
-        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_AES_256_KW);
-        assertNotNull(param.getKeyTransportEncryptionCredential());
-        assertNotNull(param.getKeyTransportEncryptionCredential().getSecretKey());
-        assertEquals(param.getKeyTransportEncryptionCredential().getSecretKey().getAlgorithm(),"AES");
-       
-    }
-
-    @Test
-    public void testBasicAESKeyWrap_NoAlgorithmSpecifiedInJWKCredential() throws Exception {
-        final CriteriaSet criteria =  buildBasicCriteriaSet();
-        config.setKeyTransportEncryptionAlgorithms(
-                List.of(KeyManagementConstants.ALGO_ID_ALG_AES_256_KW));  
+        criteria.add(new StaticCredentialCriterion(TestCredentialHelper.createSharedSecretCredential("mock-key",
+                SYMMETRIC_KEY, JWEAlgorithm.A256KW, UsageType.UNSPECIFIED)));
         
-        final JWKCredential cred = TestCredentialHelper.createSharedSecretCredential("mock-key",
-                SYMMETRIC_KEY, JWEAlgorithm.A256KW);
-        // Blank algorithm, this should work on the 'key algorithm' alone 
-        ((BasicJWKCredential)cred).setAlgorithm(null);
+        config.setKEKCredentialResolver(new MockStaticCriteriaFilteringCredentialResolver());
         
-        config.setKeyTransportEncryptionCredentials(List.of(cred));
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
         assertNotNull(param);
@@ -228,187 +307,213 @@ public class BasicJWTEncryptionParametersResolverTest {
        
     }
     
-    /* Dir is not supported by the runtime.*/
-    @Test(enabled = false)
-    public void testBasicDirectEncryption() throws Exception {
+    /* The key is 256bit and does not support the 128bit enc. algo.*/
+    @Test
+    public void testBasicAESKeyWrap_WrongKeyLength() throws Exception {
         final CriteriaSet criteria =  buildBasicCriteriaSet();
         config.setKeyTransportEncryptionAlgorithms(
-                List.of(KeyManagementConstants.ALGO_ID_ALG_DIR));        
-        config.setDataEncryptionCredentials(List.of(TestCredentialHelper.createSharedSecretCredential("mock-key",
-                SYMMETRIC_KEY, JWEAlgorithm.DIR)));
+                List.of(KeyManagementConstants.ALGO_ID_ALG_AES_128_KW));        
+        config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256));
+        
+        criteria.add(new StaticCredentialCriterion(TestCredentialHelper.createSharedSecretCredential("mock-key",
+                SYMMETRIC_KEY, JWEAlgorithm.A256KW, UsageType.UNSPECIFIED)));
+        
+        config.setKEKCredentialResolver(new MockStaticCriteriaFilteringCredentialResolver());
         
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);            
-        assertNotNull(param);
-        assertNotNull(param.getDataEncryptionAlgorithm());
-        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
-        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512);
-        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_DIR);
-        assertNotNull(param.getDataEncryptionCredential());
-        assertNotNull(param.getDataEncryptionCredential().getSecretKey());
-        assertEquals(param.getDataEncryptionCredential().getSecretKey().getAlgorithm(),"AES");
-
+        assertNull(param);
     }
     
-    /* Dir is not supported by the runtime.*/
-    @Test(enabled = false)
-    public void testBasicDirectEncryption_WhereOtherAlgsSupported() throws Exception {
+    @Test
+    public void testBasicAESKeyWrapWhenRSAKeyAlsoExists() throws Exception {
         final CriteriaSet criteria =  buildBasicCriteriaSet();
         config.setKeyTransportEncryptionAlgorithms(
-                List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256, KeyManagementConstants.ALGO_ID_ALG_DIR));        
-        config.setDataEncryptionCredentials(List.of(TestCredentialHelper.createSharedSecretCredential("mock-key",
-                SYMMETRIC_KEY, JWEAlgorithm.DIR)));
+                List.of(KeyManagementConstants.ALGO_ID_ALG_AES_256_KW));        
+        
+        final List<Credential> creds = new ArrayList<>();
+        final RSAKey key = new RSAKeyGenerator(2048)
+                .algorithm(JWEAlgorithm.RSA_OAEP_256)
+                .keyUse(KeyUse.ENCRYPTION)
+                .keyID("mock-key-rsa")
+                .generate();
+        creds.add(TestCredentialHelper.createKeyEncryptionCredential(key));
+        creds.add(TestCredentialHelper.createSharedSecretCredential("mock-key-client-secret",
+                SYMMETRIC_KEY, JWEAlgorithm.A256KW, UsageType.UNSPECIFIED));
+
+        
+        config.setKEKCredentialResolver(new MockStaticFilteringCredentialResolver(creds));
+        
+        final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
-        final JWTEncryptionParameters param = resolver.resolveSingle(criteria);            
         assertNotNull(param);
         assertNotNull(param.getDataEncryptionAlgorithm());
         assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
-        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512);
-        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_DIR);
-        assertNotNull(param.getDataEncryptionCredential());
-        assertNotNull(param.getDataEncryptionCredential().getSecretKey());
-        assertEquals(param.getDataEncryptionCredential().getSecretKey().getAlgorithm(),"AES");
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_AES_256_KW);
+        assertNotNull(param.getKeyTransportEncryptionCredential());
+        assertNotNull(param.getKeyTransportEncryptionCredential().getSecretKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getSecretKey().getAlgorithm(),"AES");
+       
     }
     
-    /* The key is 256bit and does not support the 128bit enc. algo.*/
+    /* Same as the above test, but more algorithms are allowed, so check it picks the first one first.*/
     @Test
-    public void testBasicDirectEncryption_NoSupportedDataEncryptionMethods() throws Exception {
+    public void testBasicAESKeyWrapWhenRSAKeyAlsoExists_CheckOrderingIsPreserved() throws Exception {
         final CriteriaSet criteria =  buildBasicCriteriaSet();
         config.setKeyTransportEncryptionAlgorithms(
-                List.of(KeyManagementConstants.ALGO_ID_ALG_DIR));        
-        config.setDataEncryptionCredentials(List.of(TestCredentialHelper.createSharedSecretCredential("mock-key",
-                SYMMETRIC_KEY, JWEAlgorithm.DIR)));
-        config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256));
+                List.of(KeyManagementConstants.ALGO_ID_ALG_AES_256_KW, 
+                KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256));        
         
-        final JWTEncryptionParameters param = resolver.resolveSingle(criteria);            
-        assertNull(param);
-    }
-    
-    /* Dir is not supported by the runtime.*/
-    @Test(enabled = false)
-    public void testBasicDirectEncryption_WhereOtherKeysExist_DirFirst() throws Exception {
-        final CriteriaSet criteria =  buildBasicCriteriaSet();
-        config.setKeyTransportEncryptionAlgorithms(
-                List.of(KeyManagementConstants.ALGO_ID_ALG_DIR, KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256)); 
+        final List<Credential> creds = new ArrayList<>();
         final RSAKey key = new RSAKeyGenerator(2048)
                 .algorithm(JWEAlgorithm.RSA_OAEP_256)
                 .keyUse(KeyUse.ENCRYPTION)
-                .keyID("mock-key")
+                .keyID("mock-key-rsa")
                 .generate();
-        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));
+        creds.add(TestCredentialHelper.createKeyEncryptionCredential(key));
+        creds.add(TestCredentialHelper.createSharedSecretCredential("mock-key-client-secret",
+                SYMMETRIC_KEY, JWEAlgorithm.A256KW, UsageType.UNSPECIFIED));
+
+        
+        config.setKEKCredentialResolver(new MockStaticFilteringCredentialResolver(creds));
+        
+        final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
-        final JWTEncryptionParameters param = resolver.resolveSingle(criteria);            
         assertNotNull(param);
         assertNotNull(param.getDataEncryptionAlgorithm());
         assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
-        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512);
-        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_DIR);
-        assertNotNull(param.getDataEncryptionCredential());
-        assertNotNull(param.getDataEncryptionCredential().getSecretKey());
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_AES_256_KW);
+        assertNotNull(param.getKeyTransportEncryptionCredential());
+        assertNotNull(param.getKeyTransportEncryptionCredential().getSecretKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getSecretKey().getAlgorithm(),"AES");
+       
     }
     
+    /* Same as the above test, but reversed the algorithms.*/
     @Test
-    public void testBasicDirectEncryption_WhereOtherKeysExist_RSAFirst() throws Exception {
+    public void testBasicAESKeyWrapWhenRSAKeyAlsoExists_CheckOrderingIsPreserved_RSAFirst() throws Exception {
         final CriteriaSet criteria =  buildBasicCriteriaSet();
         config.setKeyTransportEncryptionAlgorithms(
-                List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256,KeyManagementConstants.ALGO_ID_ALG_DIR)); 
+                List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256, 
+                        KeyManagementConstants.ALGO_ID_ALG_AES_256_KW));        
+        
+        final List<Credential> creds = new ArrayList<>();
         final RSAKey key = new RSAKeyGenerator(2048)
                 .algorithm(JWEAlgorithm.RSA_OAEP_256)
                 .keyUse(KeyUse.ENCRYPTION)
-                .keyID("mock-key")
+                .keyID("mock-key-rsa")
                 .generate();
-        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));
+        creds.add(TestCredentialHelper.createKeyEncryptionCredential(key));
+        creds.add(TestCredentialHelper.createSharedSecretCredential("mock-key-client-secret",
+                SYMMETRIC_KEY, JWEAlgorithm.A256KW, UsageType.UNSPECIFIED));
+
+        
+        config.setKEKCredentialResolver(new MockStaticFilteringCredentialResolver(creds));
+        
+        final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
-        final JWTEncryptionParameters param = resolver.resolveSingle(criteria);            
         assertNotNull(param);
         assertNotNull(param.getDataEncryptionAlgorithm());
         assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
-        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512);
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256);
         assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256);
         assertNotNull(param.getKeyTransportEncryptionCredential());
         assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
         assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"RSA");
+       
     }
-    
+
     @Test
-    public void testBasicRSA_WrongAlgorithmInKey() throws Exception {
+    public void testBasicAESKeyWrap_NoAlgorithmSpecifiedInJWKCredential() throws Exception {
         final CriteriaSet criteria =  buildBasicCriteriaSet();
-        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256));
-        final RSAKey key = new RSAKeyGenerator(2048)
-                .algorithm(JWEAlgorithm.RSA_OAEP)
-                .keyUse(KeyUse.ENCRYPTION)
-                .keyID("mock-key")
-                .generate();
-        config.setKeyTransportEncryptionCredentials(List.of(TestCredentialHelper.createKeyEncryptionCredential(key)));
-        final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
+        config.setKeyTransportEncryptionAlgorithms(
+                List.of(KeyManagementConstants.ALGO_ID_ALG_AES_256_KW));  
         
-        assertNull(param);           
-    }
-    
-    @Test
-    public void testBasicRSA_WrongAlgorithmInFirstKey_ChooseSecondKey() throws Exception {
-        final CriteriaSet criteria =  buildBasicCriteriaSet();
-        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256));
-        final RSAKey key = new RSAKeyGenerator(2048)
-                .algorithm(JWEAlgorithm.RSA_OAEP)
-                .keyUse(KeyUse.ENCRYPTION)
-                .keyID("mock-key-wrong-type")
-                .generate();
-        final RSAKey keyCorrect = new RSAKeyGenerator(2048)
-                .algorithm(JWEAlgorithm.RSA_OAEP_256)
-                .keyUse(KeyUse.ENCRYPTION)
-                .keyID("mock-key-correct-type")
-                .generate();
-        config.setKeyTransportEncryptionCredentials(List.of(TestCredentialHelper.createKeyEncryptionCredential(key),
-                TestCredentialHelper.createKeyEncryptionCredential(keyCorrect)));
+        final JWKCredential cred = TestCredentialHelper.createSharedSecretCredential("mock-key",
+                SYMMETRIC_KEY, JWEAlgorithm.A256KW, UsageType.UNSPECIFIED);
+        // Blank algorithm, this should work on the 'key algorithm' alone 
+        ((BasicJWKCredential)cred).setAlgorithm(null);
+        
+        criteria.add(new StaticCredentialCriterion(cred));
+        config.setKEKCredentialResolver(new MockStaticCriteriaFilteringCredentialResolver());
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
         assertNotNull(param);
         assertNotNull(param.getDataEncryptionAlgorithm());
         assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
         assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256);
-        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_AES_256_KW);
         assertNotNull(param.getKeyTransportEncryptionCredential());
-        assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
-        assertTrue(param.getKeyTransportEncryptionCredential().getKeyNames().contains("mock-key-correct-type"));
+        assertNotNull(param.getKeyTransportEncryptionCredential().getSecretKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getSecretKey().getAlgorithm(),"AES");
+       
     }
     
     @Test
-    public void testBasicRSAAndDir_NoSuitableKeys() throws Exception {
+    public void testBasicAESKeyWrap_WrongUsageType() throws Exception {
         final CriteriaSet criteria =  buildBasicCriteriaSet();
-        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256, 
-                KeyManagementConstants.ALGO_ID_ALG_DIR));
-        final RSAKey key = new RSAKeyGenerator(2048)
-                .algorithm(JWEAlgorithm.RSA_OAEP)
-                .keyUse(KeyUse.ENCRYPTION)
-                .keyID("mock-key-wrong-type")
-                .generate();        
-        config.setKeyTransportEncryptionCredentials(List.of(TestCredentialHelper.createKeyEncryptionCredential(key)));
-        config.setDataEncryptionCredentials(List.of(TestCredentialHelper.createSharedSecretCredential("mock-key",
-                SYMMETRIC_KEY, JWEAlgorithm.A128KW)));
+        config.setKeyTransportEncryptionAlgorithms(
+                List.of(KeyManagementConstants.ALGO_ID_ALG_AES_256_KW));  
+        
+        final JWKCredential cred = TestCredentialHelper.createSharedSecretCredential("mock-key",
+                SYMMETRIC_KEY, JWEAlgorithm.A256KW, UsageType.SIGNING);
+        
+        criteria.add(new StaticCredentialCriterion(cred));
+        config.setKEKCredentialResolver(new MockStaticCriteriaFilteringCredentialResolver());
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
-        assertNull(param);       
+        assertNull(param);
+       
     }
     
     @Test
-    public void testBasicRSA_WrongAlgorithmInConfig() throws Exception {
+    public void testBasicAESKeyWrap_WrongCredentialAlgorithm() throws Exception {
         final CriteriaSet criteria =  buildBasicCriteriaSet();
-        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP));
+        config.setKeyTransportEncryptionAlgorithms(
+                List.of(KeyManagementConstants.ALGO_ID_ALG_AES_256_KW));  
+        
         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)));
+        
+        criteria.add(new StaticCredentialCriterion(TestCredentialHelper.createKeyEncryptionCredential(key)));
+        
+        config.setKEKCredentialResolver(new MockStaticCriteriaFilteringCredentialResolver());
         final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
         
-        assertNull(param);           
+        assertNull(param);
+
+       
     }
+    
+    /* Direct encryption algorithm not supported at runtime.*/
+    @Test(enabled=false)
+    public void testBasicDirectEncryption() throws Exception {
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(
+                List.of(KeyManagementConstants.ALGO_ID_ALG_DIR));        
         
+        final List<Credential> creds = new ArrayList<>();
+        creds.add(TestCredentialHelper.createSharedSecretCredential("mock-key-client-secret",
+                SYMMETRIC_KEY, JWEAlgorithm.DIR, UsageType.UNSPECIFIED));
+
+        
+        config.setKEKCredentialResolver(new MockStaticFilteringCredentialResolver(creds));
+        
+        final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
+        
+        assertNotNull(param);
+        assertNotNull(param.getDataEncryptionAlgorithm());
+        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_DIR);
+        assertNotNull(param.getDataEncryptionCredential());
+        assertNotNull(param.getDataEncryptionCredential().getSecretKey());
+        assertEquals(param.getDataEncryptionCredential().getSecretKey().getAlgorithm(),"AES");
+       
+    }
+ 
 }
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/DefaultKeyTransportEncryptionAlgorithmsLookupStrategyTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/DefaultKeyTransportEncryptionAlgorithmsLookupStrategyTest.java
new file mode 100644
index 0000000..25cd777
--- /dev/null
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/DefaultKeyTransportEncryptionAlgorithmsLookupStrategyTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.assertNotNull;
+
+import java.util.List;
+
+import org.opensaml.xmlsec.algorithm.AlgorithmRegistry;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+
+import net.shibboleth.oidc.jwa.algorithm.descriptors.KeyAgreementECDHES;
+import net.shibboleth.oidc.jwa.algorithm.descriptors.KeyTransportRSAOAEP;
+import net.shibboleth.oidc.jwa.algorithm.descriptors.KeyTransportRSAOAEP256;
+import net.shibboleth.oidc.jwa.algorithm.descriptors.SymmetricKeyWrapA128KW;
+import net.shibboleth.oidc.jwa.support.KeyManagementConstants;
+import net.shibboleth.oidc.security.criterion.JWTEncryptionConfigurationCriterion;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+public class DefaultKeyTransportEncryptionAlgorithmsLookupStrategyTest {
+    
+    private DefaultKeyTransportEncryptionAlgorithmsLookupStrategy strategy;
+    
+    private BasicJWTEncryptionConfiguration config;
+    
+    private CriteriaSet criteria;
+    
+    
+    @BeforeMethod
+    public void setup() {
+        
+        //FIXME should be mocked really. 
+        final AlgorithmRegistry algorithmRegistry = new AlgorithmRegistry();
+        algorithmRegistry.register(new SymmetricKeyWrapA128KW());
+        algorithmRegistry.register(new KeyTransportRSAOAEP256());
+        algorithmRegistry.register(new KeyTransportRSAOAEP());
+        algorithmRegistry.register(new KeyAgreementECDHES());
+        
+        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, KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP));
+
+        criteria = new CriteriaSet(new JWTEncryptionConfigurationCriterion(List.of(config)));   
+        
+        strategy = new DefaultKeyTransportEncryptionAlgorithmsLookupStrategy(algorithmRegistry);
+    }
+    
+    @Test
+    public void testSuccess_AllAlgs() {                
+        final List<String> keyAlgs = strategy.apply(criteria, Predicates.alwaysTrue());        
+        assertNotNull(keyAlgs);
+        assertEquals(keyAlgs.size(), 4);
+    }
+    
+    @Test
+    public void testSuccess() {                
+        final List<String> keyAlgs = strategy.apply(criteria, Predicates.alwaysTrue());        
+        assertNotNull(keyAlgs);
+        assertEquals(keyAlgs.size(), 4);
+    }
+    
+    @Test
+    public void testSuccess_SomeAlgsNotSupported() {      
+        final AlgorithmRegistry algorithmRegistry = new AlgorithmRegistry();
+        algorithmRegistry.register(new SymmetricKeyWrapA128KW());
+        algorithmRegistry.register(new KeyTransportRSAOAEP256());
+        
+        strategy = new DefaultKeyTransportEncryptionAlgorithmsLookupStrategy(algorithmRegistry);
+        
+        final List<String> keyAlgs = strategy.apply(criteria, Predicates.alwaysTrue());        
+        assertNotNull(keyAlgs);
+        assertEquals(keyAlgs.size(), 2);
+    }
+    
+    @Test
+    public void testSuccess_SomeAlgsEcluded() {                
+        final List<String> keyAlgs = strategy.apply(
+                criteria, alg -> !KeyManagementConstants.ALGO_ID_ALG_AES_128_KW.equals(alg));        
+        assertNotNull(keyAlgs);
+        assertEquals(keyAlgs.size(), 3);
+    }
+
+}
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ProviderMetadataDataEncryptionAlgorithmsLookupStrategyTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ProviderMetadataDataEncryptionAlgorithmsLookupStrategyTest.java
new file mode 100644
index 0000000..871a916
--- /dev/null
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ProviderMetadataDataEncryptionAlgorithmsLookupStrategyTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.assertNotNull;
+import static org.testng.Assert.fail;
+
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.xmlsec.algorithm.AlgorithmRegistry;
+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.google.common.base.Predicates;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.jwa.algorithm.descriptors.EncryptionA128CBCHS256;
+import net.shibboleth.oidc.jwa.algorithm.descriptors.EncryptionA128GCM;
+import net.shibboleth.oidc.jwa.algorithm.descriptors.EncryptionA192CBCHS384;
+import net.shibboleth.oidc.jwa.algorithm.descriptors.EncryptionA256CBCHS512;
+import net.shibboleth.oidc.jwa.support.EncryptionConstants;
+import net.shibboleth.oidc.security.criterion.JWTEncryptionConfigurationCriterion;
+import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+public class ProviderMetadataDataEncryptionAlgorithmsLookupStrategyTest {
+    
+    private ProviderMetadataDataEncryptionAlgorithmsLookupStrategy strategy;
+    
+    private BasicJWTEncryptionConfiguration config;
+    
+    private CriteriaSet criteria;
+    
+    private AlgorithmRegistry algorithmRegistry;
+    
+    /**
+     * 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");
+    
+    
+    /**
+     * 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 ParseException {
+        
+        //FIXME should be mocked really. 
+        algorithmRegistry = new AlgorithmRegistry();
+        algorithmRegistry.register(new EncryptionA128GCM());
+        algorithmRegistry.register(new EncryptionA128CBCHS256());
+        algorithmRegistry.register(new EncryptionA256CBCHS512());
+        algorithmRegistry.register(new EncryptionA192CBCHS384());
+        
+        config = new BasicJWTEncryptionConfiguration();
+        config.setDataEncryptionAlgorithms(
+                List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256,EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM,
+                        EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512, EncryptionConstants.ALGO_ID_ENC_ALG_A192CBC_HS384));
+
+        criteria = new CriteriaSet(new JWTEncryptionConfigurationCriterion(List.of(config)));   
+        criteria.add(
+                new ProviderMetadataCriterion(OIDCProviderMetadata.parse(readJsonFromFile(GOOD_PROVIDER_CONFIGURATION_INFO))));
+        
+        strategy = 
+                new ProviderMetadataDataEncryptionAlgorithmsLookupStrategy(
+                        OIDCProviderMetadata::getRequestObjectJWEEncs, algorithmRegistry);
+    }
+    
+    @Test
+    public void testSuccess_AllConfiguredAlgsAreSupported() {    
+        final List<String> encAlgs = strategy.apply(criteria, Predicates.alwaysTrue());        
+        assertNotNull(encAlgs);
+        assertEquals(encAlgs.size(), 4);
+    }    
+    
+    /* 5 algs supported by config, 4 supported by OP.*/
+    @Test
+    public void testSuccess_SomeAlgsNotSupportedByOP() {      
+        
+        config.setDataEncryptionAlgorithms(
+                List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256,EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM,
+                        EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512, EncryptionConstants.ALGO_ID_ENC_ALG_A192CBC_HS384,
+                        //This one is not supported by the OP.
+                        EncryptionConstants.ALGO_ID_ENC_ALG_A256GCM));
+        
+        final List<String> encAlgs = strategy.apply(criteria, Predicates.alwaysTrue());        
+        assertNotNull(encAlgs);
+        assertEquals(encAlgs.size(), 4);
+    }
+    
+    /* One of the configured algs is excluded.*/
+    @Test
+    public void testSuccess_SomeAlgsEcluded() {                
+        final List<String> encAlgs = strategy.apply(
+                criteria, alg -> !EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256.equals(alg));        
+        assertNotNull(encAlgs);
+        assertEquals(encAlgs.size(), 3);
+    }
+    
+    @Test
+    public void testSuccess_NoSupportedAlgs() {  
+        config.setDataEncryptionAlgorithms(Collections.emptyList());
+        final List<String> encAlgs = strategy.apply(criteria, Predicates.alwaysTrue());        
+        assertNotNull(encAlgs);
+        assertEquals(encAlgs.size(), 0);
+    }
+    
+    /* Null is returned from the provider lookup, so resort to local config only.*/
+    @Test
+    public void testSuccess_NullProviderAlgs() {  
+        
+        strategy = 
+                new ProviderMetadataDataEncryptionAlgorithmsLookupStrategy(
+                        meta -> null, algorithmRegistry);
+        
+        final List<String> encAlgs = strategy.apply(criteria, Predicates.alwaysTrue());        
+        assertNotNull(encAlgs);
+        assertEquals(encAlgs.size(), 4);
+    }
+
+}
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
deleted file mode 100644
index 7678d84..0000000
--- a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ProviderMetadataEncryptionParametersResolverTest.java
+++ /dev/null
@@ -1,496 +0,0 @@
-/*
- * 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("/credentials/test-provider-resolver-remote-jwkset-response.jwk"); 
-    
-    /** A remote JWKSet with no 'alg' parameters.*/
-    private static final ClassPathResource REMOTE_JWKSET_NO_ALG_PARAM = 
-            new ClassPathResource("/credentials/test-provider-resolver-remote-jwkset-response-no-alg.jwk"); 
-    
-    /** A remote JWKSet with no 'alg' parameters.*/
-    private static final ClassPathResource REMOTE_JWKSET_NO_KEY_USE = 
-            new ClassPathResource("/credentials/test-provider-resolver-remote-jwkset-response-no-keyuse.jwk"); 
-    
-    /** A remote JWKSet where key 87ff206d-15f9-4b8c-ba88-a8c17014da13 has the wrong key use.*/
-    private static final ClassPathResource REMOTE_JWKSET_WRONG_KEYUSE = 
-            new ClassPathResource("/credentials/test-provider-resolver-remote-jwkset-response-wrong-key-use.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;
-    
-    /** The cache being used by the resolver.*/
-    private RemoteJwkSetCache cache;
-    
-    /**
-     * 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);
-        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());
-    }
-    
-    /* 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());
-        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"RSA");
-    }
-    
-    /* Key 87ff206d-15f9-4b8c-ba88-a8c17014da13 has a key use of 'sig' rather than 'end'.*/
-    @Test
-    public void testUnSuccessfulResolution_ForKeyEncryption_WrongKeyUse() throws Exception {
-        cache.setHttpClient(createMockHttpClient(readJsonFromFile(REMOTE_JWKSET_WRONG_KEYUSE)));
-        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);
-        assertFalse(params.iterator().hasNext());        
-    }
-    
-    /* Key 87ff206d-15f9-4b8c-ba88-a8c17014da13 has no key use, so assume unspecified.*/
-    @Test
-    public void testSuccessfulResolution_ForKeyEncryption_NoKeyUse() throws Exception {
-        cache.setHttpClient(createMockHttpClient(readJsonFromFile(REMOTE_JWKSET_NO_KEY_USE)));
-        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());
-        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"RSA");
-    }
-    
-    /* Test credential choice if the keys do not specify their 'alg', only the mandatory 'kty'*/
-    @Test
-    public void testSuccessfulResolution_ForKeyEncryption_NoAlgParams() throws Exception {
-        cache.setHttpClient(createMockHttpClient(readJsonFromFile(REMOTE_JWKSET_NO_ALG_PARAM)));
-        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());
-        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"RSA");
-    }
-    
-    /* Test credential choice if the keys do not specify their 'alg', only the mandatory 'kty'*/
-    @Test
-    public void testSuccessfulResolution_ForKeyAgreement_NoAlgParams() throws Exception {
-        cache.setHttpClient(createMockHttpClient(readJsonFromFile(REMOTE_JWKSET_NO_ALG_PARAM)));
-        final CriteriaSet criteria =  buildBasicCriteriaSet();
-        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_ECDH_ES));
-        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_ECDH_ES);
-        assertNotNull(param.getKeyTransportEncryptionCredential());
-        assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
-        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"EC");
-    }
-    
-    /* 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());
-        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"RSA");
-    }
-    
-    /* 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());
-        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"RSA");
-    }
-    
-    /* 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());
-        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"EC");
-    }
-    
-    @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/ProviderMetadataKeyTransportEncryptionAlgorithmsLookupStrategyTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ProviderMetadataKeyTransportEncryptionAlgorithmsLookupStrategyTest.java
new file mode 100644
index 0000000..7d8ecdd
--- /dev/null
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ProviderMetadataKeyTransportEncryptionAlgorithmsLookupStrategyTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.assertNotNull;
+import static org.testng.Assert.fail;
+
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.xmlsec.algorithm.AlgorithmRegistry;
+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.google.common.base.Predicates;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.jwa.algorithm.descriptors.KeyAgreementECDHES;
+import net.shibboleth.oidc.jwa.algorithm.descriptors.KeyTransportRSAOAEP;
+import net.shibboleth.oidc.jwa.algorithm.descriptors.KeyTransportRSAOAEP256;
+import net.shibboleth.oidc.jwa.algorithm.descriptors.SymmetricKeyWrapA128KW;
+import net.shibboleth.oidc.jwa.support.KeyManagementConstants;
+import net.shibboleth.oidc.security.criterion.JWTEncryptionConfigurationCriterion;
+import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+public class ProviderMetadataKeyTransportEncryptionAlgorithmsLookupStrategyTest {
+    
+    private ProviderMetadataKeyTransportEncryptionAlgorithmsLookupStrategy strategy;
+    
+    private BasicJWTEncryptionConfiguration config;
+    
+    private CriteriaSet criteria;
+    
+    private AlgorithmRegistry algorithmRegistry;
+    
+    /**
+     * 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");
+    
+    
+    /**
+     * 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 ParseException {
+        
+        //FIXME should be mocked really. 
+        algorithmRegistry = new AlgorithmRegistry();
+        algorithmRegistry.register(new SymmetricKeyWrapA128KW());
+        algorithmRegistry.register(new KeyTransportRSAOAEP256());
+        algorithmRegistry.register(new KeyTransportRSAOAEP());
+        algorithmRegistry.register(new KeyAgreementECDHES());
+        
+        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, KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP));
+
+        criteria = new CriteriaSet(new JWTEncryptionConfigurationCriterion(List.of(config)));   
+        criteria.add(
+                new ProviderMetadataCriterion(OIDCProviderMetadata.parse(readJsonFromFile(GOOD_PROVIDER_CONFIGURATION_INFO))));
+        
+        strategy = 
+                new ProviderMetadataKeyTransportEncryptionAlgorithmsLookupStrategy(
+                        OIDCProviderMetadata::getRequestObjectJWEAlgs, algorithmRegistry);
+    }
+    
+    @Test
+    public void testSuccess_AllConfiguredAlgsAreSupported() {    
+        final List<String> algs = strategy.apply(criteria, Predicates.alwaysTrue());        
+        assertNotNull(algs);
+        assertEquals(algs.size(), 4);
+    }    
+    
+    /* 5 algs supported by config, 4 supported by OP.*/
+    @Test
+    public void testSuccess_SomeAlgsNotSupportedByOP() {      
+        
+        config.setKeyTransportEncryptionAlgorithms(
+                List.of(KeyManagementConstants.ALGO_ID_ALG_AES_128_KW, KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256,
+                KeyManagementConstants.ALGO_ID_ALG_ECDH_ES, KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP,
+                //This one is not supported by the OP
+                KeyManagementConstants.ALGO_ID_ALG_RSA_1_5));
+        
+        final List<String> keyAlgs = strategy.apply(criteria, Predicates.alwaysTrue());        
+        assertNotNull(keyAlgs);
+        assertEquals(keyAlgs.size(), 4);
+    }
+    
+    /* One of the configured algs is excluded.*/
+    @Test
+    public void testSuccess_SomeAlgsEcluded() {                
+        final List<String> keyAlgs = strategy.apply(
+                criteria, alg -> !KeyManagementConstants.ALGO_ID_ALG_AES_128_KW.equals(alg));        
+        assertNotNull(keyAlgs);
+        assertEquals(keyAlgs.size(), 3);
+    }
+    
+    @Test
+    public void testSuccess_NoSupportedAlgs() {  
+        config.setKeyTransportEncryptionAlgorithms(Collections.emptyList());
+        final List<String> keyAlgs = strategy.apply(criteria, Predicates.alwaysTrue());        
+        assertNotNull(keyAlgs);
+        assertEquals(keyAlgs.size(), 0);
+    }
+    
+    /* Null is returned from the provider lookup, so resort to local config only.*/
+    @Test
+    public void testSuccess_NullProviderAlgs() {  
+        
+        strategy = 
+                new ProviderMetadataKeyTransportEncryptionAlgorithmsLookupStrategy(
+                        meta -> null, algorithmRegistry);
+        
+        final List<String> keyAlgs = strategy.apply(criteria, Predicates.alwaysTrue());        
+        assertNotNull(keyAlgs);
+        assertEquals(keyAlgs.size(), 4);
+    }
+
+}
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
index 3a50207..54a68b3 100644
--- 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
@@ -67,7 +67,7 @@ public final class TestCredentialHelper {
     public static JWKCredential createClientSecretCredential(final String kid, final String secret, 
             final Algorithm algorithm) 
             throws KeyException {
-        return createSharedSecretCredential(kid, secret, algorithm);
+        return createSharedSecretCredential(kid, secret, algorithm, UsageType.UNSPECIFIED);
     }
     
     /**
@@ -76,17 +76,17 @@ public final class TestCredentialHelper {
      * @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.
-     * 
+     * @param usage TODO
      * @return the credential
      * @throws KeyException on error creating the key
      */
     public static JWKCredential createSharedSecretCredential(final String kid, final String secret, 
-            final Algorithm algorithm) 
+            final Algorithm algorithm, final UsageType usage) 
             throws KeyException {
         final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
         jwkCredential.setSecretKey(KeySupport.decodeSecretKey(JWSAssemblyUtils.getSecretBytes(secret), "AES"));
         jwkCredential.setCredentialExpiresAt(Duration.ZERO);
-        jwkCredential.setUsageType(UsageType.UNSPECIFIED);
+        jwkCredential.setUsageType(usage);
         jwkCredential.setKid(kid);
         jwkCredential.setAlgorithm(algorithm);
         jwkCredential.getKeyNames().add("mockKey");
diff --git a/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-key-array.jwk b/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-key-array.jwk
new file mode 100644
index 0000000..0e0dcd2
--- /dev/null
+++ b/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-key-array.jwk
@@ -0,0 +1,3 @@
+{
+
+}
\ No newline at end of file
diff --git a/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-keyuse.jwk b/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-keyuse.jwk
deleted file mode 100644
index 10e7a50..0000000
--- a/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-keyuse.jwk
+++ /dev/null
@@ -1,63 +0,0 @@
-{
-"keys": [
-{
-"kty": "RSA",
-"e": "AQAB",
-"use": "sig",
-"kid": "7da9fea4-7a38-4398-aaef-8226b26776a2",
-"n": "kk-3jeBmUPbpMk0fEdIn-APAdNOoOckA0e-SiALLxy5dWfG-GyF51g31zuM_iNiSiMSsmG2ZAVi48iItFpd-JW9IIT40TC147I6aKrel0Rf39Mwp-1tCzME6VYEgOmgI9qDg2e4edt1cvjQfiw3IZlXakwgYQn2BuoknoCBVjETVLHrnsvEqXhPffzML9O5Ze_nBOX6-pCAzVsimr-ljoln2GQz-ID5fGzlflXJV78v7QzlyyAAQovYQMxiEBgecHu44S0Iu_esLEOOobQkZyHc-OgcwEazfJUEUhKEnevVTJFlQF3Odxp1I6W9zd-zLUceqIMKF5Xs10AfmkPhboQ"
-},
-{
-"kty": "RSA",
-"e": "AQAB",
-"use": "sig",
-"kid": "9ae4e77c-a0c7-4c52-982f-b8e5e6b62ab8",
-"n": "lBh4Ujl1k_H9CAfJe-SD-ngZnllWh5lShhv2FF_OlSlDEwr5wbf4WimeQhqLtfeT-dJXALpLSncaG_5y8pHHh0Pflnx_pZfCoOOc4Fba7wZgpHzfSQePwIDH8ygmzMLNzLaECa5m1LxnDD0oVHsABOab-_6_Uvuvam5xo2pKfJHoxkVsEDxQ2R0T_GfqC2bmCNJCdadeqw43yF_ILBRX-9sosA_7GPwyBWKAyiHX-DTUKwWrpR2bwCGE2Bxfgj3cDa97prSX8Vwpj_DEPOH8hbMAjO-N4EBcvcJZ0O0CD3X3IrquC__wqc9aOMEh2xbRxnTHdrNNG1KqzS8-L2yS0w"
-},
-{
-"kty": "EC",
-"use": "sig",
-"crv": "P-256",
-"kid": "7b1a7c28-df25-4d54-b111-11903db56d52",
-"x": "XRlwH72XaSlYjybpA6q4DTHsOphTuSWNPULNKwQ38wo",
-"y": "xWIoYZAyQxZM7RJCL-k14PdIHkCPo4m2tKRiCWyXU9w"
-},
-{
-"kty": "EC",
-"use": "sig",
-"crv": "P-256",
-"kid": "00cfe876-ed35-4052-8045-be1088c3212f",
-"x": "SwSHs_Df-Qxl83Mibu_lWzxn0mBn9hGts9gougQlrqs",
-"y": "kKQfhy4jDV_cxpC3iptQTFODkgENp-HC4XK7NIDqt5s"
-},
-{
-"kty": "EC",
-"use": "sig",
-"crv": "secp256k1",
-"kid": "4d1d9b37-3acc-46b9-9426-ee209db8541f",
-"x": "cZb41D8qgFxbpxnqOcp-kc78M8EdtSYqotje0IhWk_o",
-"y": "viJ95PgOZdYFPHRqdO4NOhRQgkejVDv8RmDprbLE31g"
-},
-{
-"kty": "OKP",
-"use": "sig",
-"crv": "Ed25519",
-"kid": "0c54869d-7d20-4faf-b607-3b040d1e1f27",
-"x": "gi3CalT0xmz8V52rgfdvYyM-rUwKnf8gUUUqB87Gycw"
-},
-{
-"kty": "RSA",
-"e": "AQAB",
-"kid": "87ff206d-15f9-4b8c-ba88-a8c17014da13",
-"n": "uAVnVD3cMEbrAsDg1c3n6GfzR3sSg9C9pbjTw39_jgWk5YQCHPPOt4zYyZZL2JCnm9TFjnndCCW5ZPWHPJjumiNB2r-vC0CmI-T66JSRX3YYw0h2Odiusr_74FNe_mYyEuClFa4hwo-RMgrp8L1sbrAWcgGOc84rD6-fZXVrWFMkOb0jg6tqF1EwBSxZFG1cfvUmatNuBXs6njPHvvqhd7Bz6adK4YkpzCUbD-jSjpvAvU-Q4TZT_bXq4WRFOPqXv2NX4ch7ErjEm5tJEk7BIqOh7Byg0pWB4WAwsMcZKnHlp7JjtB2T1s_45_iqD2xipxpF-NxoHUlz67qHt7-W4Q"
-},
-{
-"kty": "EC",
-"use": "enc",
-"crv": "P-256",
-"kid": "c689ce91-8d82-45f2-b671-38ee38e7599f",
-"x": "redOUw802EuKJRoS8kQx6_RjuCypx0dcMBhv4IAALvQ",
-"y": "oaypduaS_wPLGCDQfJ0SKtQu0urJzhr4ZRn5wYMhiyk"
-}
-]
-}
\ No newline at end of file
diff --git a/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-nokeys.jwk b/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-nokeys.jwk
new file mode 100644
index 0000000..837a1ac
--- /dev/null
+++ b/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-nokeys.jwk
@@ -0,0 +1,5 @@
+{
+"keys": [
+
+]
+}
\ No newline at end of file
diff --git a/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-wrong-key-use.jwk b/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-wrong-key-use.jwk
deleted file mode 100644
index a388099..0000000
--- a/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-wrong-key-use.jwk
+++ /dev/null
@@ -1,64 +0,0 @@
-{
-"keys": [
-{
-"kty": "RSA",
-"e": "AQAB",
-"use": "sig",
-"kid": "7da9fea4-7a38-4398-aaef-8226b26776a2",
-"n": "kk-3jeBmUPbpMk0fEdIn-APAdNOoOckA0e-SiALLxy5dWfG-GyF51g31zuM_iNiSiMSsmG2ZAVi48iItFpd-JW9IIT40TC147I6aKrel0Rf39Mwp-1tCzME6VYEgOmgI9qDg2e4edt1cvjQfiw3IZlXakwgYQn2BuoknoCBVjETVLHrnsvEqXhPffzML9O5Ze_nBOX6-pCAzVsimr-ljoln2GQz-ID5fGzlflXJV78v7QzlyyAAQovYQMxiEBgecHu44S0Iu_esLEOOobQkZyHc-OgcwEazfJUEUhKEnevVTJFlQF3Odxp1I6W9zd-zLUceqIMKF5Xs10AfmkPhboQ"
-},
-{
-"kty": "RSA",
-"e": "AQAB",
-"use": "sig",
-"kid": "9ae4e77c-a0c7-4c52-982f-b8e5e6b62ab8",
-"n": "lBh4Ujl1k_H9CAfJe-SD-ngZnllWh5lShhv2FF_OlSlDEwr5wbf4WimeQhqLtfeT-dJXALpLSncaG_5y8pHHh0Pflnx_pZfCoOOc4Fba7wZgpHzfSQePwIDH8ygmzMLNzLaECa5m1LxnDD0oVHsABOab-_6_Uvuvam5xo2pKfJHoxkVsEDxQ2R0T_GfqC2bmCNJCdadeqw43yF_ILBRX-9sosA_7GPwyBWKAyiHX-DTUKwWrpR2bwCGE2Bxfgj3cDa97prSX8Vwpj_DEPOH8hbMAjO-N4EBcvcJZ0O0CD3X3IrquC__wqc9aOMEh2xbRxnTHdrNNG1KqzS8-L2yS0w"
-},
-{
-"kty": "EC",
-"use": "sig",
-"crv": "P-256",
-"kid": "7b1a7c28-df25-4d54-b111-11903db56d52",
-"x": "XRlwH72XaSlYjybpA6q4DTHsOphTuSWNPULNKwQ38wo",
-"y": "xWIoYZAyQxZM7RJCL-k14PdIHkCPo4m2tKRiCWyXU9w"
-},
-{
-"kty": "EC",
-"use": "sig",
-"crv": "P-256",
-"kid": "00cfe876-ed35-4052-8045-be1088c3212f",
-"x": "SwSHs_Df-Qxl83Mibu_lWzxn0mBn9hGts9gougQlrqs",
-"y": "kKQfhy4jDV_cxpC3iptQTFODkgENp-HC4XK7NIDqt5s"
-},
-{
-"kty": "EC",
-"use": "sig",
-"crv": "secp256k1",
-"kid": "4d1d9b37-3acc-46b9-9426-ee209db8541f",
-"x": "cZb41D8qgFxbpxnqOcp-kc78M8EdtSYqotje0IhWk_o",
-"y": "viJ95PgOZdYFPHRqdO4NOhRQgkejVDv8RmDprbLE31g"
-},
-{
-"kty": "OKP",
-"use": "sig",
-"crv": "Ed25519",
-"kid": "0c54869d-7d20-4faf-b607-3b040d1e1f27",
-"x": "gi3CalT0xmz8V52rgfdvYyM-rUwKnf8gUUUqB87Gycw"
-},
-{
-"kty": "RSA",
-"e": "AQAB",
-"use": "sig",
-"kid": "87ff206d-15f9-4b8c-ba88-a8c17014da13",
-"n": "uAVnVD3cMEbrAsDg1c3n6GfzR3sSg9C9pbjTw39_jgWk5YQCHPPOt4zYyZZL2JCnm9TFjnndCCW5ZPWHPJjumiNB2r-vC0CmI-T66JSRX3YYw0h2Odiusr_74FNe_mYyEuClFa4hwo-RMgrp8L1sbrAWcgGOc84rD6-fZXVrWFMkOb0jg6tqF1EwBSxZFG1cfvUmatNuBXs6njPHvvqhd7Bz6adK4YkpzCUbD-jSjpvAvU-Q4TZT_bXq4WRFOPqXv2NX4ch7ErjEm5tJEk7BIqOh7Byg0pWB4WAwsMcZKnHlp7JjtB2T1s_45_iqD2xipxpF-NxoHUlz67qHt7-W4Q"
-},
-{
-"kty": "EC",
-"use": "enc",
-"crv": "P-256",
-"kid": "c689ce91-8d82-45f2-b671-38ee38e7599f",
-"x": "redOUw802EuKJRoS8kQx6_RjuCypx0dcMBhv4IAALvQ",
-"y": "oaypduaS_wPLGCDQfJ0SKtQu0urJzhr4ZRn5wYMhiyk"
-}
-]
-}
\ No newline at end of file
diff --git a/oidc-common-crypto-impl/src/test/resources/metadata/test-resolver-provider-encryption.json b/oidc-common-crypto-impl/src/test/resources/metadata/test-resolver-provider-encryption.json
index 02633d9..635a211 100644
--- a/oidc-common-crypto-impl/src/test/resources/metadata/test-resolver-provider-encryption.json
+++ b/oidc-common-crypto-impl/src/test/resources/metadata/test-resolver-provider-encryption.json
@@ -28,7 +28,6 @@
       "HS256"
    ],
    "request_object_encryption_alg_values_supported":[
-      "RSA1_5",
       "RSA-OAEP",
       "RSA-OAEP-256",
       "RSA-OAEP-384",
@@ -50,8 +49,8 @@
       "A192CBC-HS384",
       "A256CBC-HS512",
       "A128GCM",
-      "A192GCM",
-      "A256GCM"
+      "A192GCM"
+
    ],
    "scopes_supported":[
       "openid",
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/audit/impl/DateBasedJWTClaimAuditExtractor.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/audit/impl/DateBasedJWTClaimAuditExtractor.java
index ab5f7f9..2b018e3 100644
--- a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/audit/impl/DateBasedJWTClaimAuditExtractor.java
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/audit/impl/DateBasedJWTClaimAuditExtractor.java
@@ -64,6 +64,10 @@ public class DateBasedJWTClaimAuditExtractor extends AbstractClaimsSetAuditExtra
     @Override
     protected String doLookup(final ClaimsSet claimsSet) throws ParseException {    
         final Date dateClaim = claimsSet.getDateClaim(getClaimName());
+        if (dateClaim == null) {
+            log.trace("Date claim '{}' was null", getClaimName());
+            return null;
+        }
         try {            
             return dateTimeFormatter.format(dateClaim.toInstant());
         } catch (final DateTimeException e) {

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


More information about the commits mailing list