[java-idp-plugin-oidc-rp] branch main updated: Add support for JWT decryption and wire up for id_token

Phil Smart philip.smart at jisc.ac.uk
Wed Jun 1 16:42:47 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=20879d578c7bf4399916b5684de7ad8ae6d0a406

The following commit(s) were added to refs/heads/main by this push:
     new 20879d5  Add support for JWT decryption and wire up for id_token
20879d5 is described below

commit 20879d578c7bf4399916b5684de7ad8ae6d0a406
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Wed Jun 1 17:42:41 2022 +0100

    Add support for JWT decryption and wire up for id_token
---
 .../JWTDecryptionConfigurationLookupFunction.java  |  92 ++++++++
 .../idp/plugin/authn/oidc/rp/impl/DecryptJWT.java  | 176 +++++++++++++++
 .../impl/IDTokenInAccessTokenUpdateStrategy.java   |  80 +++++++
 .../rp/impl/PopulateJWTDecryptionParameters.java   | 212 ++++++++++++++++++
 .../META-INF/net.shibboleth.idp/postconfig.xml     |  10 +-
 .../oidc-relying-party-authn-beans.xml             |  44 ++--
 .../oidc-relying-party-authn-flow.xml              |   1 +
 .../idp/service/relying-party/postconfig.xml       | 120 ++++++-----
 .../authn/oidc/rp/conf/authn/rp-credentials.xml    |  18 ++
 .../plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java  | 239 +++++++++++++++++++--
 .../test/resources/conf/authn/rp-credentials.xml   |  18 ++
 11 files changed, 915 insertions(+), 95 deletions(-)

diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/JWTDecryptionConfigurationLookupFunction.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/JWTDecryptionConfigurationLookupFunction.java
new file mode 100644
index 0000000..daf4c06
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/JWTDecryptionConfigurationLookupFunction.java
@@ -0,0 +1,92 @@
+/*
+ * 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.config.navigate;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.profile.config.ProfileConfiguration;
+import net.shibboleth.idp.profile.config.SecurityConfiguration;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.profile.context.navigate.AbstractRelyingPartyLookupFunction;
+import net.shibboleth.idp.relyingparty.RelyingPartyConfigurationResolver;
+import net.shibboleth.oidc.profile.config.OIDCSecurityConfiguration;
+import net.shibboleth.oidc.security.DecryptionConfiguration;
+
+/**
+ * A function that returns a {@link DecryptionConfiguration} list for JWE decryption by way
+ * of various lookup strategies. 
+ * 
+ * <p>
+ * If a specific setting is unavailable, a null value is returned.
+ * </p>
+ */
+public class JWTDecryptionConfigurationLookupFunction 
+            extends AbstractRelyingPartyLookupFunction<List<DecryptionConfiguration>> {
+
+    /** A resolver for default security configurations. */
+    @Nullable
+    private RelyingPartyConfigurationResolver rpResolver;
+
+    /**
+     * Set the resolver for default security configurations.
+     * 
+     * @param resolver the resolver to use
+     */
+    public void setRelyingPartyConfigurationResolver(@Nullable final RelyingPartyConfigurationResolver resolver) {
+        rpResolver = resolver;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public List<DecryptionConfiguration> apply(@Nullable final ProfileRequestContext input) {
+
+        final List<DecryptionConfiguration> configs = new ArrayList<>();
+
+        final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
+        if (rpc != null) {
+            final ProfileConfiguration pc = rpc.getProfileConfig();
+            if (pc != null && pc.getSecurityConfiguration(input) instanceof OIDCSecurityConfiguration
+                    && ((OIDCSecurityConfiguration) pc.getSecurityConfiguration(input))
+                            .getIdtokenJwtDecryptionConfig() != null) {
+                configs.add(((OIDCSecurityConfiguration) pc.getSecurityConfiguration(input))
+                        .getIdtokenJwtDecryptionConfig());
+            }
+        }
+
+        // Check for a per-profile default (relying party independent) config.
+        if (input != null && rpResolver != null) {
+            final SecurityConfiguration defaultConfig =
+                    rpResolver.getDefaultSecurityConfiguration(input.getProfileId());
+            if (defaultConfig instanceof OIDCSecurityConfiguration
+                    && ((OIDCSecurityConfiguration) defaultConfig)
+                    .getIdtokenJwtDecryptionConfig() != null) {
+                configs.add(
+                        ((OIDCSecurityConfiguration) defaultConfig).getIdtokenJwtDecryptionConfig());
+            }
+        }
+        // TODO: Support for Global Default configuration?
+        return configs;
+    }
+}
+
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
new file mode 100644
index 0000000..aae31b5
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DecryptJWT.java
@@ -0,0 +1,176 @@
+/*
+ * 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.impl;
+
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
+import org.opensaml.xmlsec.encryption.support.DecryptionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jwt.EncryptedJWT;
+import com.nimbusds.jwt.JWT;
+
+import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.oidc.profile.core.OidcEventIds;
+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.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/** 
+ * Decrypt the located JWE using the decryption parameters stored in the security context.
+ * 
+ */
+public class DecryptJWT extends AbstractProfileAction {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(DecryptJWT.class);
+    
+    /** Function that looks up a signed JWT token from the given message context to validate.*/
+    @NonnullAfterInit private Function<ProfileRequestContext, EncryptedJWT> jwtTokenLookupStrategy;
+    
+    /** 
+     * A consumer that sets the JWT produced from this decryption operation back into the profile request context.
+     */
+    @NonnullAfterInit private BiConsumer<ProfileRequestContext, JWT> jwtUpdateStrategy;
+    
+    /** The extracted encrypted JWT that is to be validated.*/
+    @Nullable private EncryptedJWT encryptedJwt;
+    
+    /** The decryption object. */
+    @Nullable private JWTDecrypter decrypter;
+    
+    /** Strategy used to locate the {@link JWTSecurityParametersContext}. */
+    @Nonnull private Function<ProfileRequestContext, JWTSecurityParametersContext> securityParamsLookupStrategy;
+    
+    /** Constructor.*/
+    public DecryptJWT() {
+        securityParamsLookupStrategy =
+                new ChildContextLookup<>(JWTSecurityParametersContext.class).compose(
+                        new InboundMessageContextLookup());
+    }
+    
+    /**
+     * Set the strategy used to locate the {@link JWTSecurityParametersContext} associated with a given
+     * {@link ProfileRequestContext}.
+     * 
+     * @param strategy strategy used to locate the {@link JWTSecurityParametersContext} associated with a given
+     *            {@link ProfileRequestContext}
+     */
+    public void setSecurityParametersContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, JWTSecurityParametersContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        securityParamsLookupStrategy =
+                Constraint.isNotNull(strategy, "SecurityParametersContext lookup strategy cannot be null");
+    }
+    
+    /**
+     * Set the strategy used to update the profile request context with the JWT produced as a result
+     * of this decryption operation.
+     * 
+     * @param strategy the strategy
+     */
+    public void setJwtUpdateStrategy(@Nonnull final BiConsumer<ProfileRequestContext, JWT> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        jwtUpdateStrategy = Constraint.isNotNull(strategy, "JWT update stategy can not be null");
+    }
+    
+    /**
+     * Set the strategy used to look up a {@link EncryptedJWT encrypted JWT token}.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setJwtTokenLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, EncryptedJWT> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        jwtTokenLookupStrategy = Constraint.isNotNull(strategy,
+                "JwtToken lookup strategy cannot be null");
+    }
+    
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (jwtTokenLookupStrategy == null) {
+            throw new ComponentInitializationException("JWTTokenLookupStrategy cannot be null");
+        }
+        if (jwtUpdateStrategy == null) {
+            throw new ComponentInitializationException("JWTUpdateStrategy cannot be null");
+        }
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+        if (!super.doPreExecute(profileRequestContext)) {
+            return false;
+        }
+        
+        encryptedJwt = jwtTokenLookupStrategy.apply(profileRequestContext);
+        if (encryptedJwt == null) {
+            log.debug("{} Extracted JWT was not an EncryptedJwt, nothing to decrypt",
+                    getLogPrefix());
+            return false;
+        }
+        
+        final JWTSecurityParametersContext paramsCtx = securityParamsLookupStrategy.apply(profileRequestContext);
+        if (paramsCtx == null || paramsCtx.getDecryptionParameters() == null) {
+            log.debug("{} No security parameter context or decryption parameters", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_SEC_CFG);
+            return false;
+        } else {
+            final JWTDecryptionParameters params = paramsCtx.getDecryptionParameters();
+            decrypter = new JWTDecrypter(params);
+        }
+        
+        
+        return true;
+    }
+    
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+        
+        log.debug("{} Decrypting encrypted JWT", getLogPrefix());
+        try {
+            final JWT decryptedJWT = decrypter.decrypt(encryptedJwt);
+            log.debug("{} JWT decrypted successfully", getLogPrefix());
+            jwtUpdateStrategy.accept(profileRequestContext, decryptedJWT);
+        } catch (final DecryptionException e) {
+            log.error("{} Unable to decrypt JWT", getLogPrefix(), e);
+            ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_ID_TOKEN);
+            return;
+        }     
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/IDTokenInAccessTokenUpdateStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/IDTokenInAccessTokenUpdateStrategy.java
new file mode 100644
index 0000000..8db68b7
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/IDTokenInAccessTokenUpdateStrategy.java
@@ -0,0 +1,80 @@
+/*
+ * 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.impl;
+
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jwt.JWT;
+
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.AccessTokenResponseContext;
+import net.shibboleth.utilities.java.support.annotation.ParameterName;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/** Strategy to update the id_token in the {@link AccessTokenResponseContext}.*/
+public class IDTokenInAccessTokenUpdateStrategy implements  BiConsumer<ProfileRequestContext, JWT> {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(IDTokenInAccessTokenUpdateStrategy.class);
+    
+    /** Strategy used to look up the {@link AccessTokenResponseContext} to set id_token on. */
+    @Nonnull private final Function<ProfileRequestContext, AccessTokenResponseContext> 
+            tokenResponseContextLookupStrategy;
+    
+    /**
+     * 
+     * Constructor.
+     *
+     * @param strategy the strategy used look up the {@link AccessTokenResponseContext}.
+     */
+    public IDTokenInAccessTokenUpdateStrategy(@ParameterName(name="accessTokenContextLookupStrategy") final
+        Function<ProfileRequestContext, AccessTokenResponseContext> strategy) {
+        
+        tokenResponseContextLookupStrategy = 
+                Constraint.isNotNull(strategy, "accessTokenContextLookupStrategy can not be null");
+    }
+    
+    /** Constructor.*/
+    public IDTokenInAccessTokenUpdateStrategy() {
+        tokenResponseContextLookupStrategy =
+                new ChildContextLookup<>(AccessTokenResponseContext.class, true).compose(
+                        new InboundMessageContextLookup()); 
+    }
+
+    @Override
+    public void accept(final ProfileRequestContext profileRequestContext, final JWT idToken) {
+        
+        final AccessTokenResponseContext context = 
+                tokenResponseContextLookupStrategy.apply(profileRequestContext);  
+        if (context != null) {
+            context.setIdToken(idToken);
+        } else {
+            log.warn("Unable to set id_token back onto access token response context");
+        }
+        
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/PopulateJWTDecryptionParameters.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/PopulateJWTDecryptionParameters.java
new file mode 100644
index 0000000..e9bf192
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/PopulateJWTDecryptionParameters.java
@@ -0,0 +1,212 @@
+/*
+ * 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.impl;
+
+import java.util.List;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
+import org.opensaml.xmlsec.EncryptionParametersResolver;
+import org.opensaml.xmlsec.context.SecurityParametersContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+import net.shibboleth.oidc.security.DecryptionConfiguration;
+import net.shibboleth.oidc.security.JWTDecryptionParameters;
+import net.shibboleth.oidc.security.JWTDecryptionParametersResolver;
+import net.shibboleth.oidc.security.context.JWTSecurityParametersContext;
+import net.shibboleth.oidc.security.criterion.DecryptionConfigurationCriterion;
+import net.shibboleth.oidc.security.criterion.StaticCredentialCriterion;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+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.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+public class PopulateJWTDecryptionParameters extends AbstractProfileAction {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(PopulateJWTDecryptionParameters.class);
+    
+    /** Strategy used to look up the {@link SecurityParametersContext} to set the parameters for. */
+    @Nonnull 
+    private Function<ProfileRequestContext,JWTSecurityParametersContext> securityParametersContextLookupStrategy;
+    
+    /** Strategy used to lookup a per-request {@link DecryptionConfiguration} list. */
+    @NonnullAfterInit private Function<ProfileRequestContext,List<DecryptionConfiguration>> configurationLookupStrategy;
+    
+    /** Lookup function for relying party context. */
+    @Nonnull private Function<ProfileRequestContext,RelyingPartyContext> relyingPartyContextLookupStrategy;
+    
+    /** Resolver for parameters to store into context. */
+    @NonnullAfterInit private JWTDecryptionParametersResolver resolver;
+    
+    /**
+     * Constructor.
+     */
+    public PopulateJWTDecryptionParameters() {
+        // Create context by default.
+        securityParametersContextLookupStrategy =
+                new ChildContextLookup<>(JWTSecurityParametersContext.class, true).compose(
+                        new InboundMessageContextLookup());       
+        relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
+    }
+    
+    /**
+     * Set lookup strategy for relying party context.
+     * 
+     * @param strategy  lookup strategy
+     */
+    public void setRelyingPartyContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,RelyingPartyContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        relyingPartyContextLookupStrategy =
+                Constraint.isNotNull(strategy, "RelyingPartyContext lookup strategy cannot be null");
+    }
+    
+    /**
+     * Set the resolver to use for the parameters to store into the context.
+     * 
+     * @param newResolver   resolver to use
+     */
+    public void setDecryptionParametersResolver(@Nonnull final JWTDecryptionParametersResolver newResolver) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        resolver = Constraint.isNotNull(newResolver, "DecryptionParametersResolver cannot be null");
+    }
+    
+    /**
+     * Set the strategy used to look up a per-request {@link DecryptionConfiguration} list.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setConfigurationLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, List<DecryptionConfiguration>> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        configurationLookupStrategy = Constraint.isNotNull(strategy,
+                "DecryptionConfiguration lookup strategy cannot be null");
+    }
+    
+    /**
+     * Set the strategy used to look up the {@link SecurityParametersContext} to set the parameters for.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setSecurityParametersContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, JWTSecurityParametersContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        securityParametersContextLookupStrategy = Constraint.isNotNull(strategy,
+                "SecurityParametersContext lookup strategy cannot be null");
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (resolver == null) {
+            throw new ComponentInitializationException("DecryptionParametersResolver cannot be null");
+        } 
+        if (configurationLookupStrategy == null) {
+            throw new ComponentInitializationException("DecryptionConfiguraitonLookup cannot be null");
+        } 
+    }
+    
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+        log.debug("{} Resolving JWT DecryptionParameters for request", getLogPrefix());
+        
+        final List<DecryptionConfiguration> configs = configurationLookupStrategy.apply(profileRequestContext);
+        if (configs == null || configs.isEmpty()) {
+            log.error("{} No DecryptionConfigurations returned by lookup strategy", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_SEC_CFG);
+            return;
+        }
+        
+        final JWTSecurityParametersContext paramsCtx =
+                securityParametersContextLookupStrategy.apply(profileRequestContext);
+        if (paramsCtx == null) {
+            log.debug("{} No SecurityParametersContext returned by lookup strategy", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+            return;
+        }
+        
+        try {           
+            
+            final JWTDecryptionParameters params = 
+                    resolver.resolveSingle(buildCriteriaSet(profileRequestContext, configs));
+            paramsCtx.setDecryptionParameters(params);
+            log.debug("{} {} DecryptionParameters", getLogPrefix(),
+                    params != null ? "Resolved" : "Failed to resolve");
+        } catch (final ResolverException e) {
+            log.error("{} Error resolving DecryptionParameters", getLogPrefix(), e);
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_SEC_CFG);
+        }
+        
+    }
+    
+    /**
+     * Build the criteria used as input to the {@link JWTDecryptionParametersResolver}.
+     * 
+     * @param profileRequestContext current profile request context
+     * @param configs a list of {@link DecryptionConfiguration}s to add to the criteria set.
+     * 
+     * @return the criteria set to use
+     */
+    @Nonnull
+    private CriteriaSet buildCriteriaSet(@Nonnull final ProfileRequestContext profileRequestContext,
+            final List<DecryptionConfiguration> configs) {
+        
+        final CriteriaSet criteria = new CriteriaSet();
+        criteria.add(new DecryptionConfigurationCriterion(configs));
+        
+        // Build a static credential criteria. Extract the decryption credential from the RP config.
+        final RelyingPartyContext rpCtx = relyingPartyContextLookupStrategy.apply(profileRequestContext);     
+        if (rpCtx != null && rpCtx.getConfiguration() != null &&
+                rpCtx.getProfileConfig() instanceof OIDCAuthorizationConfiguration) {
+            final OIDCAuthorizationConfiguration profileConfiguration = 
+                    (OIDCAuthorizationConfiguration) rpCtx.getProfileConfig();
+            
+            if (profileConfiguration != null) {
+                criteria.add(new StaticCredentialCriterion(
+                        profileConfiguration.getClientCredential(profileRequestContext)));
+            } else {
+                log.warn("{} Profile configuration not available, client credential missing", getLogPrefix());
+            }
+        }
+        
+        return criteria;
+        
+    }
+    
+}
+    
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index 008cf5d..72d54eb 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -9,8 +9,14 @@
 
     default-init-method="initialize" default-destroy-method="destroy">
 
