[java-idp-plugin-oidc-rp] branch main updated: Improve provider metadata encryption params resolver

Phil Smart philip.smart at jisc.ac.uk
Wed Aug 3 13:30:06 UTC 2022


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

philsmart pushed a commit to branch main
in repository java-idp-plugin-oidc-rp.

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

The following commit(s) were added to refs/heads/main by this push:
     new 767dfdf  Improve provider metadata encryption params resolver
767dfdf is described below

commit 767dfdf35d01a944d6aa4659df550a61371005c1
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Wed Aug 3 14:30:00 2022 +0100

    Improve provider metadata encryption params resolver
    
    Loop over all algorithms and keys together. Choosing the first
    compatible
    Cleanup encrypt JWT action and add tests
---
 .../plugin/authn/oidc/rp/support/JWKSupport.java   |  49 ------
 .../idp/plugin/authn/oidc/rp/impl/DecryptJWT.java  |   4 -
 .../rp/impl/PopulateJWTEncryptionParameters.java   |   4 +-
 ...oviderMetadataEncryptionParametersResolver.java | 188 ++++++++++-----------
 ...RelyingPartyProxySigningParametersResolver.java |   4 +-
 .../authn/oidc/rp/messaging/impl/EncryptJWT.java   |  28 +--
 .../oidc-relying-party-authn-beans.xml             |  22 +--
 .../authn/oidc/rp/conf/authn/oidc-rp-config.xml    |  18 ++
 .../plugin/authn/oidc/rp/impl/EncryptJWTTest.java  | 182 ++++++++++++++++++++
 ...erMetadataEncryptionParametersResolverTest.java |  45 ++++-
 .../authn/oidc/rp/impl/TestCredentialHelper.java   |  30 +++-
 .../plugin/authn/oidc/rp/impl/TestTokenHelper.java |  22 +++
 12 files changed, 405 insertions(+), 191 deletions(-)

diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/support/JWKSupport.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/support/JWKSupport.java
deleted file mode 100644
index 259a464..0000000
--- a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/support/JWKSupport.java
+++ /dev/null
@@ -1,49 +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.idp.plugin.authn.oidc.rp.support;
-
-import java.util.stream.Collectors;
-
-import org.opensaml.security.credential.Credential;
-
-import net.shibboleth.oidc.security.credential.JWKCredential;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
-
-/** Support class for JSON Web Key credentials.*/
-public final class JWKSupport {
-    
-    /** Constructor. */
-    private JWKSupport() {
-    }
-    
-    /**
-     * Extract the credentials name for display. Favouring the keyId if there is one, over the keyNames.
-     * 
-     * @param credential the credential to extract the name from
-     * 
-     * @return the key name. 
-     */
-    public static String extractKeyName(final Credential credential) {
-        if (credential instanceof JWKCredential && 
-                StringSupport.trimOrNull(((JWKCredential)credential).getKid()) != null) {
-            return ((JWKCredential)credential).getKid();
-        } 
-        return credential.getKeyNames().stream().collect(Collectors.joining(","));     
-    }
-
-}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DecryptJWT.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DecryptJWT.java
index e4b1319..fc0aeff 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DecryptJWT.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DecryptJWT.java
@@ -33,8 +33,6 @@ import org.opensaml.xmlsec.encryption.support.DecryptionException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.nimbusds.jose.JWEObject;
-import com.nimbusds.jose.JWEObject.State;
 import com.nimbusds.jwt.EncryptedJWT;
 import com.nimbusds.jwt.JWT;
 
@@ -44,8 +42,6 @@ import net.shibboleth.oidc.security.JWTDecryptionParameters;
 import net.shibboleth.oidc.security.context.JWTSecurityParametersContext;
 import net.shibboleth.oidc.security.impl.JWTDecrypter;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.utilities.java.support.codec.Base64Support;
