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