-    <!-- System beans needed for extension to function, loaded after global.xml. The default template shows an incomplete 
-        example authentication flow descriptor which can be removed if not needed -->
+    <!-- System beans needed for extension to function, loaded after global.xml.  -->
+    
+    <bean id="shibboleth.authn.oidc.rp.JWKCredential" abstract="true"
+        class="net.shibboleth.oidc.security.impl.BasicJWKCredentialFactoryBean" />
+
+    <bean id="shibboleth.authn.oidc.rp.ExpiringJWKCredential" abstract="true"
+        class="net.shibboleth.oidc.security.impl.BasicExpiringJWTSharedSecretCredentialFactoryBean"
+        p:credentialExpiresAt="%{idp.authn.oidc.rp.client.clientSecretExpiresAt:PT0S}" />
 
     <!-- Functions use by the flow and global beans -->
 
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 62a1059..0d1009a 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
@@ -252,23 +252,42 @@
 
 
     <!-- ID_TOKEN Decryption -->
-    
-     <bean id="PopulateIDTokenDecryptionParameters" parent="NestedWebFlowProfileActionAdaptor" scope="prototype">
+
+    <bean id="PopulateIDTokenDecryptionParameters" parent="NestedWebFlowProfileActionAdaptor" scope="prototype">
         <constructor-arg>