-import net.shibboleth.utilities.java.support.codec.DecodingException;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/PopulateJWTEncryptionParameters.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/PopulateJWTEncryptionParameters.java
index fc8bef0..93c3dd7 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/PopulateJWTEncryptionParameters.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/PopulateJWTEncryptionParameters.java
@@ -243,7 +243,7 @@ public class PopulateJWTEncryptionParameters extends AbstractProfileAction {
     @Override
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
 
-        log.debug("{} Resolving EncryptionParameters for '{}'", getLogPrefix(),forFriendlyName);
+        log.debug("{} Resolving EncryptionParameters for '{}' encryption", getLogPrefix(),forFriendlyName);
         
         try {
             encryptionConfigurations = configurationLookupStrategy.apply(profileRequestContext);
@@ -295,7 +295,7 @@ public class PopulateJWTEncryptionParameters extends AbstractProfileAction {
         final OIDCProviderMetadataContext oidcProviderMetadataCtx = 
                 oidcProviderMetadataContextLookupStrategy.apply(profileRequestContext);
         if (oidcProviderMetadataCtx != null && oidcProviderMetadataCtx.getProviderInformation() != null) {
-            log.debug("{} Adding OIDC provider information to resolution criteria for signing/digest algorithms",
+            log.debug("{} Adding OIDC provider information to resolution criteria",
                     getLogPrefix());
             criteria.add(new ProviderMetadataCriterion(oidcProviderMetadataCtx.getProviderInformation()));
         } else {
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ProviderMetadataEncryptionParametersResolver.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ProviderMetadataEncryptionParametersResolver.java
index d8a1df0..b4fc384 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ProviderMetadataEncryptionParametersResolver.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ProviderMetadataEncryptionParametersResolver.java
@@ -48,11 +48,11 @@ import com.nimbusds.jose.jwk.KeyUse;
 import com.nimbusds.jose.jwk.RSAKey;
 import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
 
-import net.shibboleth.idp.plugin.authn.oidc.rp.support.JWKSupport;
 import net.shibboleth.oidc.jwk.RemoteJwkSetCache;
 import net.shibboleth.oidc.security.credential.BasicJWKCredential;
 import net.shibboleth.oidc.security.credential.JWKCredential;
 import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
+import net.shibboleth.oidc.security.impl.CredentialConversionUtil;
 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;
@@ -61,10 +61,10 @@ import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
 /**
  * A specialization of {@link BasicEncryptionParametersResolver} which resolves encryption credentials from 
  * various sources based on what is supported by both the IdP and the downstream OpenID Provider. 
- * Broadly this supports:
+ * Broadly, this supports the following credentials:
  * <ul>
  * <li>Private KeyWrapping credentials found in the {@link EncryptionConfiguration}.</li>
- * <li>Public KeyEncryption credentials found in the Providers remote keyset. The Provider's metadata
+ * <li>Public KeyEncryption or KeyAgreement credentials found in the Providers remote keyset. The Provider's metadata
  * must be contained inside a ProviderMetadataCriterion.</li>
  * </ul>
  * Failure to resolve credentials from the above sources delegates resolution back to the 
@@ -153,6 +153,12 @@ public class ProviderMetadataEncryptionParametersResolver extends BasicEncryptio
             log.error("OIDC Provider metadata encryption parameters resolver does not have a remote JWKSet cache set");
         }
         
+        if (!criteria.contains(EncryptionConfigurationCriterion.class)) {
+            log.debug("No encryption configuration criterion, falling back to default configuration");
+            super.resolveAndPopulateCredentialsAndAlgorithms(params, criteria, includeExcludePredicate);
+            return;
+        }
+        
         if (!criteria.contains(ProviderMetadataCriterion.class)) {
             log.debug("No provider metadata criterion, falling back to local configuration");
             super.resolveAndPopulateCredentialsAndAlgorithms(params, criteria, includeExcludePredicate);
@@ -168,8 +174,7 @@ public class ProviderMetadataEncryptionParametersResolver extends BasicEncryptio
             return;
         }
         
-
-        // We populate the parameters for the algorithm the provider has registered
+        // 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);
@@ -195,8 +200,7 @@ public class ProviderMetadataEncryptionParametersResolver extends BasicEncryptio
         
         final List<String> supportedAndConfiguredKeyTransportAlgorithms = 
                 findAlgorithmIntersection(keyTransportAlgorithms.stream().map(JWEAlgorithm::getName)
-                        .collect(Collectors.toList()),keyTransportAlgorithms.stream().map(JWEAlgorithm::getName)
-                        .collect(Collectors.toList()));
+                        .collect(Collectors.toList()),keyTransportAlgorithmSupported);
         
         final List<String> supportedAndConfiguredDataEncryptionAlgorithms = 
                 findAlgorithmIntersection(dataEncryptionMethods.stream().map(EncryptionMethod::getName)
@@ -228,19 +232,11 @@ public class ProviderMetadataEncryptionParametersResolver extends BasicEncryptio
             super.resolveAndPopulateCredentialsAndAlgorithms(params, criteria, includeExcludePredicate);
             return;
         } 
-         
-        // Find key wrapping credentials. Find these first, as faster to resolve
-        resolveKeyWrappingCredential(supportedJWEKeyTransportAlgorithms, encryptionConfigurations, 
-                params, encryptionMethod);
-        
-        // Short circuit if found to avoid keyset retrieval
-        if (params.getKeyTransportEncryptionCredential() != null) {
-            return;
-        }
-        
-        // Find and process key encryption mode
-        resolveKeyEncryptionAndAgreementCredential(getProviderKeys(metadata), supportedJWEKeyTransportAlgorithms, 
-                params, encryptionMethod);
+
+        resolveCredentialForSupportedAlgorithm(metadata, supportedJWEKeyTransportAlgorithms,
+                encryptionConfigurations.stream()
+                .flatMap(config -> config.getKeyTransportEncryptionCredentials().stream())
+                .collect(Collectors.toList()), params, encryptionMethod);
 
         if (params.getKeyTransportEncryptionCredential() == null) {
             log.debug("Not able to locate encryption credentials based on provider metadata or encryption "
@@ -252,92 +248,81 @@ public class ProviderMetadataEncryptionParametersResolver extends BasicEncryptio
     }
     
     /**
-     * Resolve a suitable key encryption or key agreement credential from those fetched from the OP's remote key set.
-     * The first compatible key and algorithm are chosen. 
+     * Resolve a credential compatible with a supported algorithm. Algorithms are tried in the order
+     * they appear in the list of keyTransportAlgorithms.  
+     * 
+     * <p>Key wrapping credentials come from locally configured keys.</p>
+     * <p>
+     * Key encryption or key agreement credentials are fetched from the OP's remote key set.
+     * </p>
+     * <p>The first key that is compatible with the algorithm is returned. That is, if two or more keys
+     * support the same algorithm, the first key in the local credentials list or provider metadata keys set
+     * will be returned. There is no guarantee which key that is.</p>
      * 
-     * @param keySet the providers keyset
+     * @param metadata the OpenID Provider's metadata
      * @param keyTransportAlgorithms the set of supported key transport algorithms
+     * @param localCredentials the list of local credentials that might represent a private/secret key
      * @param params the encryption parameters to add the credential to
      * @param encryptionMethod the encryption method to use
      */
-    private void resolveKeyEncryptionAndAgreementCredential(final JWKSet keySet, 
-            final List<JWEAlgorithm> keyTransportAlgorithms,
-            final EncryptionParameters params, final EncryptionMethod encryptionMethod) {
-        
-        JWKSet providerKeySet = keySet;
-        if (providerKeySet == null) {
-            providerKeySet = new JWKSet();
-        }        
-        log.trace("Has '{}' keys from provider's JWKSet", providerKeySet.getKeys().size());
+    private void resolveCredentialForSupportedAlgorithm(@Nonnull final OIDCProviderMetadata metadata, 
+            @Nonnull final List<JWEAlgorithm> keyTransportAlgorithms,
+            @Nonnull final List<Credential> localCredentials,
+            @Nonnull final EncryptionParameters params, @Nonnull final EncryptionMethod encryptionMethod) {
         
-        for (final JWK key : providerKeySet.getKeys()) {
-            if (KeyUse.SIGNATURE.equals(key.getKeyUse())) {
-                continue;
+        for (final JWEAlgorithm algorithm : keyTransportAlgorithms) {
+            // First match against local secret keys
+            final JWKCredential localCred = localCredentials.stream()
+                .filter(JWKCredential.class::isInstance)
+                .map(JWKCredential.class::cast)
+                .filter(c -> algorithm.equals(c.getAlgorithm()))
+                .findFirst().orElse(null);
+            
+            if (localCred != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Selected key '{}' for alg {} and enc {}", 
+                            CredentialConversionUtil.resolveKid(localCred),
+                            algorithm.getName(), encryptionMethod.getName());
+                }
+                params.setKeyTransportEncryptionCredential(localCred);
+                params.setKeyTransportEncryptionAlgorithm(algorithm.getName());
+                params.setDataEncryptionAlgorithm(encryptionMethod.getName());
+                return;
             }            
-            for (final JWEAlgorithm algorithm : keyTransportAlgorithms) {
-                
-                if (key.getAlgorithm().equals(algorithm)) {
-                    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);
-                        continue;
+            
+            // Now match key encryption/agreement key from OP's keyset
+            final JWKSet providerKeySet = getProviderKeys(metadata);            
+            
+            final JWK key = 
+                    providerKeySet.getKeys().stream()
+                    .filter(k -> KeyUse.ENCRYPTION == k.getKeyUse())
+                    .filter(k -> k.getAlgorithm().equals(algorithm))
+                    .findFirst().orElse(null);
+            
+            if (key != null) {
+                final BasicJWKCredential jwkCredential = new BasicJWKCredential();
+                jwkCredential.setAlgorithm(algorithm);
+                jwkCredential.setKid(key.getKeyID());
+                try {
+                    if (key.getKeyType().equals(KeyType.RSA)) {
+                        jwkCredential.setPublicKey(((RSAKey) key).toPublicKey());
+                    } else if (key.getKeyType().equals(KeyType.EC)){
+                        jwkCredential.setPublicKey(((ECKey) key).toPublicKey());
                     }
-                    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());
-                    return;                
-                }            
-            }
-        }
-        
-    }
-    
-    /**
-     * Resolve a suitable key wrapping credential from those stored in the encryption configurations.   
-     * The first compatible key and algorithm are chosen.
-     * 
-     * @param keyTransportAlgorithms the set of supported key transport algorithms
-     * @param encryptionConfigurations the encryption configurations where the credentials are stored
-     * @param params the encryption parameters to add the credential to
-     * @param encryptionMethod the encryption method to use
-     */
-    private void resolveKeyWrappingCredential(final List<JWEAlgorithm> keyTransportAlgorithms,
-            final List<EncryptionConfiguration> encryptionConfigurations,
-            final EncryptionParameters params, final EncryptionMethod encryptionMethod) {
-        
-        for (final EncryptionConfiguration config : encryptionConfigurations) {
-            for (final Credential credential : config.getKeyTransportEncryptionCredentials()) {  
-                
-                for (final JWEAlgorithm algorithm : keyTransportAlgorithms) {                
-                    if (credential instanceof JWKCredential && credential.getSecretKey() != null && 
-                            algorithm.equals(((JWKCredential)credential).getAlgorithm())) {
-                        
-                            if (log.isDebugEnabled()) {
-                                log.debug("Selected key '{}' for alg {} and enc {}", 
-                                        JWKSupport.extractKeyName(credential),
-                                        algorithm.getName(), encryptionMethod.getName());
-                            }
-                            params.setKeyTransportEncryptionCredential(credential);
-                            params.setKeyTransportEncryptionAlgorithm(algorithm.getName());
-                            params.setDataEncryptionAlgorithm(encryptionMethod.getName());
-                            return;
-                        }                    
+                } catch (final JOSEException e) {
+                    log.warn("Unable to parse keyset", e);
+                    continue;
                 }
+                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());
+                return;  
             }
         }
     }
 
-
     /**
      * Return the first encryption method in the supported list, or null otherwise.
      * 
@@ -354,6 +339,7 @@ public class ProviderMetadataEncryptionParametersResolver extends BasicEncryptio
     
     /**
      * Convert the algorithms represented as strings, into Nimbus {@link Algorithm}s for later comparison.
+     * This will preserve the order of the original list ({@link List} is ordered). 
      * 
      * @param algos the algorithms to convert
      * 
@@ -368,15 +354,21 @@ public class ProviderMetadataEncryptionParametersResolver extends BasicEncryptio
      * 
      * @param metadata the OpenID Provider's metadata
      * 
-     * @return the JSON Web Keys set.
+     * @return the JSON Web Keys set. Or an empty key set if the fetch failed.
      */
-    @Nullable private JWKSet getProviderKeys(@Nonnull final OIDCProviderMetadata metadata) {
-            return remoteJwkSetCache.fetch(metadata.getJWKSetURI(),
-                    Instant.now().plus(keyFetchInterval));        
+    @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
@@ -385,7 +377,7 @@ public class ProviderMetadataEncryptionParametersResolver extends BasicEncryptio
      */
     @Nonnull private List<String> findAlgorithmIntersection(@Nonnull final List<String> providerAlgorithms,
             @Nonnull final List<String> configAlgorithms){        
-        return providerAlgorithms.stream().filter(configAlgorithms::contains).collect(Collectors.toList());
+        return configAlgorithms.stream().filter(providerAlgorithms::contains).collect(Collectors.toList());
         
     }
     
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/RelyingPartyProxySigningParametersResolver.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/RelyingPartyProxySigningParametersResolver.java
index 40c9081..5f32b18 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/RelyingPartyProxySigningParametersResolver.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/RelyingPartyProxySigningParametersResolver.java
@@ -39,9 +39,9 @@ import com.nimbusds.jose.JWSAlgorithm;
 import com.nimbusds.jose.jwk.Curve;
 import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
 
-import net.shibboleth.idp.plugin.authn.oidc.rp.support.JWKSupport;
 import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
 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.logic.FunctionSupport;
 import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
@@ -122,7 +122,7 @@ public class RelyingPartyProxySigningParametersResolver extends BasicSignatureSi
         // Pick the first credential that matches one of the supported algorithms
         for (final Credential credential : allCredentials) {   
             if (log.isTraceEnabled()) {
-                log.trace("Evaluating signing credential '{}'", JWKSupport.extractKeyName(credential));
+                log.trace("Evaluating signing credential '{}'", CredentialConversionUtil.resolveKid(credential));
             }
             final JWSAlgorithm foundSupportedAlgorithm = 
                     credentialSupportsSigningAlgorithm(credential, supportedAlgorithms);
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/EncryptJWT.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/EncryptJWT.java
index f5bb11c..c621158 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/EncryptJWT.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/EncryptJWT.java
@@ -20,7 +20,6 @@ package net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl;
 
 import java.security.interfaces.ECPublicKey;
 import java.security.interfaces.RSAPublicKey;
-import java.text.ParseException;
 import java.util.function.BiConsumer;
 import java.util.function.Function;
 
@@ -39,7 +38,6 @@ 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.JWEHeader;
 import com.nimbusds.jose.JWEObject;
@@ -56,12 +54,14 @@ import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterI
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
 
 /**
  * A {@link MessageHandler} that encrypts a JWT using the {@link EncryptionParameters} found in the 
  * {@link JWTSecurityParametersContext}. The {@link Payload} to encrypt is determined by lookup strategy. 
  * A consumer takes the {@link EncryptedJWT} and updates the correct object in the {@link MessageContext}.
  */
+//TODO does not support 'dir'?
 public class EncryptJWT extends AbstractMessageHandler {
     
     /** Class logger. */
@@ -157,6 +157,14 @@ public class EncryptJWT extends AbstractMessageHandler {
             return false;
         }
         
+        // If we have parameters (so encryption is enabled), but the parameters are not in the correct state, 
+        // throw an exception as opposed to skipping encryption
+        if (StringSupport.trimOrNull(encryptionParameters.getKeyTransportEncryptionAlgorithm()) == null ||
+                StringSupport.trimOrNull(encryptionParameters.getDataEncryptionAlgorithm()) == null ||
+                encryptionParameters.getKeyTransportEncryptionCredential() == null) {
+            throw new MessageHandlerException("Message context did not contain all required encryption parameters");
+        }
+        
         return true;
     }    
 
@@ -174,21 +182,21 @@ public class EncryptJWT extends AbstractMessageHandler {
         final EncryptionMethod encEnc = EncryptionMethod.parse(encryptionParameters.getDataEncryptionAlgorithm());
         final String kid = CredentialConversionUtil.resolveKid(credential);
 
-        log.debug("{} Encrypting with kid {} and params alg: {} enc: {}", getLogPrefix(), kid, encAlg.getName(),
+        log.debug("{} Encrypting with kid '{}' and params alg: {} enc: {}", getLogPrefix(), kid, encAlg.getName(),
                 encEnc.getName());
 
         final JWEObject jweObject =
                 new JWEObject(new JWEHeader.Builder(encAlg, encEnc).contentType("JWT").keyID(kid).build(), payload);
-        try {
-            //TODO does not support 'dir'?
-            if (JWEAlgorithm.Family.RSA.contains(encAlg)) {
+        try {            
+            if (JWEAlgorithm.Family.RSA.contains(encAlg) && credential.getPublicKey() != null) {
                 jweObject.encrypt(new RSAEncrypter((RSAPublicKey) credential.getPublicKey()));
-            } else if (JWEAlgorithm.Family.ECDH_ES.contains(encAlg)) {
+            } else if (JWEAlgorithm.Family.ECDH_ES.contains(encAlg) && credential.getPublicKey() != null) {
                 jweObject.encrypt(new ECDHEncrypter((ECPublicKey) credential.getPublicKey()));
-            } else if (JWEAlgorithm.Family.SYMMETRIC.contains(encAlg)) {
+            } else if (JWEAlgorithm.Family.SYMMETRIC.contains(encAlg) && credential.getSecretKey() != null) {
                 jweObject.encrypt(new AESEncrypter(credential.getSecretKey()));
             } else {
-                log.error("{} Unsupported algorithm {}", getLogPrefix(), encAlg.getName());
+                log.error("{} Unsupported algorithm {} or key '{}'", getLogPrefix(), encAlg.getName(),
+                        kid);
                 throw new MessageHandlerException("Unsupported algorithm "+encAlg.getName());
             }
 
@@ -201,7 +209,7 @@ public class EncryptJWT extends AbstractMessageHandler {
                 log.debug("{} Encrypted RequestObject: {}", getLogPrefix(), encryptedJWT.serialize());
             }
             
-        } catch (final JOSEException | ParseException e) {
+        } catch (final Exception e) {
             log.error("{} Encryption failed", getLogPrefix(), e);           
             throw new MessageHandlerException("Encryption failed", e);
         }
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
index 1a2a8a0..3b2f0a3 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
@@ -129,23 +129,6 @@
         p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" 
         p:requestedClaimsHook="#{getObject('shibboleth.oidc.rp.RequestedClaimsHook')}" />
    
-   <!-- TEST request claims hook -->
-   <bean id="shibboleth.oidc.rp.RequestedClaimsHook" parent="shibboleth.Functions.Scripted" 
-        factory-method="inlineScript"
-        p:inputType="org.opensaml.profile.context.ProfileRequestContext"
-        p:outputType="com.nimbusds.openid.connect.sdk.OIDCClaimsRequest">
-        <constructor-arg>
-        <value>
-        <![CDATA[
-            var requestedClaims =  new com.nimbusds.openid.connect.sdk.OIDCClaimsRequest()
-                .withIDTokenClaimsRequest(new com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest().add("given_name"))
-                .withUserInfoClaimsRequest(new com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest().add("family_name"))
-            requestedClaims;
-         ]]>
-        </value>
-    </constructor-arg>
-   
-   </bean>
                  
     <bean id="AddRedirectURI" scope="prototype"
         class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AddRedirectURI"
@@ -823,6 +806,11 @@
         p:requesterLookupStrategy-ref="shibboleth.ResponderIdLookup.Simple"
         p:attributeFilter-ref="shibboleth.AttributeFilterService"
         p:transcoderRegistry-ref="shibboleth.AttributeRegistryService" />
+        
+        
+     <!-- Can override one or more of the beans above. Note, the property override is mostly to allow tests
+    to change the location of the user config file. -->
+    <import resource="conditional:%{idp.home}/conf/authn/%{shibboleth.authn.oidc.rp.user.config:oidc-rp-config.xml}" />
 
 
 </beans>
diff --git a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/oidc-rp-config.xml b/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/oidc-rp-config.xml
new file mode 100644
index 0000000..382e122
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/oidc-rp-config.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:p="http://www.springframework.org/schema/p"
+       xmlns:c="http://www.springframework.org/schema/c"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+                           
+       default-init-method="initialize"
+       default-destroy-method="destroy"
+       default-lazy-init="true">
+
+
+   
+</beans>
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/EncryptJWTTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/EncryptJWTTest.java
new file mode 100644
index 0000000..6077b24
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/EncryptJWTTest.java
@@ -0,0 +1,182 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.util.Date;
+
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.opensaml.xmlsec.EncryptionParameters;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.JWEObject.State;
+import com.nimbusds.jose.crypto.AESDecrypter;
+import com.nimbusds.jose.crypto.RSADecrypter;
+import com.nimbusds.jose.jwk.Curve;
+import com.nimbusds.jose.jwk.ECKey;
+import com.nimbusds.jose.jwk.KeyUse;
+import com.nimbusds.jose.jwk.OctetKeyPair;
+import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.jose.jwk.gen.ECKeyGenerator;
+import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
+import com.nimbusds.jwt.EncryptedJWT;
+import com.nimbusds.jwt.JWTClaimsSet;
+
+import net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.EncryptJWT;
+import net.shibboleth.oidc.jwa.support.EncryptionConstants;
+import net.shibboleth.oidc.jwa.support.KeyManagementConstants;
+import net.shibboleth.oidc.security.context.JWTSecurityParametersContext;
+
+/** Tests for EncryptJWT.*/
+public class EncryptJWTTest extends AbstractOIDCTest {
+    
+    /** The client_secret.*/
+    private static final String CLIENT_SECRET = "Xp2s5v8y/B?E(H+MbQeThWmYq3t6w9z$";
+    
+    /** Decrypt action to test.*/
+    private EncryptJWT action;
+    
+    /** The Security Params to use.*/
+    private JWTSecurityParametersContext secContext;
+    
+    /** The encryption params to use.*/
+    private EncryptionParameters params;
+    
+    @Override
+    @BeforeMethod
+    public void setup() throws Exception {
+        super.setup();
+        action = new EncryptJWT();       
+        
+        action.setPayloadToEncryptLookupStrategy(prc -> {
+            
+            final JWTClaimsSet claims = new JWTClaimsSet.Builder()
+                    .audience("https://op.example.com")
+                    .issuer("https://rp.example.com")
+                    .issueTime(new Date())
+                    .build();
+            try {
+                return TestTokenHelper.createHMACSignedJWT(claims, CLIENT_SECRET).getPayload();
+            } catch (final JOSEException e) {
+                fail();
+            }
+            fail();
+            return null;
+
+        });
+        
+        action.setJwtUpdateConsumer((jwt, mc) -> {
+            // Add the EncryptedJWT to the message context directly, so we can test it later
+            mc.setMessage(jwt);
+        });
+        
+        secContext = 
+                prc.getOutboundMessageContext().getSubcontext(JWTSecurityParametersContext.class, true);
+        params = new EncryptionParameters();
+        secContext.setEncryptionParameters(params);
+
+    }
+    
+    @Test
+    public void testEncryptWithKeyEncryption() throws Exception {
+        
+        params.setKeyTransportEncryptionAlgorithm(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256);
+        params.setDataEncryptionAlgorithm(EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM);
+        final RSAKey key = new RSAKeyGenerator(2048)
+                .algorithm(JWEAlgorithm.RSA_OAEP_256)
+                .keyUse(KeyUse.ENCRYPTION)
+                .keyID("mock-key")
+                .generate();
+        params.setKeyTransportEncryptionCredential(TestCredentialHelper.createKeyEncryptionCredential(key));
+        
+        action.initialize();        
+        action.invoke(prc.getOutboundMessageContext());
+        
+        assertNotNull(prc.getOutboundMessageContext().getMessage());
+        assertTrue(prc.getOutboundMessageContext().getMessage() instanceof EncryptedJWT);
+        
+        final EncryptedJWT encryptedJWT = (EncryptedJWT) prc.getOutboundMessageContext().getMessage();
+        final RSADecrypter decrypter = new RSADecrypter(key);
+        encryptedJWT.decrypt(decrypter);
+        assertTrue(encryptedJWT.getState() == State.DECRYPTED);
+        final JWTClaimsSet claims = encryptedJWT.getJWTClaimsSet();
+        assertEquals(claims.getIssuer(), "https://rp.example.com"); 
+    }
+    
+    @Test(expectedExceptions = MessageHandlerException.class)
+    public void testEncryptWithKeyEncryption_WrongAlgorithmForKey() throws Exception {
+        
+        params.setKeyTransportEncryptionAlgorithm(KeyManagementConstants.ALGO_ID_ALG_AES_256_KW);
+        params.setDataEncryptionAlgorithm(EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM);
+        final RSAKey key = new RSAKeyGenerator(2048)
+                .algorithm(JWEAlgorithm.RSA_OAEP_256)
+                .keyUse(KeyUse.ENCRYPTION)
+                .keyID("mock-key")
+                .generate();
+        params.setKeyTransportEncryptionCredential(TestCredentialHelper.createKeyEncryptionCredential(key));
+        
+        action.initialize();        
+        action.invoke(prc.getOutboundMessageContext());
+    }
+    
+    @Test(expectedExceptions = MessageHandlerException.class)
+    public void testEncryptWithKeyEncryption_WrongKeyType() throws Exception {
+        
+        params.setKeyTransportEncryptionAlgorithm(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256);
+        params.setDataEncryptionAlgorithm(EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM);
+        final ECKey key = new ECKeyGenerator(new Curve("P-256"))
+                .algorithm(JWEAlgorithm.RSA_OAEP_256)
+                .keyUse(KeyUse.ENCRYPTION)
+                .keyID("mock-key")
+                .generate();
+        params.setKeyTransportEncryptionCredential(TestCredentialHelper.createKeyAgreementCredential(key));
+        
+        action.initialize();        
+        action.invoke(prc.getOutboundMessageContext());
+    }
+    
+    @Test(expectedExceptions = MessageHandlerException.class)
+    public void testEncrypWithUnsupportedAlgorithm() throws Exception {
+        
+        params.setKeyTransportEncryptionAlgorithm("NotSupported");
+        params.setDataEncryptionAlgorithm(EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM);
+        final RSAKey key = new RSAKeyGenerator(2048)
+                .algorithm(JWEAlgorithm.RSA_OAEP_256)
+                .keyUse(KeyUse.ENCRYPTION)
+                .keyID("mock-key")
+                .generate();
+        params.setKeyTransportEncryptionCredential(TestCredentialHelper.createKeyEncryptionCredential(key));
+        
+        action.initialize();        
+        action.invoke(prc.getOutboundMessageContext());
+    }
+    
+    @Test
+    public void testEncryptWithKeyWrap() throws Exception {
+        
+        params.setKeyTransportEncryptionAlgorithm(KeyManagementConstants.ALGO_ID_ALG_AES_256_KW);
+        params.setDataEncryptionAlgorithm(EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM);
+        final var sharedKey = TestCredentialHelper.createClientSecretCredential(CLIENT_SECRET);
+        params.setKeyTransportEncryptionCredential(sharedKey);
+        
+        action.initialize();        
+        action.invoke(prc.getOutboundMessageContext());
+        
+        assertNotNull(prc.getOutboundMessageContext().getMessage());
+        assertTrue(prc.getOutboundMessageContext().getMessage() instanceof EncryptedJWT);
+        
+        final EncryptedJWT encryptedJWT = (EncryptedJWT) prc.getOutboundMessageContext().getMessage();
+        final AESDecrypter decrypter = new AESDecrypter(sharedKey.getSecretKey());
+        encryptedJWT.decrypt(decrypter);
+        assertTrue(encryptedJWT.getState() == State.DECRYPTED);
+        final JWTClaimsSet claims = encryptedJWT.getJWTClaimsSet();
+        assertEquals(claims.getIssuer(), "https://rp.example.com"); 
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ProviderMetadataEncryptionParametersResolverTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ProviderMetadataEncryptionParametersResolverTest.java
index 44e431d..b205ae1 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ProviderMetadataEncryptionParametersResolverTest.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ProviderMetadataEncryptionParametersResolverTest.java
@@ -50,6 +50,7 @@ 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.credential.JWKCredential;
 import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
 import net.shibboleth.oidc.security.criterion.StaticCredentialCriterion;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
@@ -74,6 +75,9 @@ public class ProviderMetadataEncryptionParametersResolverTest {
     /** 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;
     
@@ -164,7 +168,8 @@ public class ProviderMetadataEncryptionParametersResolverTest {
         assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
     }
     
-    /* Should chose key encryption creds as they are the only ones configured.*/
+    /* 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();
@@ -184,15 +189,16 @@ public class ProviderMetadataEncryptionParametersResolverTest {
         assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
     }
     
-    /* Should chose key wrap creds first.*/
+    /* 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_RSA_OAEP, 
-                KeyManagementConstants.ALGO_ID_ALG_AES_256_KW));
+        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(SYMMETRIC_KEY, JWEAlgorithm.A256KW)));
+                List.of(TestCredentialHelper.createClientSecretCredential("mockKey", 
+                        SYMMETRIC_KEY, JWEAlgorithm.A256KW)));
         
         final Iterable<EncryptionParameters> params = resolver.resolve(criteria);
         assertNotNull(params);
@@ -212,7 +218,32 @@ public class ProviderMetadataEncryptionParametersResolverTest {
         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(SYMMETRIC_KEY, JWEAlgorithm.A256KW)));
+                List.of(TestCredentialHelper.createClientSecretCredential("mockKey", SYMMETRIC_KEY, 
+                        JWEAlgorithm.A256KW)));
+        
+        final Iterable<EncryptionParameters> params = resolver.resolve(criteria);
+        assertNotNull(params);
+        assertTrue(params.iterator().hasNext());
+        final EncryptionParameters 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());
+    }
+    
+    /* 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<EncryptionParameters> params = resolver.resolve(criteria);
         assertNotNull(params);
@@ -222,6 +253,8 @@ public class ProviderMetadataEncryptionParametersResolverTest {
         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());
     }
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestCredentialHelper.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestCredentialHelper.java
index 62f4821..96bcaa9 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestCredentialHelper.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestCredentialHelper.java
@@ -27,6 +27,7 @@ import org.opensaml.security.crypto.KeySupport;
 import com.nimbusds.jose.JOSEException;
 import com.nimbusds.jose.JWEAlgorithm;
 import com.nimbusds.jose.jwk.AsymmetricJWK;
+import com.nimbusds.jose.jwk.ECKey;
 import com.nimbusds.jose.jwk.JWK;
 import com.nimbusds.jose.jwk.RSAKey;
 import com.nimbusds.jose.Algorithm;
@@ -51,25 +52,27 @@ public final class TestCredentialHelper {
      * @throws KeyException on error creating the key
      */
     public static JWKCredential createClientSecretCredential(final String secret) throws KeyException {        
-        return createClientSecretCredential(secret, null);
+        return createClientSecretCredential("mockKey", secret, null);
     }
     
     /**
      * Create a simple symmetric key client credential from from the given shared secret.
      * 
+     * @param kid the key ID
      * @param secret the secret to convert to a {@link JWKCredential}.
      * @param algorithm the JWA algorithm to set on the credential.
      * 
      * @return the credential
      * @throws KeyException on error creating the key
      */
-    public static JWKCredential createClientSecretCredential(final String secret, final Algorithm algorithm) 
+    public static JWKCredential createClientSecretCredential(final String kid, final String secret, 
+            final Algorithm algorithm) 
             throws KeyException {
         final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
         jwkCredential.setSecretKey(KeySupport.decodeSecretKey(JWSAssemblyUtils.getSecretBytes(secret), "AES"));
         jwkCredential.setCredentialExpiresAt(Duration.ZERO);
         jwkCredential.setUsageType(UsageType.UNSPECIFIED);
-        jwkCredential.setKid("mockKey");
+        jwkCredential.setKid(kid);
         jwkCredential.setAlgorithm(algorithm);
         jwkCredential.getKeyNames().add("mockKey");
         return jwkCredential;
@@ -139,5 +142,26 @@ public final class TestCredentialHelper {
         jwkCredential.setAlgorithm(secret.getAlgorithm());
         return jwkCredential;
     }
+    
+    /**
+     * Create a direct encryption {@link JWKCredential} from the given shared secret.
+     * 
+     * @param secret the secret to convert to a {@link JWKCredential}.
+     * 
+     * @return the credential
+     * @throws JOSEException  on error
+     */
+    public static JWKCredential createKeyAgreementCredential(final ECKey secret) throws JOSEException {
+        final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+        jwkCredential.setPrivateKey(secret.toPrivateKey());
+        jwkCredential.setPublicKey(secret.toPublicKey());
+        jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+        jwkCredential.setUsageType(UsageType.ENCRYPTION);
+        
+        jwkCredential.setKid(secret.getKeyID());
+        jwkCredential.getKeyNames().add("mockKey");
+        jwkCredential.setAlgorithm(secret.getAlgorithm());
+        return jwkCredential;
+    }
 
 }
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestTokenHelper.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestTokenHelper.java
index 774ceed..5be9ef2 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestTokenHelper.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestTokenHelper.java
@@ -31,6 +31,7 @@ import com.nimbusds.jose.JWEHeader;
 import com.nimbusds.jose.JWEObject;
 import com.nimbusds.jose.JWSAlgorithm;
 import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.KeyLengthException;
 import com.nimbusds.jose.Payload;
 import com.nimbusds.jose.crypto.DirectEncrypter;
 import com.nimbusds.jose.crypto.ECDSASigner;
@@ -286,6 +287,27 @@ public final class TestTokenHelper {
         return signedJWT;
     }
     
+    /**
+     * Create a MAC signed JWT from the given JWT claims set, using the supplied secret.
+     * 
+     * @param payload the payload to sign
+     * @param secret the secret used to create the MAC
+     * 
+     * @return the signed JWT
+     * 
+     * @throws KeyLengthException on error
+     * @throws JOSEException on error
+     */
+    public static SignedJWT createHMACSignedJWT(final JWTClaimsSet payload, final String secret) 
+            throws KeyLengthException, JOSEException {
+        final var header = new JWSHeader.Builder(JWSAlgorithm.HS256)
+                .type(JOSEObjectType.JWT)
+                .build();
+        final var signedJWT = new SignedJWT(header, payload);
+        signedJWT.sign(new MACSigner(secret));
+        return signedJWT;
+    }
+    
     /**
      * Create a Plain UserInfo response JWT.
      * 

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


More information about the commits mailing list