[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