-            <bean class="org.opensaml.profile.action.impl.PopulateDecryptionParameters"
+            <bean class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateJWTDecryptionParameters"
                 p:configurationLookupStrategy-ref="shibboleth.authn.oidc.rp.DecryptionConfigurationLookup"
-                p:decryptionParametersResolver-ref="shibboleth.DecryptionParametersResolver" />
+                p:decryptionParametersResolver-ref="shibboleth.authn.oidc.rp.IDTokenDecryptionParametersResolver" />
         </constructor-arg>
     </bean>
-         
-        <!-- FIXME: this bean already exists in security-system -->
+
+    <bean id="shibboleth.authn.oidc.rp.IDTokenDecryptionParametersResolver"
+        class="net.shibboleth.oidc.security.impl.DefaultJWTDecryptionParametersResolver"/>
+
     <bean id="shibboleth.authn.oidc.rp.DecryptionConfigurationLookup" lazy-init="true"
-        class="net.shibboleth.idp.profile.config.navigate.DecryptionConfigurationLookupFunction"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.config.navigate.JWTDecryptionConfigurationLookupFunction"
         p:relyingPartyConfigurationResolver-ref="shibboleth.RelyingPartyConfigurationResolver" />
-        
-    <bean id="shibboleth.DecryptionParametersResolver"
-        class="org.opensaml.xmlsec.impl.BasicDecryptionParametersResolver" />
-        
+
+    <bean id="IDTokenInAccessTokenUpdateStrategy" 
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.IDTokenInAccessTokenUpdateStrategy"/>
+
+    <bean id="DecryptJWT" parent="NestedWebFlowProfileActionAdaptor" scope="prototype">
+        <constructor-arg>
+            <bean class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.DecryptJWT">
+                <property name="jwtTokenLookupStrategy">
+                    <bean class="net.shibboleth.idp.profile.context.navigate.SpringExpressionContextLookupFunction"
+                        c:_0="#{ T(org.opensaml.profile.context.ProfileRequestContext) }"
+                        c:outputType="#{T(com.nimbusds.jwt.EncryptedJWT)}"
+                        c:expression="#input.getInboundMessageContext().getSubcontext(T(net.shibboleth.idp.plugin.authn.oidc.rp.context.AccessTokenResponseContext)).getIdToken()" />
+                </property>
+                <property name="jwtUpdateStrategy">
+                    <ref bean="IDTokenInAccessTokenUpdateStrategy"/>
+                </property>
+            </bean>
+        </constructor-arg>
+    </bean>
+
+
     <!-- ID_TOKEN Signature Validation -->
 
     <bean id="PopulateIDTokenSignatureValidationParameters" parent="NestedWebFlowProfileActionAdaptor"
@@ -296,8 +315,7 @@
                     <list>
                         <bean
                             class="net.shibboleth.idp.plugin.authn.oidc.rp.metadata.impl.OIDCProviderMetadataLookupHandler"
-                            scope="prototype" 
-                            p:copyContextStrategy-ref="OutboundOIDCMetadataContextLookup"
+                            scope="prototype" p:copyContextStrategy-ref="OutboundOIDCMetadataContextLookup"
                             p:providerMetadataResolver-ref="shibboleth.oidc.rp.ProviderMetadataResolver" />
                         <bean class="net.shibboleth.oidc.security.impl.JWTMessageSignatureSecurityHandler"
                             scope="prototype">
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
index 5d45e82..367a719 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
@@ -109,6 +109,7 @@
     <!-- TODO claim validation will differ per grant_type -->
     <action-state id="ValidateToken">
         <evaluate expression="PopulateIDTokenDecryptionParameters" />
+        <evaluate expression="DecryptJWT"/>
         <evaluate expression="PopulateIDTokenSignatureValidationParameters" />
         <evaluate expression="HandleIDTokenValidation" />
 
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
index cc799db..b565388 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
@@ -23,7 +23,7 @@
         p:alwaysIncludedAttributes="%{idp.authn.oidc.rp.alwaysIncludedAttributes:}" />
 
     <!-- FIXME This will NEED a new ID and possibly class. If not, the OP plugin and RP plugin can not be installed together -->
-    <!--  Only load the default client_id and client_secret if discovery is disabled -->
+    <!-- Only load the default client_id and client_secret if discovery is disabled -->
     <bean id="OIDC.SSO" parent="AbstractOIDCSSOProfile" lazy-init="true"
         class="net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration"
         p:httpRequestMethod="%{idp.authn.oidc.rp.httpRequestMethod:GET}"
@@ -33,84 +33,86 @@
         p:deniedUserInfoAttributes="%{idp.authn.oidc.rp.deniedUserInfoAttributes:}"
         p:clientId="#{%{idp.authn.oidc.rp.discoveryRequired:false} == true ? null : '%{idp.authn.oidc.rp.client.clientId:}'}"
         p:clientCredential="#{%{idp.authn.oidc.rp.discoveryRequired:false} == true ? {null} : getObject('shibboleth.authn.oidc.rp.DefaultCredential')}"
