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