-        p:clientAuthenticationMethod="%{idp.authn.oidc.rp.clientAuthenticationMethod:client_secret_basic}"/>
-    
+        p:clientAuthenticationMethod="%{idp.authn.oidc.rp.clientAuthenticationMethod:client_secret_basic}" />
+
 
     <!-- Security Configuration Defaults. These settings establish the default security configurations for signatures and 
         loads the default credentials used. -->
 
     <bean id="shibboleth.authn.oidc.rp.DefaultSecurityConfiguration"
         class="net.shibboleth.oidc.profile.config.OIDCSecurityConfiguration">
-        <!-- Add these back were appropriate -->
-        <!-- <property name="signatureSigningConfiguration"> <ref bean="#{'%{idp.oidc.signing.config:shibboleth.oidc.SigningConfiguration}'.trim()}" 
-            /> </property> <property name="encryptionConfiguration"> <ref bean="#{'%{idp.oidc.encryption.config:shibboleth.oidc.EncryptionConfiguration}'.trim()}" 
-            /> </property> <property name="requestObjectDecryptionConfiguration"> <ref bean="#{'%{idp.oidc.rodecrypt.config:shibboleth.oidc.requestObjectDecryptionConfiguration}'.trim()}" 
-            /> </property> <property name="requestObjectSignatureValidationConfiguration"> <ref bean="#{'%{idp.oidc.rovalid.config:shibboleth.oidc.requestObjectSignatureValidationConfiguration}'.trim()}" 
-            /> </property> <property name="tokenEndpointJwtSignatureValidationConfiguration"> <ref bean="#{'%{idp.oidc.rovalid.config:shibboleth.oidc.tokenEndpointJwtSignatureValidationConfiguration}'.trim()}" 
-            /> </property> -->
-            <property name="idTokenJwtSignatureValidationConfig"> 
-                <ref bean="#{'%{idp.oidc.rp.rovalid.config:shibboleth.authn.oidc.rp.IDTokenJwtSignatureValidationConfiguration}'.trim()}"/>
-            </property>
+        <property name="idTokenJwtDecryptionConfig">
+            <ref
+                bean="#{'%{dp.authn.oidc.rp.idtoken.decrypt.config:shibboleth.authn.oidc.rp.DefaultDecryptionConfiguration}'.trim()}" />
+        </property>
+        <property name="idTokenJwtSignatureValidationConfig">
+            <ref
+                bean="#{'%{dp.authn.oidc.rp.idtoken.valid.config:shibboleth.authn.oidc.rp.IDTokenJwtSignatureValidationConfiguration}'.trim()}" />
+        </property>
     </bean>
+
     
-   <!-- For developers to override per RP config --> 
-   <bean id="shibboleth.authn.oidc.rp.ExpiringJWKCredential" abstract="true"
-        class="net.shibboleth.oidc.security.impl.BasicExpiringJWTStaticCredentialFactoryBean"
-        p:credentialExpiresAt="%{idp.authn.oidc.rp.client.clientSecretExpiresAt:PT0S}"/>
-        
-   <bean id="shibboleth.authn.oidc.rp.DefaultCredential"
+
+    <bean id="shibboleth.authn.oidc.rp.DefaultCredential" 
         parent="shibboleth.authn.oidc.rp.ExpiringJWKCredential"
-        p:secret="%{idp.authn.oidc.rp.client.clientSecret:#{null}}"
-        p:keyNames="defaultPropertiesClientSecret"/>
-        
-    
+        p:secret="%{idp.authn.oidc.rp.client.clientSecret:#{null}}" 
+        p:keyNames="defaultPropertiesClientSecret"
+        p:encMethod="%{idp.authn.oidc.rp.client.clientSecret.encMethods:A256GCM}"
+        p:alg="dir" />
+
+    <bean id="shibboleth.authn.oidc.rp.DefaultDecryptionConfiguration"
+        class="net.shibboleth.oidc.security.impl.BasicJWTDecryptionConfiguration"
+        p:KEKCredentialResolver-ref="defaultOIDCRPKeyEncryptionCredentialResolver"
+        p:contentEncryptionKeyCredentialResolver-ref="defaultOIDCRPContentEncryptionKeyCredentialResolver"/>
+
+    <!-- A resolver to public/private keys global to the RP -->
+    <bean id="defaultOIDCRPKeyEncryptionCredentialResolver"
+        class="org.opensaml.security.credential.impl.ChainingCredentialResolver">
+        <constructor-arg>
+            <list>                
+                <bean class="org.opensaml.security.credential.impl.StaticCredentialResolver"
+                    c:credentials-ref="shibboleth.authn.oidc.rp.DefaultKeyEncryptionCredentials" />
+            </list>
+        </constructor-arg>
+    </bean>
     
-     <!-- Configuration for supported algorithms for token endpoint authentication JWT signature validation. -->
-     <!-- TODO This was a parent bean, but as that was not compatible with the new trust engine stuff, I moved to it's own class for now -->
-    <bean id="shibboleth.authn.oidc.rp.IDTokenJwtSignatureValidationConfiguration" 
+    <!-- A pre-shared Direct Encryption key e.g. a pairwise client_secret from the input criterion -->
+    <bean id="defaultOIDCRPContentEncryptionKeyCredentialResolver"
+        class="org.opensaml.security.credential.impl.ChainingCredentialResolver">
+        <constructor-arg>
+            <list>                
+                <bean id="CriterionCredentialResolver"
+                    class="net.shibboleth.oidc.security.impl.CriterionCredentialResolver" />
+            </list>
+        </constructor-arg>
+    </bean>
+
+    <!-- Configuration for supported algorithms for token endpoint authentication JWT signature validation. -->
+    <!-- TODO This was a parent bean, but as that was not compatible with the new trust engine stuff, I moved to it's own 
+        class for now -->
+    <bean id="shibboleth.authn.oidc.rp.IDTokenJwtSignatureValidationConfiguration"
         class="net.shibboleth.oidc.security.impl.BasicSignatureValidationConfiguration"
         p:signatureTrustEngine-ref="ExplicitKeySignedJWTTrustEngine">
-        <!-- <property name="signatureAlgorithms">
-            <list>
-                <util:constant
-                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_RS_256" />
-                <util:constant
-                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_RS_384" />
-                <util:constant
-                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_RS_512" />
-                <util:constant
-                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_ES_256" />
-                <util:constant 
-                     static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_ES_384" /> 
-                <util:constant 
-                     static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_ES_512" /> 
-                <util:constant
-                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_HS_256" />
-                <util:constant
-                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_HS_384" />
-                <util:constant
-                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_HS_512" />
-            </list>
-        </property> -->
     </bean>
-    
-    <bean id="defaultSignedJWTCredentialResolver" class="org.opensaml.security.credential.impl.ChainingCredentialResolver">
+
+    <bean id="defaultSignedJWTCredentialResolver"
+        class="org.opensaml.security.credential.impl.ChainingCredentialResolver">
         <constructor-arg>
             <list>
-               <bean id="OIDCProviderMetadataCredentialResolver" 
-                        class="net.shibboleth.oidc.security.impl.ProviderMetadataCredentialResolver"
-                        p:remoteJwkSetCache-ref="shibboleth.authn.oidc.rp.RemoteJwkSetCache"/>
-                 <bean id="CriterionCredentialResolver" 
-                        class="net.shibboleth.oidc.security.impl.CriterionCredentialResolver"/>
+                <bean id="OIDCProviderMetadataCredentialResolver"
+                    class="net.shibboleth.oidc.security.impl.ProviderMetadataCredentialResolver"
+                    p:remoteJwkSetCache-ref="shibboleth.authn.oidc.rp.RemoteJwkSetCache" />
+                <bean id="CriterionCredentialResolver"
+                    class="net.shibboleth.oidc.security.impl.CriterionCredentialResolver" />
             </list>
         </constructor-arg>
     </bean>
-    
+
     <bean id="ExplicitKeySignedJWTTrustEngine"
         class="net.shibboleth.oidc.security.impl.ExplicitKeySignedJWTTrustEngine"
-        c:resolver-ref="defaultSignedJWTCredentialResolver"/>
-    
-    
+        c:resolver-ref="defaultSignedJWTCredentialResolver" />
+
+
 
 
 </beans>
diff --git a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/rp-credentials.xml b/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/rp-credentials.xml
new file mode 100644
index 0000000..7dc5a6f
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/rp-credentials.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">
+
+    <!-- Your RP's default encryption (really decryption) keys, set via property file. -->
+    <util:list id="shibboleth.authn.oidc.rp.DefaultKeyEncryptionCredentials">
+        <bean parent="shibboleth.authn.oidc.rp.JWKCredential" p:failIfResourceIsNull="false" 
+        p:resource="%{idp.authn.oidc.rp.client.enc.key:#{null}}" />
+    </util:list>
+    
+</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/OIDCRPFlowTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
index e5fe1d2..0b1a817 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
@@ -42,7 +42,6 @@ import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.security.credential.Credential;
 import org.opensaml.security.credential.CredentialResolver;
 import org.opensaml.security.credential.UsageType;
-import org.opensaml.xmlsec.DecryptionConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -53,7 +52,6 @@ import org.springframework.webflow.engine.impl.FlowExecutionImpl;
 import org.springframework.webflow.execution.FlowExecution;
 import org.springframework.webflow.test.MockFlowBuilderContext;
 
-import com.google.common.base.Enums;
 import com.nimbusds.jose.Algorithm;
 import com.nimbusds.jose.EncryptionMethod;
 import com.nimbusds.jose.JOSEException;
@@ -108,14 +106,16 @@ import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
 import net.shibboleth.oidc.security.credential.BasicExpiringJWKCredential;
 import net.shibboleth.oidc.security.credential.BasicJWKCredential;
 import net.shibboleth.oidc.security.credential.JWKCredential;
-import net.shibboleth.oidc.security.impl.BasicExpiringJWTStaticCredentialFactoryBean;
+import net.shibboleth.oidc.security.credential.impl.JWKEncryptionCredentialContext;
+import net.shibboleth.oidc.security.impl.BasicJWTDecryptionConfiguration;
 import net.shibboleth.oidc.security.impl.BasicSignatureValidationConfiguration;
+import net.shibboleth.oidc.security.impl.CriterionCredentialResolver;
 import net.shibboleth.oidc.security.impl.ExplicitKeySignedJWTTrustEngine;
 import net.shibboleth.oidc.security.impl.JWSAssemblyUtils;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
-import net.shibboleth.utilities.java.support.logic.FunctionSupport;
+import net.shibboleth.utilities.java.support.collection.Pair;
 import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
 import net.shibboleth.utilities.java.support.resolver.ResolverException;
 import okhttp3.mockwebserver.MockResponse;
@@ -306,6 +306,9 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         
         loadBeanDefinitionsFromXmlFile(builderContext, 
                 new ClassPathResource("attribute/filter/attribute-filter-system.xml"), null);
+        
+        loadBeanDefinitionsFromXmlFile(builderContext, 
+                new ClassPathResource("conf/authn/rp-credentials.xml"), null);
     }
     
     /**
@@ -388,6 +391,56 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         return accessTokenSerialized;
     }
     
+    /**
+     * Create an OAuth access token with a runtime constructed id_token. This allows the
+     * expiry to be current. The token is signed using HS256 and encrypted using a key encryption
+     * management mode.
+     * 
+     * @return a serialized access token response.
+     * 
+     * @throws Exception on error.
+     */
+    private Pair<String, RSAKey> createAccessTokenResponseJSONSignedAndAsymmetricEncrypted() throws Exception {
+        final var header = new JWSHeader.Builder(JWSAlgorithm.HS256)
+                .type(JOSEObjectType.JWT)
+                .build();
+        final var payload = new JWTClaimsSet.Builder()
+                .issuer(OP_ISSUER_ID)
+                .audience(List.of(CLIENT_ID,"demo_rp2"))
+                .subject("jdoe")
+                .claim("nonce", "abadnonce")
+                .claim("azp", CLIENT_ID)
+                .claim("name","jdoe")
+                .expirationTime(Date.from(Instant.now().plusSeconds(120)))
+                .build();
+        payload.getClaims().forEach((k,v) -> log.debug("{}:{}",k,v));
+        final var signedJWT = new SignedJWT(header,payload);
+        signedJWT.sign(new MACSigner(CLIENT_SECRET));
+        
+        
+        final RSAKey keyRecipient = new RSAKeyGenerator(2048)
+                .keyID("1")
+                .keyUse(KeyUse.ENCRYPTION)
+                .generate();
+        
+        final JWEObject jweObject = 
+                new JWEObject(new JWEHeader.Builder(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A256GCM)
+                .contentType("JWT")
+                .build(),
+                new Payload(signedJWT));
+        jweObject.encrypt(new RSAEncrypter(keyRecipient.toPublicJWK()));
+        final EncryptedJWT jwe = EncryptedJWT.parse(jweObject.serialize());
+        
+        final String accessTokenSerialized = "{\n"
+        + "  \"access_token\": \"W0y5aDNAzEPNpSzu1cuMG904BZuQFZJUUwG5F3ct0zydZWy1ji\",\n"
+        + "  \"token_type\": \"Bearer\",\n"
+        + "  \"id_token\": \""+jwe.serialize()+"\",\n"
+        + "  \"scope\": \"openid\"\n"
+        + "}";
+        log.debug("Access token: \n {}",accessTokenSerialized);
+        return new Pair<String, RSAKey>(accessTokenSerialized, keyRecipient);
+    }
+    
     /**
      * Create a signed UserInfo response JWT.
      * 
@@ -640,7 +693,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         partyContext.setProfileConfig(partyConfig);
         partyConfig.setClientAuthenticationMethod("client_secret_basic");
         partyConfig.setClientId(CLIENT_ID);
-        partyConfig.setClientCredential(createCredentialFromSharedSecret(CLIENT_SECRET));
+        partyConfig.setClientCredential(createDirectEncryptionCredentialFromSharedSecret(CLIENT_SECRET));
         // Set a default security config for the profile config
         final OIDCSecurityConfiguration secConfig = new OIDCSecurityConfiguration();
         final BasicSignatureValidationConfiguration<SignedJWT> sigValidation = 
@@ -866,34 +919,152 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         partyContext.setProfileConfig(partyConfig);
         partyConfig.setClientAuthenticationMethod("client_secret_basic");
         partyConfig.setClientId(CLIENT_ID);
-        partyConfig.setClientCredential(createCredentialFromSharedSecret(CLIENT_SECRET));
+        partyConfig.setClientCredential(createDirectEncryptionCredentialFromSharedSecret(CLIENT_SECRET));
         // Set a default security config for the profile config
         final OIDCSecurityConfiguration secConfig = new OIDCSecurityConfiguration();
+        
+        //Use a mocked signature config
         final BasicSignatureValidationConfiguration<SignedJWT> sigValidation = 
                 new BasicSignatureValidationConfiguration<>();
         sigValidation.setSignatureTrustEngine(new ExplicitKeySignedJWTTrustEngine(
-                new CredentialResolver() {
+                new CriterionCredentialResolver()));
+        
+        secConfig.setIdTokenJwtSignatureValidationConfig(sigValidation);   
+        
+        final var decryptConfig = new BasicJWTDecryptionConfiguration();
+        
+        // Use a real resolver.
+        decryptConfig.setContentEncryptionKeyCredentialResolver(new CriterionCredentialResolver());
+        
+        secConfig.setIdTokenJwtDecryptionConfig(decryptConfig);    
+        partyConfig.setSecurityConfiguration(secConfig);
+        
+                
+        final RelyingPartyConfiguration rPartyConfig = new RelyingPartyConfiguration();
+        rPartyConfig.setResponderId("http://idp.example.com/");
+        partyContext.setConfiguration(rPartyConfig);
+        nestPrc.addSubcontext(partyContext);
+        
+       
+        // Setup outbound context
+        final MessageContext outMsgCtx = new MessageContext();        
+        outMsgCtx.setMessage(createAuthenticationRequest());        
+        outMsgCtx.addSubcontext(createPeerContext());
+        outMsgCtx.addSubcontext(createResponseTypeAndModeContext());
+        nestPrc.setOutboundMessageContext(outMsgCtx);    
+        outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext(CLIENT_ID,null));
+        
+        // Setup inbound context.
+        final MessageContext inMsgCtx = new MessageContext();
+        inMsgCtx.setMessage(createAuthenticationResponse());
+        nestPrc.setInboundMessageContext(inMsgCtx);
+        
+        // Add prc to flow.
+        prc.getSubcontext(AuthenticationContext.class)
+        .addSubcontext(new ExternalAuthenticationContext(new ExternalAuthenticationImpl(false)));
+                        flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
+                        
+        
+        updateFlowExecution(flowExecution);
+        
+        //set start view and ending event to transition on.
+        externalContext.setEventId("proceed");
+        setCurrentState("AuthRequest");       
+        resumeFlow(externalContext);
+        
+        mockOPServer.shutdown();
+        
+        //assert success conditions
+        assertFlowExecutionEnded();
+        assertNotNull(prc.getSubcontext(AuthenticationContext.class));
+        assertNotNull(prc.getSubcontext(SubjectCanonicalizationContext.class));
+        assertEquals(prc.getSubcontext(SubjectCanonicalizationContext.class).getPrincipalName(),"jdoe");
+      
+       
+    }
+    
+    /**
+     * Uses symmetric MAC and asymmetric encryption.
+     * 
+     * @throws Exception on error.
+     */
+    @Test 
+    public void testAuthnFlowFromAuthorizationCallback_Using_SignedAndAsymetricEncryptedIDToken() 
+            throws Exception {
+        
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);        
+        
+        final Map<String,String> mockProperties = Map.of(
+                "idp.service.clientinfo.failFast","false",
+                "idp.entityID", "http://idp.example.com/",
+                "idp.authn.oidc.rp.proxyIssuer",OP_ISSUER_ID,
+                "idp.oidc.rp.redirecturl.allowedOrigins", RP_ALLOWED_ORIGINS);
+        
+        setMockProperties(mockProperties);
+        
+        final Pair<String, RSAKey> accessTokenAndKey = createAccessTokenResponseJSONSignedAndAsymmetricEncrypted();
+        final MockWebServer mockOPServer = createSimpleServer();
+        // First is token exchange
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(accessTokenAndKey.getFirst()));
+        // Second is userInfo
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(USERINFO_RESPONSE));
+        mockOPServer.start(9918);
+        
+
+        final FlowExecutionImpl flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
+                .createFlowExecution(getFlowDefinition());
+        final ProfileRequestContext prc =  buildProfileRequestContext("authn/OIDCRelyingParty", false,false);
+        prc.getSubcontext(AuthenticationContext.class).setAuthenticatingAuthority(OP_ISSUER_ID);
+        
+        // create a nested PRC under the authentication context
+        final ProfileRequestContext nestPrc = (ProfileRequestContext) 
+                prc.getSubcontext(AuthenticationContext.class).addSubcontext(new ProfileRequestContext(), true);  
+        
+        // Add under nest PRC
+        final RelyingPartyContext partyContext = new RelyingPartyContext();
+        final OIDCAuthorizationConfiguration partyConfig = new OIDCAuthorizationConfiguration();        
+        partyContext.setProfileConfig(partyConfig);
+        partyConfig.setClientAuthenticationMethod("client_secret_basic");
+        partyConfig.setClientId(CLIENT_ID);
+        partyConfig.setClientCredential(createDirectEncryptionCredentialFromSharedSecret(CLIENT_SECRET));
+        // Set a default security config for the profile config
+        final OIDCSecurityConfiguration secConfig = new OIDCSecurityConfiguration();
+        final BasicSignatureValidationConfiguration<SignedJWT> sigValidation = 
+                new BasicSignatureValidationConfiguration<>();
+        sigValidation.setSignatureTrustEngine(new ExplicitKeySignedJWTTrustEngine(
+                new CriterionCredentialResolver()));
+        
+        secConfig.setIdTokenJwtSignatureValidationConfig(sigValidation);   
+        
+        final var decryptConfig = new BasicJWTDecryptionConfiguration();
+        decryptConfig.setKEKCredentialResolver(new CredentialResolver() {
             
             @Override
             public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
                 final BasicJWKCredential jwkCredential = new BasicJWKCredential();
-                jwkCredential.setAlgorithm(JWSAlgorithm.HS256);
-                jwkCredential.setKid("secret_key");                
-                jwkCredential.setSecretKey(new SecretKeySpec(
-                        JWSAssemblyUtils.getSecretBytes(CLIENT_SECRET), "NONE"));              
+                jwkCredential.setAlgorithm(JWEAlgorithm.RSA_OAEP_256);
+                jwkCredential.setKid(accessTokenAndKey.getSecond().getKeyID());                
+                try {
+                    jwkCredential.setPrivateKey(accessTokenAndKey.getSecond().toPrivateKey());
+                    jwkCredential.setPublicKey(accessTokenAndKey.getSecond().toPublicKey());
+                } catch (final JOSEException e) {
+                    fail();
+                }                
                 return jwkCredential;
-            }
-            
+            }            
             @Override
             public Iterable<Credential> resolve(final CriteriaSet criteria) throws ResolverException {
                 return List.of(resolveSingle(criteria));
             }
-        }));
-        
-        secConfig.setIdTokenJwtSignatureValidationConfig(sigValidation);
-        final var decryptConfig = new OIDCDecryptionConfiguration();
-        secConfig.setRequestObjectDecryptionConfiguration(null)
-        
+        });
+
+        secConfig.setIdTokenJwtDecryptionConfig(decryptConfig);    
         partyConfig.setSecurityConfiguration(secConfig);
         
                 
@@ -1027,17 +1198,43 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
     }
     
     /**
-     * Create a basic {@link JWKCredential} from the given shared secret.
+     * Create a direct encryption {@link JWKCredential} from the given shared secret.
      * 
      * @param secret the secret to convert to a {@link JWKCredential}.
      * 
      * @return the credential
      */
-    private JWKCredential createCredentialFromSharedSecret(final String secret) {
+    private JWKCredential createDirectEncryptionCredentialFromSharedSecret(final String secret) {
         final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
         jwkCredential.setSecretKey(new SecretKeySpec(JWSAssemblyUtils.getSecretBytes(secret), "NONE"));
         jwkCredential.setCredentialExpiresAt(Duration.ZERO);
         jwkCredential.setUsageType(UsageType.UNSPECIFIED);
+        jwkCredential.getCredentialContextSet().add(
+                new JWKEncryptionCredentialContext(EncryptionMethod.A256GCM));
+        jwkCredential.setKid("mockKey");
+        jwkCredential.getKeyNames().add("mockKey");
+        jwkCredential.setAlgorithm(JWEAlgorithm.DIR);
+        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 
+     */
+    private JWKCredential createKeyEncryptionCredential(final RSAKey secret) throws JOSEException {
+        final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+        jwkCredential.setPrivateKey(secret.toPrivateKey());
+        jwkCredential.setPublicKey(secret.toPublicKey());
+        jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+        jwkCredential.setUsageType(UsageType.ENCRYPTION);
+        
+        jwkCredential.setKid(secret.getKeyID());
+        jwkCredential.getKeyNames().add("mockKey");
+        jwkCredential.setAlgorithm(secret.getAlgorithm());
         return jwkCredential;
     }
     
diff --git a/idp-oidc-rp-impl/src/test/resources/conf/authn/rp-credentials.xml b/idp-oidc-rp-impl/src/test/resources/conf/authn/rp-credentials.xml
new file mode 100644
index 0000000..7dc5a6f
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/resources/conf/authn/rp-credentials.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">
+
+    <!-- Your RP's default encryption (really decryption) keys, set via property file. -->
+    <util:list id="shibboleth.authn.oidc.rp.DefaultKeyEncryptionCredentials">
+        <bean parent="shibboleth.authn.oidc.rp.JWKCredential" p:failIfResourceIsNull="false" 
+        p:resource="%{idp.authn.oidc.rp.client.enc.key:#{null}}" />
+    </util:list>
+    
+</beans>
\ No newline at end of file

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


More information about the commits mailing list