[java-oidc-common] branch dev/JCOMOIDC-41 updated: JCOMOIDC-41 - Move OIDC Signature Validation resolvers and parameter classes to commons
Phil Smart
philip.smart at jisc.ac.uk
Thu Aug 18 13:54:58 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=29d093a6df357bbaaeb022d1f47fcd00285b73eb
The following commit(s) were added to refs/heads/dev/JCOMOIDC-41 by this push:
new 29d093a JCOMOIDC-41 - Move OIDC Signature Validation resolvers and parameter classes to commons
29d093a is described below
commit 29d093a6df357bbaaeb022d1f47fcd00285b73eb
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Thu Aug 18 14:54:52 2022 +0100
JCOMOIDC-41 - Move OIDC Signature Validation resolvers and parameter
classes to commons
- Created new JWT versions of the encryption and decryption
configuration and parameter classes.
- Updated the populate JWT parameters resolver to support the new
classes.
- Created a new Basic JWT encryption parameter resolver with logic more
appropriate to parameter resolution for use by the Nimbus encrypters.
- Substantially improved the
ProviderMetadataEncryptionParametersResolver to support the new base
resolver.
https://shibboleth.atlassian.net/browse/JCOMOIDC-41
---
...ration.java => JWTDecryptionConfiguration.java} | 2 +-
.../oidc/security/JWTEncryptionConfiguration.java | 44 ++
.../oidc/security/JWTEncryptionParameters.java | 95 +++
...n.java => JWTEncryptionParametersResolver.java} | 21 +-
.../security/JWTSignatureSigningParameters.java | 71 +++
... => JWTSignatureSigningParametersResolver.java} | 22 +-
.../security/SignatureValidationConfiguration.java | 2 -
.../context/JWTSecurityParametersContext.java | 16 +-
.../DecryptionConfigurationCriterion.java | 12 +-
.../oidc/security/criterion/JWKSetCriterion.java | 100 +++
...va => JWTEncryptionConfigurationCriterion.java} | 27 +-
.../impl/BasicJWTDecryptionConfiguration.java | 6 +-
.../impl/BasicJWTEncryptionConfiguration.java | 133 ++++
.../impl/BasicJWTEncryptionParametersResolver.java | 670 +++++++++++++++++++++
...BasicJWTSignatureSigningParametersResolver.java | 278 +++++++++
.../DefaultJWTDecryptionParametersResolver.java | 6 +-
.../PopulateJWTSignatureSigningParameters.java | 9 +-
...pulateJWTSignatureSigningParametersHandler.java | 11 +-
.../BasicJWTEncryptionParametersResolverTest.java | 400 ++++++++++++
.../impl/ExplicitKeySignedJWTTrustEngineTest.java | 3 +-
.../profile/config/OIDCSecurityConfiguration.java | 37 +-
21 files changed, 1875 insertions(+), 90 deletions(-)
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/DecryptionConfiguration.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTDecryptionConfiguration.java
similarity index 94%
rename from oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/DecryptionConfiguration.java
rename to oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTDecryptionConfiguration.java
index eb4b62b..f55a1f9 100644
--- a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/DecryptionConfiguration.java
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTDecryptionConfiguration.java
@@ -23,7 +23,7 @@ import org.opensaml.xmlsec.AlgorithmPolicyConfiguration;
import net.shibboleth.oidc.security.credential.JOSEObjectCredentialResolver;
-public interface DecryptionConfiguration extends AlgorithmPolicyConfiguration {
+public interface JWTDecryptionConfiguration extends AlgorithmPolicyConfiguration {
/**
* Get the CredentialResolver to use when processing the encrypted content.
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
new file mode 100644
index 0000000..53ca21e
--- /dev/null
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTEncryptionConfiguration.java
@@ -0,0 +1,44 @@
+package net.shibboleth.oidc.security;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.security.credential.Credential;
+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;
+
+public interface JWTEncryptionConfiguration extends AlgorithmPolicyConfiguration {
+
+ /**
+ * Get the list of data encryption credentials to use, in preference order.
+ *
+ * @return the list of encryption credentials, may be empty
+ */
+ @Nonnull @NonnullElements @Unmodifiable @NotLive public List<Credential> getDataEncryptionCredentials();
+
+ /**
+ * Get the list of preferred data encryption algorithm URIs, in preference order.
+ *
+ * @return the list of algorithm URIs, may be empty
+ */
+ @Nonnull @NonnullElements @Unmodifiable @NotLive public List<String> getDataEncryptionAlgorithms();
+
+ /**
+ * Get the list of key transport encryption credentials to use, in preference order.
+ *
+ * @return the list of encryption credentials, may be empty
+ */
+ @Nonnull @NonnullElements @Unmodifiable @NotLive public List<Credential> getKeyTransportEncryptionCredentials();
+
+ /**
+ * 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> getKeyTransportEncryptionAlgorithms();
+
+}
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTEncryptionParameters.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTEncryptionParameters.java
new file mode 100644
index 0000000..04601dc
--- /dev/null
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTEncryptionParameters.java
@@ -0,0 +1,95 @@
+package net.shibboleth.oidc.security;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.security.credential.Credential;
+
+/**
+ * The effective parameters to use when generating encrypted JWTs.
+ */
+public class JWTEncryptionParameters {
+
+ /** The EncryptedData encryption credential. */
+ private Credential dataEncryptionCredential;
+
+ /** The EncryptedKey encryption credential. */
+ private Credential keyTransportEncryptionCredential;
+
+ /** The EncryptedData encryption algorithm URI. */
+ private String dataEncryptionAlgorithmURI;
+
+ /** The EncryptedKey encryption algorithm URI. */
+ private String keyTransportEncryptionAlgorithmURI;
+
+ /**
+ * Get the encryption credential to use when encrypting the EncryptedData.
+ *
+ * @return the encryption credential
+ */
+ @Nullable public Credential getDataEncryptionCredential() {
+ return dataEncryptionCredential;
+ }
+
+ /**
+ * Set the encryption credential to use when encrypting the EncryptedData.
+ *
+ * @param credential the encryption credential
+ */
+ public void setDataEncryptionCredential(@Nullable final Credential credential) {
+ dataEncryptionCredential = credential;
+ }
+
+ /**
+ * Get the encryption credential to use when encrypting the EncryptedKey.
+ *
+ * @return the encryption credential
+ */
+ @Nullable public Credential getKeyTransportEncryptionCredential() {
+ return keyTransportEncryptionCredential;
+ }
+
+ /**
+ * Set the encryption credential to use when encrypting the EncryptedKey.
+ *
+ * @param credential the encryption credential
+ */
+ public void setKeyTransportEncryptionCredential(@Nullable final Credential credential) {
+ keyTransportEncryptionCredential = credential;
+ }
+
+ /**
+ * Get the encryption algorithm URI to use when encrypting the EncryptedData.
+ *
+ * @return an encryption algorithm URI
+ */
+ @Nullable public String getDataEncryptionAlgorithm() {
+ return dataEncryptionAlgorithmURI;
+ }
+
+ /**
+ * Set the encryption algorithm URI to use when encrypting the EncryptedData.
+ *
+ * @param uri an encryption algorithm URI
+ */
+ public void setDataEncryptionAlgorithm(@Nullable final String uri) {
+ dataEncryptionAlgorithmURI = uri;
+ }
+
+ /**
+ * Get the encryption algorithm URI to use when encrypting the EncryptedKey.
+ *
+ * @return an encryption algorithm URI
+ */
+ @Nullable public String getKeyTransportEncryptionAlgorithm() {
+ return keyTransportEncryptionAlgorithmURI;
+ }
+
+ /**
+ * Set the encryption algorithm URI to use when encrypting the EncryptedKey.
+ *
+ * @param uri an encryption algorithm URI
+ */
+ public void setKeyTransportEncryptionAlgorithm(@Nullable final String uri) {
+ keyTransportEncryptionAlgorithmURI = uri;
+ }
+}
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationConfiguration.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTEncryptionParametersResolver.java
similarity index 57%
copy from oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationConfiguration.java
copy to oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTEncryptionParametersResolver.java
index b593989..1215f16 100644
--- a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationConfiguration.java
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTEncryptionParametersResolver.java
@@ -17,25 +17,12 @@
package net.shibboleth.oidc.security;
-import javax.annotation.Nullable;
-
-import org.opensaml.security.trust.TrustEngine;
-import org.opensaml.xmlsec.AlgorithmPolicyConfiguration;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.Resolver;
/**
- * The configuration information to use when validating a signature.
- *
- * @param <T> the token type to validate
+ * An interface for components which resolve {@link JWTEncryptionParameters} based on a {@link CriteriaSet}.
*/
-//TODO We can not use the existing SignatureValidationConfiguration
-//as it implements the signaturetrustengine which is only compatible with XML.
-public interface SignatureValidationConfiguration<T> extends AlgorithmPolicyConfiguration {
-
- /**
- * Get the signature trust engine to use.
- *
- * @return the signature trust engine
- */
- @Nullable public TrustEngine<T> getSignatureTrustEngine();
+public interface JWTEncryptionParametersResolver extends Resolver<JWTEncryptionParameters, CriteriaSet> {
}
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTSignatureSigningParameters.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTSignatureSigningParameters.java
new file mode 100644
index 0000000..5fbd842
--- /dev/null
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTSignatureSigningParameters.java
@@ -0,0 +1,71 @@
+/*
+ * 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 javax.annotation.Nullable;
+
+import org.opensaml.security.credential.Credential;
+
+/**
+ * The effective parameters to use when generating an JWT signature.
+ */
+public class JWTSignatureSigningParameters {
+
+ /** The signing credential. */
+ private Credential signingCredential;
+
+ /** The signature algorithm URI. */
+ private String signatureAlgorithmURI;
+
+ /**
+ * Get the signing credential to use when signing.
+ *
+ * @return the signing credential
+ */
+ @Nullable public Credential getSigningCredential() {
+ return signingCredential;
+ }
+
+ /**
+ * Set the signing credential to use when signing.
+ *
+ * @param credential the signing credential
+ */
+ public void setSigningCredential(@Nullable final Credential credential) {
+ signingCredential = credential;
+ }
+
+ /**
+ * Get the signature algorithm URI to use when signing.
+ *
+ * @return a signature algorithm URI mapping
+ */
+ @Nullable public String getSignatureAlgorithm() {
+ return signatureAlgorithmURI;
+ }
+
+ /**
+ * Set the signature algorithm URI to use when signing.
+ *
+ * @param uri a signature algorithm URI mapping
+ */
+ public void setSignatureAlgorithm(@Nullable final String uri) {
+ signatureAlgorithmURI = uri;
+ }
+
+}
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationConfiguration.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTSignatureSigningParametersResolver.java
similarity index 56%
copy from oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationConfiguration.java
copy to oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTSignatureSigningParametersResolver.java
index b593989..ac79925 100644
--- a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationConfiguration.java
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/JWTSignatureSigningParametersResolver.java
@@ -17,25 +17,9 @@
package net.shibboleth.oidc.security;
-import javax.annotation.Nullable;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.Resolver;
-import org.opensaml.security.trust.TrustEngine;
-import org.opensaml.xmlsec.AlgorithmPolicyConfiguration;
-
-/**
- * The configuration information to use when validating a signature.
- *
- * @param <T> the token type to validate
- */
-//TODO We can not use the existing SignatureValidationConfiguration
-//as it implements the signaturetrustengine which is only compatible with XML.
-public interface SignatureValidationConfiguration<T> extends AlgorithmPolicyConfiguration {
-
- /**
- * Get the signature trust engine to use.
- *
- * @return the signature trust engine
- */
- @Nullable public TrustEngine<T> getSignatureTrustEngine();
+public interface JWTSignatureSigningParametersResolver extends Resolver<JWTSignatureSigningParameters, CriteriaSet> {
}
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationConfiguration.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationConfiguration.java
index b593989..d708034 100644
--- a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationConfiguration.java
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationConfiguration.java
@@ -27,8 +27,6 @@ import org.opensaml.xmlsec.AlgorithmPolicyConfiguration;
*
* @param <T> the token type to validate
*/
-//TODO We can not use the existing SignatureValidationConfiguration
-//as it implements the signaturetrustengine which is only compatible with XML.
public interface SignatureValidationConfiguration<T> extends AlgorithmPolicyConfiguration {
/**
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/context/JWTSecurityParametersContext.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/context/JWTSecurityParametersContext.java
index 1dfa595..ac35481 100644
--- a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/context/JWTSecurityParametersContext.java
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/context/JWTSecurityParametersContext.java
@@ -21,12 +21,12 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opensaml.messaging.context.BaseContext;
-import org.opensaml.xmlsec.EncryptionParameters;
-import org.opensaml.xmlsec.SignatureSigningParameters;
import com.nimbusds.jwt.SignedJWT;
import net.shibboleth.oidc.security.JWTDecryptionParameters;
+import net.shibboleth.oidc.security.JWTEncryptionParameters;
+import net.shibboleth.oidc.security.JWTSignatureSigningParameters;
import net.shibboleth.oidc.security.SignatureValidationParameters;
/**
@@ -39,13 +39,13 @@ public final class JWTSecurityParametersContext extends BaseContext {
@Nullable private SignatureValidationParameters<SignedJWT> signatureValidationParameters;
/** Signature signing parameters.*/
- @Nullable private SignatureSigningParameters signatureSigningParameters;
+ @Nullable private JWTSignatureSigningParameters signatureSigningParameters;
/** Decryption parameters. */
@Nullable private JWTDecryptionParameters decryptionParameters;
/** Encryption parameters.*/
- @Nullable private EncryptionParameters encryptionParameters;
+ @Nullable private JWTEncryptionParameters encryptionParameters;
/**
* Get the parameters to use for signature validation operations.
@@ -74,7 +74,7 @@ public final class JWTSecurityParametersContext extends BaseContext {
*
* @return the parameters
*/
- @Nullable public SignatureSigningParameters getSignatureSigningParameters() {
+ @Nullable public JWTSignatureSigningParameters getSignatureSigningParameters() {
return signatureSigningParameters;
}
@@ -86,7 +86,7 @@ public final class JWTSecurityParametersContext extends BaseContext {
* @return this context
*/
@Nonnull public JWTSecurityParametersContext setSignatureSigningParameters(
- @Nullable final SignatureSigningParameters params) {
+ @Nullable final JWTSignatureSigningParameters params) {
signatureSigningParameters = params;
return this;
}
@@ -119,7 +119,7 @@ public final class JWTSecurityParametersContext extends BaseContext {
*
* @return this context
*/
- public JWTSecurityParametersContext setEncryptionParameters(@Nullable final EncryptionParameters params) {
+ public JWTSecurityParametersContext setEncryptionParameters(@Nullable final JWTEncryptionParameters params) {
encryptionParameters = params;
return this;
}
@@ -129,7 +129,7 @@ public final class JWTSecurityParametersContext extends BaseContext {
*
* @return the parameters
*/
- @Nullable public EncryptionParameters getEncryptionParameters() {
+ @Nullable public JWTEncryptionParameters getEncryptionParameters() {
return encryptionParameters;
}
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/DecryptionConfigurationCriterion.java
index 0fb0d22..368c588 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/DecryptionConfigurationCriterion.java
@@ -21,7 +21,7 @@ import java.util.List;
import javax.annotation.Nonnull;
-import net.shibboleth.oidc.security.DecryptionConfiguration;
+import net.shibboleth.oidc.security.JWTDecryptionConfiguration;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
@@ -30,12 +30,12 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.resolver.Criterion;
/**
- * Criterion which holds one or more instances of {@link DecryptionConfiguration}.
+ * Criterion which holds one or more instances of {@link JWTDecryptionConfiguration}.
*/
public class DecryptionConfigurationCriterion implements Criterion {
/** The list of configuration instances. */
- @Nonnull @NonnullElements private final List<DecryptionConfiguration> configs;
+ @Nonnull @NonnullElements private final List<JWTDecryptionConfiguration> configs;
/**
* Constructor.
@@ -43,7 +43,7 @@ public class DecryptionConfigurationCriterion implements Criterion {
* @param configurations list of configuration instances
*/
public DecryptionConfigurationCriterion(@Nonnull @NonnullElements @NotEmpty final
- List<DecryptionConfiguration> configurations) {
+ List<JWTDecryptionConfiguration> configurations) {
configs = List.copyOf(Constraint.isNotNull(configurations, "List of configurations cannot be null"));
Constraint.isNotEmpty(configs, "At least one configuration is required");
@@ -55,7 +55,7 @@ public class DecryptionConfigurationCriterion implements Criterion {
* @param configurations varargs array of configuration instances
*/
public DecryptionConfigurationCriterion(@Nonnull @NonnullElements @NotEmpty final
- DecryptionConfiguration... configurations) {
+ JWTDecryptionConfiguration... configurations) {
configs = List.of(Constraint.isNotNull(configurations, "List of configurations cannot be null"));
Constraint.isNotEmpty(configs, "At least one configuration is required");
}
@@ -66,7 +66,7 @@ public class DecryptionConfigurationCriterion implements Criterion {
* @return the list of configuration instances
*/
@Nonnull @NonnullElements @NotLive @Unmodifiable @NotEmpty
- public List<DecryptionConfiguration> getConfigurations() {
+ public List<JWTDecryptionConfiguration> getConfigurations() {
return configs;
}
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/JWKSetCriterion.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/JWKSetCriterion.java
new file mode 100644
index 0000000..b879911
--- /dev/null
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/JWKSetCriterion.java
@@ -0,0 +1,100 @@
+/*
+ * 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.criterion;
+
+import javax.annotation.Nullable;
+
+import com.nimbusds.jose.jwk.JWKSet;
+
+import net.shibboleth.utilities.java.support.resolver.Criterion;
+
+/**
+ * An implementation of {@link Criterion} which specifies criteria based
+ * on the contents of a {@link JWKSet} element.
+ */
+public final class JWKSetCriterion implements Criterion {
+
+ /** The JWKSet which serves as a source of credentials. */
+ @Nullable private JWKSet jwkSet;
+
+ /**
+ * Constructor.
+ *
+ * @param jwkSet the JWKSet credentials
+ */
+ public JWKSetCriterion(@Nullable final JWKSet jwkSet) {
+ setJWKSet(jwkSet);
+ }
+
+ /**
+ * Gets the JWKSet which is the source of credentials.
+ *
+ * @return the JWKSet credentials
+ */
+ @Nullable public JWKSet getJWKSet() {
+ return jwkSet;
+ }
+
+ /**
+ * Sets the JWKSet which is the source of credentials.
+ *
+ * @param newJwkSet the JWKSet to use as a source of credentials
+ *
+ */
+ public void setJWKSet(@Nullable final JWKSet newJwkSet) {
+ jwkSet = newJwkSet;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("JWKSetCriterion [JWKSet=");
+ builder.append("<contents not displayable>");
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ if (jwkSet != null) {
+ return jwkSet.hashCode();
+ }
+ return super.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null) {
+ return false;
+ }
+
+ if (obj instanceof JWKSetCriterion) {
+ return jwkSet.equals(((JWKSetCriterion) obj).jwkSet);
+ }
+
+ return false;
+ }
+
+}
\ No newline at end of file
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/JWTEncryptionConfigurationCriterion.java
similarity index 76%
copy from oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/DecryptionConfigurationCriterion.java
copy to oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/JWTEncryptionConfigurationCriterion.java
index 0fb0d22..f50f283 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/JWTEncryptionConfigurationCriterion.java
@@ -21,7 +21,7 @@ import java.util.List;
import javax.annotation.Nonnull;
-import net.shibboleth.oidc.security.DecryptionConfiguration;
+import net.shibboleth.oidc.security.JWTEncryptionConfiguration;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
@@ -30,20 +30,20 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.resolver.Criterion;
/**
- * Criterion which holds one or more instances of {@link DecryptionConfiguration}.
+ * Criterion which holds one or more instances of {@link JWTEncryptionConfigurationCriterion}.
*/
-public class DecryptionConfigurationCriterion implements Criterion {
+public class JWTEncryptionConfigurationCriterion implements Criterion {
/** The list of configuration instances. */
- @Nonnull @NonnullElements private final List<DecryptionConfiguration> configs;
+ @Nonnull @NonnullElements private final List<JWTEncryptionConfiguration> configs;
/**
* Constructor.
*
* @param configurations list of configuration instances
*/
- public DecryptionConfigurationCriterion(@Nonnull @NonnullElements @NotEmpty final
- List<DecryptionConfiguration> configurations) {
+ public JWTEncryptionConfigurationCriterion(@Nonnull @NonnullElements @NotEmpty final
+ List<JWTEncryptionConfiguration> configurations) {
configs = List.copyOf(Constraint.isNotNull(configurations, "List of configurations cannot be null"));
Constraint.isNotEmpty(configs, "At least one configuration is required");
@@ -54,19 +54,18 @@ public class DecryptionConfigurationCriterion implements Criterion {
*
* @param configurations varargs array of configuration instances
*/
- public DecryptionConfigurationCriterion(@Nonnull @NonnullElements @NotEmpty final
- DecryptionConfiguration... configurations) {
+ public JWTEncryptionConfigurationCriterion(@Nonnull @NonnullElements @NotEmpty final
+ JWTEncryptionConfiguration... configurations) {
configs = List.of(Constraint.isNotNull(configurations, "List of configurations cannot be null"));
Constraint.isNotEmpty(configs, "At least one configuration is required");
}
/**
* Get the list of configuration instances.
- *
* @return the list of configuration instances
*/
@Nonnull @NonnullElements @NotLive @Unmodifiable @NotEmpty
- public List<DecryptionConfiguration> getConfigurations() {
+ public List<JWTEncryptionConfiguration> getConfigurations() {
return configs;
}
@@ -74,7 +73,7 @@ public class DecryptionConfigurationCriterion implements Criterion {
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
- builder.append("DecryptionConfigurationCriterion [configs=");
+ builder.append("EncryptionConfigurationCriterion [configs=");
builder.append(configs);
builder.append("]");
return builder.toString();
@@ -97,11 +96,11 @@ public class DecryptionConfigurationCriterion implements Criterion {
return false;
}
- if (obj instanceof DecryptionConfigurationCriterion) {
- return configs.equals(((DecryptionConfigurationCriterion) obj).getConfigurations());
+ if (obj instanceof JWTEncryptionConfigurationCriterion) {
+ return configs.equals(((JWTEncryptionConfigurationCriterion) obj).getConfigurations());
}
return false;
}
-}
\ No newline at end of file
+}
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTDecryptionConfiguration.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTDecryptionConfiguration.java
index 7fce719..09abf1e 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTDecryptionConfiguration.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTDecryptionConfiguration.java
@@ -21,14 +21,14 @@ import javax.annotation.Nullable;
import org.opensaml.xmlsec.impl.BasicAlgorithmPolicyConfiguration;
-import net.shibboleth.oidc.security.DecryptionConfiguration;
+import net.shibboleth.oidc.security.JWTDecryptionConfiguration;
import net.shibboleth.oidc.security.credential.JOSEObjectCredentialResolver;
/**
- * Basic implementation of {@link DecryptionConfiguration} for handling JWEs.
+ * Basic implementation of {@link JWTDecryptionConfiguration} for handling JWEs.
*/
public class BasicJWTDecryptionConfiguration
- extends BasicAlgorithmPolicyConfiguration implements DecryptionConfiguration {
+ extends BasicAlgorithmPolicyConfiguration implements JWTDecryptionConfiguration {
/** The EncryptedKey's credential resolver. */
@Nullable private JOSEObjectCredentialResolver kekKeyCredentialResolver;
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
new file mode 100644
index 0000000..5b58806
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionConfiguration.java
@@ -0,0 +1,133 @@
+/*
+ * 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 javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.security.credential.Credential;
+import org.opensaml.xmlsec.impl.BasicAlgorithmPolicyConfiguration;
+
+import net.shibboleth.oidc.security.JWTEncryptionConfiguration;
+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;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+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;
+
+ /** 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;
+ }
+
+ /**
+ * Set the data encryption credentials to use.
+ *
+ * @param credentials the list of data encryption credentials
+ */
+ public void setDataEncryptionCredentials(@Nullable @NonnullElements final List<Credential> credentials) {
+ if (credentials == null) {
+ dataEncryptionCredentials = Collections.emptyList();
+ } else {
+ dataEncryptionCredentials = List.copyOf(credentials);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull @NonnullElements @Unmodifiable @NotLive public List<String> getDataEncryptionAlgorithms() {
+ return dataEncryptionAlgorithms;
+ }
+
+ /**
+ * Set the data encryption algorithms to use.
+ *
+ * @param algorithms the list of algorithms
+ */
+ public void setDataEncryptionAlgorithms(@Nullable @NonnullElements final List<String> algorithms) {
+ if (algorithms == null) {
+ dataEncryptionAlgorithms = Collections.emptyList();
+ } else {
+ dataEncryptionAlgorithms = List.copyOf(StringSupport.normalizeStringCollection(algorithms));
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull @NonnullElements @Unmodifiable @NotLive public List<Credential> getKeyTransportEncryptionCredentials() {
+ return keyTransportEncryptionCredentials;
+ }
+
+ /**
+ * Set the key transport encryption credentials to use.
+ *
+ * @param credentials the list of key transport encryption credentials
+ */
+ public void setKeyTransportEncryptionCredentials(@Nullable @NonnullElements final List<Credential> credentials) {
+ if (credentials == null) {
+ keyTransportEncryptionCredentials = Collections.emptyList();
+ } else {
+ keyTransportEncryptionCredentials = List.copyOf(credentials);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull @NonnullElements @Unmodifiable @NotLive public List<String> getKeyTransportEncryptionAlgorithms() {
+ return keyTransportEncryptionAlgorithms;
+ }
+
+ /**
+ * Set the key transport encryption algorithms to use.
+ *
+ * @param algorithms the list of algorithms
+ */
+ public void setKeyTransportEncryptionAlgorithms(@Nullable @NonnullElements final List<String> algorithms) {
+ if (algorithms == null) {
+ keyTransportEncryptionAlgorithms = Collections.emptyList();
+ } else {
+ keyTransportEncryptionAlgorithms = List.copyOf(StringSupport.normalizeStringCollection(algorithms));
+ }
+ }
+
+}
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/BasicJWTEncryptionParametersResolver.java
new file mode 100644
index 0000000..8f7dcbc
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolver.java
@@ -0,0 +1,670 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.CredentialSupport;
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.xmlsec.EncryptionParametersResolver;
+import org.opensaml.xmlsec.algorithm.AlgorithmRegistry;
+import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
+import org.opensaml.xmlsec.criterion.EncryptionOptionalCriterion;
+import org.opensaml.xmlsec.impl.AbstractSecurityParametersResolver;
+import org.opensaml.xmlsec.impl.AlgorithmRuntimeSupportedPredicate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jose.Algorithm;
+import com.nimbusds.jose.EncryptionMethod;
+import com.nimbusds.jose.JWEAlgorithm;
+
+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.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.
+ *
+ * <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>
+ * </ul>
+ */
+public class BasicJWTEncryptionParametersResolver extends AbstractSecurityParametersResolver<JWTEncryptionParameters>
+ implements JWTEncryptionParametersResolver {
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(BasicJWTEncryptionParametersResolver.class);
+
+ /** The AlgorithmRegistry used when processing algorithm URIs. */
+ @Nonnull private AlgorithmRegistry algorithmRegistry;
+
+ /** Constructor. */
+ public BasicJWTEncryptionParametersResolver() {
+ algorithmRegistry = AlgorithmSupport.getGlobalAlgorithmRegistry();
+ }
+
+ /**
+ * Get the {@link AlgorithmRegistry} instance used when resolving algorithm URIs. Defaults to
+ * the registry resolved via {@link AlgorithmSupport#getGlobalAlgorithmRegistry()}.
+ *
+ * @return the algorithm registry instance
+ */
+ @Nonnull public AlgorithmRegistry getAlgorithmRegistry() {
+ // Handle case where this resolver was ctored before the library was properly initialized.
+ if (algorithmRegistry == null) {
+ return AlgorithmSupport.getGlobalAlgorithmRegistry();
+ }
+ return algorithmRegistry;
+ }
+
+ /**
+ * Set the {@link AlgorithmRegistry} instance used when resolving algorithm URIs. Defaults to
+ * the registry resolved via {@link AlgorithmSupport#getGlobalAlgorithmRegistry()}.
+ *
+ * @param registry the new algorithm registry instance
+ */
+ public void setAlgorithmRegistry(@Nonnull final AlgorithmRegistry registry) {
+ algorithmRegistry = Constraint.isNotNull(registry, "AlgorithmRegistry was null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull public Iterable<JWTEncryptionParameters> resolve(@Nonnull final CriteriaSet criteria)
+ throws ResolverException {
+ final JWTEncryptionParameters params = resolveSingle(criteria);
+ if (params != null) {
+ return Collections.singletonList(params);
+ }
+ return Collections.emptyList();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable public JWTEncryptionParameters resolveSingle(@Nonnull final CriteriaSet criteria) throws ResolverException {
+ Constraint.isNotNull(criteria, "CriteriaSet was null");
+ Constraint.isNotNull(criteria.get(JWTEncryptionConfigurationCriterion.class),
+ "Resolver requires an instance of JWTEncryptionConfigurationCriterion");
+
+ final Predicate<String> includeExcludePredicate = getIncludeExcludePredicate(criteria);
+
+ final JWTEncryptionParameters params = new JWTEncryptionParameters();
+
+ resolveAndPopulateCredentialsAndAlgorithms(params, criteria, includeExcludePredicate);
+
+ boolean encryptionOptional = false;
+ final EncryptionOptionalCriterion encryptionOptionalCrit = criteria.get(EncryptionOptionalCriterion.class);
+ if (encryptionOptionalCrit != null) {
+ encryptionOptional = encryptionOptionalCrit.isEncryptionOptional();
+ }
+
+ if (validate(params, encryptionOptional)) {
+ logResult(params);
+ return params;
+ }
+ return null;
+
+ }
+
+ /**
+ * Log the resolved parameters.
+ *
+ * @param params the resolved param
+ */
+ protected void logResult(@Nonnull final JWTEncryptionParameters params) {
+ if (log.isDebugEnabled()) {
+ log.debug("Resolved EncryptionParameters:");
+
+ final Key keyTransportKey =
+ CredentialSupport.extractEncryptionKey(params.getKeyTransportEncryptionCredential());
+ if (keyTransportKey != null) {
+ log.debug("\tKey transport credential with key algorithm: {}", keyTransportKey.getAlgorithm());
+ } else {
+ log.debug("\tKey transport credential: null");
+ }
+
+ log.debug("\tKey transport algorithm URI: {}", params.getKeyTransportEncryptionAlgorithm());
+
+ final Key dataKey = CredentialSupport.extractEncryptionKey(params.getDataEncryptionCredential());
+ if (dataKey != null) {
+ log.debug("\tData encryption credential with key algorithm: {}", dataKey.getAlgorithm());
+ } else {
+ log.debug("\tData encryption credential: null");
+ }
+
+ log.debug("\tData encryption algorithm URI: {}", params.getDataEncryptionAlgorithm());
+ }
+ }
+
+// Checkstyle: CyclomaticComplexity OFF
+ /**
+ * Validate that the {@link JWTEncryptionParameters} instance has all the required properties populated.
+ *
+ * @param params the parameters instance to evaluate
+ * @param encryptionOptional whether to consider invalid parameters to be a problem
+ *
+ * @return true if parameters instance passes validation, false otherwise
+ */
+ protected boolean validate(@Nonnull final JWTEncryptionParameters params, final boolean encryptionOptional) {
+ if (params.getKeyTransportEncryptionCredential() == null
+ && params.getDataEncryptionCredential() == null) {
+ final String msg = "Validation failure: Failed to resolve an encryption key";
+ if (encryptionOptional) {
+ log.debug(msg);
+ } else {
+ log.warn(msg);
+ }
+ return false;
+ }
+ if (params.getKeyTransportEncryptionCredential() != null
+ && params.getKeyTransportEncryptionAlgorithm() == null) {
+ final String msg = "Validation failure: Unable to resolve key encryption algorithm URI for credential";
+ if (encryptionOptional) {
+ log.debug(msg);
+ } else {
+ log.warn(msg);
+ }
+ return false;
+ }
+ if (params.getDataEncryptionCredential() != null
+ && params.getDataEncryptionAlgorithm() == null) {
+ final String msg = "Validation failure: Unable to resolve data encryption algorithm URI for credential";
+ if (encryptionOptional) {
+ log.debug(msg);
+ } else {
+ log.warn(msg);
+ }
+ return false;
+ }
+ if (params.getKeyTransportEncryptionCredential() != null
+ && params.getDataEncryptionCredential() == null
+ && params.getDataEncryptionAlgorithm() == null) {
+ final String msg = "Validation failure: Unable to resolve a data encryption algorithm URI "
+ + "for auto-generation of data encryption key";
+ if (encryptionOptional) {
+ log.debug(msg);
+ } else {
+ log.warn(msg);
+ }
+ return false;
+ }
+
+ return true;
+ }
+// Checkstyle: CyclomaticComplexity ON
+
+ /**
+ * Get a predicate which implements the effective configured include/exclude policy.
+ *
+ * @param criteria the input criteria being evaluated
+ *
+ * @return a include/exclude predicate instance
+ */
+ @Nonnull protected Predicate<String> getIncludeExcludePredicate(@Nonnull final CriteriaSet criteria) {
+ return resolveIncludeExcludePredicate(criteria,
+ criteria.get(JWTEncryptionConfigurationCriterion.class).getConfigurations());
+ }
+
+ /**
+ * Resolve and populate the data encryption and key transport credentials and algorithm URIs.
+ *
+ * @param params the params instance being populated
+ * @param criteria the input criteria being evaluated
+ * @param includeExcludePredicate the include/exclude predicate with which to evaluate the
+ * candidate data encryption and key transport algorithm URIs
+ */
+ protected void resolveAndPopulateCredentialsAndAlgorithms(@Nonnull final JWTEncryptionParameters params,
+ @Nonnull final CriteriaSet criteria, @Nonnull final Predicate<String> includeExcludePredicate) {
+
+ if (!criteria.contains(JWTEncryptionConfigurationCriterion.class)) {
+ log.debug("No encryption configuration criterion, encryption parameters can not be resolved");
+ return;
+ }
+ // Pre-resolve these for efficiency
+ final List<Credential> keyTransportCredentials = getEffectiveKeyTransportCredentials(criteria);
+ final List<String> keyTransportAlgorithms =
+ getEffectiveKeyTransportAlgorithms(criteria, includeExcludePredicate);
+ log.trace("Resolved effective key transport algorithms: {}", keyTransportAlgorithms);
+
+ final List<String> dataEncryptionAlgorithms = getEffectiveDataEncryptionAlgorithms(criteria,
+ includeExcludePredicate);
+ final List<Credential> dataEncryptionCredentials = getEffectiveDataEncryptionCredentials(criteria);
+ log.trace("Resolved effective data encryption algorithms: {}", dataEncryptionAlgorithms);
+
+ resolveCredentialForSupportedAlgorithm(criteria,
+ convertStringAlgorithmURIsToJwkAlgorithms(keyTransportAlgorithms),
+ convertStringEncryptionMethodURIsToEncryptionMethods(dataEncryptionAlgorithms),
+ keyTransportCredentials, dataEncryptionCredentials, params);
+ }
+
+ /**
+ * Resolve a credential 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>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>
+ *
+ * @param criteria the set of criterion passed into additional source implementations
+ * @param keyTransportAlgorithms the set of supported key transport algorithms
+ * @param dataEncryptionAlgorithms the set of supported data encryption algorithms
+ * @param keyTransportCredentials the list of local key transport credentials that might
+ * represent a private/secret key from configuration
+ * @param dataEncryptionCredentials the list of local data encryption credentials
+ * @param params the encryption parameters to add the credential to
+ */
+ protected void resolveCredentialForSupportedAlgorithm(
+ @Nonnull final CriteriaSet criteria,
+ @Nonnull final List<JWEAlgorithm> keyTransportAlgorithms,
+ @Nonnull final List<EncryptionMethod> dataEncryptionAlgorithms,
+ @Nonnull final List<Credential> keyTransportCredentials,
+ @Nonnull final List<Credential> dataEncryptionCredentials,
+ @Nonnull final JWTEncryptionParameters params) {
+
+ for (final JWEAlgorithm algorithm : keyTransportAlgorithms) {
+
+ if (JWEAlgorithm.DIR == algorithm) {
+ // Is a direct encryption type, so need to populate the data enc. creds.
+ final Credential localCred =
+ findCredentialThatSupportsAlgorithm(dataEncryptionCredentials, algorithm);
+
+ if (localCred != null) {
+ final EncryptionMethod encryptionMethod =
+ findEncryptionMethodThatSupportsCredential(dataEncryptionAlgorithms, localCred);
+
+ 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;
+ }
+ }
+
+ } else {
+ // Supports key encryption, key wrapping, or key agreement.
+ final Credential localCred =
+ findCredentialThatSupportsAlgorithm(keyTransportCredentials, algorithm);
+
+ final EncryptionMethod encryptionMethod =
+ resolveEncryptionMethod(dataEncryptionAlgorithms);
+
+ if (localCred != null) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("Selected key '{}' for alg '{}' and enc '{}'",
+ CredentialConversionUtil.resolveKid(localCred),
+ algorithm.getName(), encryptionMethod.getName());
+ }
+ params.setKeyTransportEncryptionCredential(localCred);
+ params.setKeyTransportEncryptionAlgorithm(algorithm.getName());
+ params.setDataEncryptionAlgorithm(encryptionMethod.getName());
+ return;
+ } 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;
+ }
+ }
+
+
+ }
+ }
+ }
+
+ /**
+ * 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>
+ *
+ * <p>Implementation classes should override this method, the default behaviour does nothing.</p>
+ *
+ * @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
+ */
+ protected void resolveDataEncryptionCredentialForSupportedAlgorithmFromAdditionalSource(
+ @Nonnull final List<EncryptionMethod> dataEncryptionAlgorithms,
+ @Nonnull final CriteriaSet criteria, @Nonnull final JWTEncryptionParameters params) {
+ // Default method does nothing
+ }
+
+ /**
+ * 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.
+ *
+ * <p>Implementation classes should override this method, the default behaviour does nothing.</p>
+ *
+ * @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.
+ */
+ protected void resolveKeyTransportCredentialForSupportedAlgorithmFromAdditionalSource(
+ @Nonnull final JWEAlgorithm algorithm, @Nonnull final EncryptionMethod encryptionMethod,
+ @Nonnull final CriteriaSet criteria, @Nonnull final JWTEncryptionParameters params) {
+ // Default method does nothing
+ }
+
+ /**
+ * Determine if any of the given credentials match the given algorithm.
+ *
+ * <p>For a credential to be returned, it must meet the following criteria:</p>
+ * <ol>
+ * <li>The credential must be a {@link JWKCredential}</li>
+ * <li>The credential must have a {@link UsageType} of {@link UsageType#ENCRYPTION}
+ * or {@link UsageType#UNSPECIFIED}</li>
+ * <li>The credentials algorithm must match to one of the input key transport algorithms</li>
+ * <li>The credential's key must match the keylength required by that algorithm</li>
+ * </ol>
+ *
+ * <p>The first key that is compatible with the algorithm is returned. That is, if two or more keys
+ * support the same algorithm, the first key in the local credentials list
+ * will be returned. There is no guarantee which key that is.</p>
+ *
+ * @param credentials the credentials to match against the algorithm
+ * @param algorithm the algorithm to locate a credential for
+ *
+ * @return the first credential that matches the algorithm, {@literal null} otherwise.
+ */
+ @Nullable protected Credential findCredentialThatSupportsAlgorithm(@Nonnull final List<Credential> credentials,
+ @Nonnull final JWEAlgorithm algorithm) {
+ return credentials.stream()
+ .filter(Objects::nonNull)
+ .filter(JWKCredential.class::isInstance)
+ .filter(k -> UsageType.ENCRYPTION == k.getUsageType() || UsageType.UNSPECIFIED == k.getUsageType())
+ .map(JWKCredential.class::cast)
+ .filter(k -> algorithm.equals(k.getAlgorithm()))
+ .filter(k -> checkKeyAlgorithmAndLength(k, algorithm.getName()))
+ .findFirst().orElse(null);
+ }
+
+ /**
+ * Find an encryption method that is supported by the credential. That is, supports the algorithm and
+ * has the correct key length.
+ *
+ * @param dataEncryptionAlgorithms the data encryption algorithms
+ * @param credential the credential to test
+ *
+ * @return the first supported encryption method, {@literal null} otherwise.
+ */
+ @Nullable private EncryptionMethod findEncryptionMethodThatSupportsCredential(
+ @Nonnull final List<EncryptionMethod> dataEncryptionAlgorithms,
+ @Nonnull final Credential credential) {
+
+ final Key key = CredentialSupport.extractEncryptionKey(credential);
+
+ for (final EncryptionMethod method : dataEncryptionAlgorithms) {
+ if (AlgorithmSupport.checkKeyAlgorithmAndLength(key, getAlgorithmRegistry().get(method.getName()))) {
+ return method;
+ }
+ }
+ return null;
+
+ }
+
+
+ /**
+ * Return the first encryption method in the supported list, or null otherwise.
+ *
+ * @param dataEncryptionAlgorithms the supported data encryption method
+ *
+ * @return the first supported encryption method, or {@literal null}.
+ */
+ @Nullable protected EncryptionMethod resolveEncryptionMethod(
+ @Nonnull final List<EncryptionMethod> dataEncryptionAlgorithms) {
+ if (!dataEncryptionAlgorithms.isEmpty()) {
+ return dataEncryptionAlgorithms.get(0);
+ }
+ return null;
+ }
+
+ /**
+ * Convert the algorithms represented as strings, into Nimbus {@link Algorithm}s for later comparison.
+ * This will preserve the order of the original list ({@link List} is ordered).
+ *
+ * @param algos the algorithms to convert
+ *
+ * @return the converted algorithms
+ */
+ @Nonnull protected List<JWEAlgorithm> convertStringAlgorithmURIsToJwkAlgorithms(@Nonnull final List<String> algos) {
+ return algos.stream().filter(Objects::nonNull).map(JWEAlgorithm::parse).collect(Collectors.toList());
+ }
+
+ /**
+ * Convert the encryption methods represented as strings, into Nimbus {@link EncryptionMethod}s for later
+ * comparison. This will preserve the order of the original list ({@link List} is ordered).
+ *
+ * @param algos the encryption methods to convert
+ *
+ * @return the converted encryption methods
+ */
+ @Nonnull protected List<EncryptionMethod> convertStringEncryptionMethodURIsToEncryptionMethods(
+ @Nonnull final List<String> encMethods) {
+ 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
+ * by the runtime environment.
+ *
+ * @return the predicate
+ */
+ @Nonnull protected Predicate<String> getAlgorithmRuntimeSupportedPredicate() {
+ return new AlgorithmRuntimeSupportedPredicate(getAlgorithmRegistry());
+ }
+
+ /**
+ * Evaluate whether the specified credential is supported for use with the specified algorithm URI.
+ *
+ * @param credential the credential to evaluate
+ * @param algorithm the algorithm URI to evaluate
+ *
+ * @return true if credential may be used with the supplied algorithm URI, false otherwise
+ */
+ protected boolean credentialSupportsAlgorithm(@Nonnull final Credential credential,
+ @Nonnull @NotEmpty final String algorithm) {
+
+ return AlgorithmSupport.credentialSupportsAlgorithmForEncryption(credential,
+ getAlgorithmRegistry().get(algorithm));
+ }
+
+ /**
+ * Evaluate whether the specified credential is supported for use with the specified algorithm URI
+ * and the key length matches.
+ *
+ * @param credential the credential 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
+ */
+ protected boolean checkKeyAlgorithmAndLength(@Nonnull final Credential credential,
+ @Nonnull @NotEmpty final String algorithm) {
+
+ final Key key = CredentialSupport.extractEncryptionKey(credential);
+ if (key == null) {
+ return false;
+ }
+
+ return AlgorithmSupport.checkKeyAlgorithmAndLength(key, getAlgorithmRegistry().get(algorithm));
+ }
+
+ /**
+ * Evaluate whether the specified algorithm is a key encryption or key wrapping algorithm.
+ *
+ * @param algorithm the algorithm URI to evaluate
+ *
+ * @return true if is a key transport algorithm URI, false otherwise
+ */
+ protected boolean isKeyEncryptionAlgorithm(@Nonnull final String algorithm) {
+
+ return AlgorithmSupport.isKeyEncryptionAlgorithm(getAlgorithmRegistry().get(algorithm));
+ }
+
+ /**
+ * Evaluate whether the specified algorithm is a data encryption algorithm.
+ *
+ * @param algorithm the algorithm URI to evaluate
+ *
+ * @return true if is a key transport algorithm URI, false otherwise
+ */
+ protected boolean isDataEncryptionAlgorithm(final String algorithm) {
+
+ return AlgorithmSupport.isDataEncryptionAlgorithm(getAlgorithmRegistry().get(algorithm));
+ }
+
+}
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTSignatureSigningParametersResolver.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTSignatureSigningParametersResolver.java
new file mode 100644
index 0000000..41f39bd
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTSignatureSigningParametersResolver.java
@@ -0,0 +1,278 @@
+/*
+ * 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.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.security.credential.Credential;
+import org.opensaml.security.credential.CredentialSupport;
+import org.opensaml.xmlsec.SignatureSigningConfiguration;
+import org.opensaml.xmlsec.algorithm.AlgorithmRegistry;
+import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
+import org.opensaml.xmlsec.criterion.SignatureSigningConfigurationCriterion;
+import org.opensaml.xmlsec.impl.AbstractSecurityParametersResolver;
+import org.opensaml.xmlsec.impl.AlgorithmRuntimeSupportedPredicate;
+import org.opensaml.xmlsec.impl.BasicSignatureSigningParametersResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.oidc.security.JWTSignatureSigningParameters;
+import net.shibboleth.oidc.security.JWTSignatureSigningParametersResolver;
+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 {@link JWTSignatureSigningParametersResolver}.
+ *
+ * <p>
+ * The following {@link net.shibboleth.utilities.java.support.resolver.Criterion} inputs are supported:
+ * </p>
+ * <ul>
+ * <li>{@link SignatureSigningConfigurationCriterion} - required</li>
+ * </ul>
+ * <p>See also (for the SAML case) {@link BasicSignatureSigningParametersResolver}.</p>
+ */
+public class BasicJWTSignatureSigningParametersResolver
+ extends AbstractSecurityParametersResolver<JWTSignatureSigningParameters>
+ implements JWTSignatureSigningParametersResolver {
+
+ private final Logger log = LoggerFactory.getLogger(BasicJWTSignatureSigningParametersResolver.class);
+
+ /** The AlgorithmRegistry used when processing algorithm URIs. */
+ private AlgorithmRegistry algorithmRegistry;
+
+ /** Constructor. */
+ public BasicJWTSignatureSigningParametersResolver() {
+ algorithmRegistry = AlgorithmSupport.getGlobalAlgorithmRegistry();
+ }
+
+ /**
+ * Get the {@link AlgorithmRegistry} instance used when resolving algorithm URIs. Defaults to
+ * the registry obtained via {@link AlgorithmSupport#getGlobalAlgorithmRegistry()}.
+ *
+ * @return the algorithm registry instance
+ */
+ public AlgorithmRegistry getAlgorithmRegistry() {
+ // Handle case where this resolver was constructed before the library was properly initialized.
+ if (algorithmRegistry == null) {
+ return AlgorithmSupport.getGlobalAlgorithmRegistry();
+ }
+ return algorithmRegistry;
+ }
+
+ /**
+ * Set the {@link AlgorithmRegistry} instance used when resolving algorithm URIs. Defaults to
+ * the registry obtained via {@link AlgorithmSupport#getGlobalAlgorithmRegistry()}.
+ *
+ * @param registry the new algorithm registry instance
+ */
+ public void setAlgorithmRegistry(@Nonnull final AlgorithmRegistry registry) {
+ algorithmRegistry = Constraint.isNotNull(registry, "AlgorithmRegistry was null");
+ }
+
+ @Override
+ @Nonnull
+ public Iterable<JWTSignatureSigningParameters> resolve(@Nonnull final CriteriaSet criteria) throws ResolverException {
+ final JWTSignatureSigningParameters params = resolveSingle(criteria);
+ if (params != null) {
+ return Collections.singletonList(params);
+ }
+ return Collections.emptyList();
+ }
+
+ @Override
+ @Nullable
+ public JWTSignatureSigningParameters resolveSingle(@Nonnull final CriteriaSet criteria) throws ResolverException {
+ Constraint.isNotNull(criteria, "CriteriaSet was null");
+ Constraint.isNotNull(criteria.get(SignatureSigningConfigurationCriterion.class),
+ "Resolver requires an instance of SignatureSigningConfigurationCriterion");
+
+ final Predicate<String> includeExcludePredicate = getIncludeExcludePredicate(criteria);
+
+ final JWTSignatureSigningParameters params = new JWTSignatureSigningParameters();
+
+ resolveAndPopulateCredentialAndSignatureAlgorithm(params, criteria, includeExcludePredicate);
+
+ if (validate(params)) {
+ logResult(params);
+ return params;
+ }
+ return null;
+ }
+
+ /**
+ * Log the resolved parameters.
+ *
+ * @param params the resolved param
+ */
+ protected void logResult(@Nonnull final JWTSignatureSigningParameters params) {
+ if (log.isDebugEnabled()) {
+ log.debug("Resolved SignatureSigningParameters:");
+
+ final Key signingKey = CredentialSupport.extractSigningKey(params.getSigningCredential());
+ if (signingKey != null) {
+ log.debug("\tSigning credential with key algorithm: {}", signingKey.getAlgorithm());
+ } else {
+ log.debug("\tSigning credential: null");
+ }
+
+ log.debug("\tSignature algorithm URI: {}", params.getSignatureAlgorithm());
+ }
+ }
+
+ /**
+ * Validate that the {@link JWTSignatureSigningParameters} instance has all the required properties populated.
+ *
+ * @param params the parameters instance to evaluate
+ *
+ * @return true if parameters instance passes validation, false otherwise
+ */
+ protected boolean validate(@Nonnull final JWTSignatureSigningParameters params) {
+ if (params.getSigningCredential() == null) {
+ log.warn("Validation failure: Unable to resolve signing credential");
+ return false;
+ }
+ if (params.getSignatureAlgorithm() == null) {
+ log.warn("Validation failure: Unable to resolve signing algorithm URI");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get a predicate which implements the effective configured include/exclude policy.
+ *
+ * @param criteria the input criteria being evaluated
+ *
+ * @return include/exclude predicate instance
+ */
+ @Nonnull protected Predicate<String> getIncludeExcludePredicate(@Nonnull final CriteriaSet criteria) {
+ return resolveIncludeExcludePredicate(criteria,
+ criteria.get(SignatureSigningConfigurationCriterion.class).getConfigurations());
+ }
+
+ /**
+ * Resolve and populate the signing credential and signature method algorithm URI on the
+ * supplied parameters instance.
+ *
+ * @param params the parameters instance being populated
+ * @param criteria the input criteria being evaluated
+ * @param includeExcludePredicate the include/exclude predicate with which to evaluate the
+ * candidate signing method algorithm URIs
+ */
+ protected void resolveAndPopulateCredentialAndSignatureAlgorithm(@Nonnull final JWTSignatureSigningParameters params,
+ @Nonnull final CriteriaSet criteria, final Predicate<String> includeExcludePredicate) {
+
+ final List<Credential> credentials = getEffectiveSigningCredentials(criteria);
+ final List<String> algorithms = getEffectiveSignatureAlgorithms(criteria, includeExcludePredicate);
+ log.trace("Resolved effective signature algorithms: {}", algorithms);
+
+ for (final Credential credential : credentials) {
+ if (log.isTraceEnabled()) {
+ final Key key = CredentialSupport.extractSigningKey(credential);
+ log.trace("Evaluating credential of type: {}", key != null ? key.getAlgorithm() : "n/a");
+ }
+ for (final String algorithm : algorithms) {
+ log.trace("Evaluating credential against algorithm: {}", algorithm);
+ if (credentialSupportsAlgorithm(credential, algorithm)) {
+ log.trace("Credential passed eval against algorithm: {}", algorithm);
+ params.setSigningCredential(credential);
+ params.setSignatureAlgorithm(algorithm);
+ return;
+ }
+ log.trace("Credential failed eval against algorithm: {}", algorithm);
+ }
+ }
+
+ }
+
+ /**
+ * 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());
+ }
+
+ /**
+ * Evaluate whether the specified credential is supported for use with the specified algorithm URI.
+ *
+ * @param credential the credential to evaluate
+ * @param algorithm the algorithm URI to evaluate
+ * @return true if credential may be used with the supplied algorithm URI, false otherwise
+ */
+ protected boolean credentialSupportsAlgorithm(@Nonnull final Credential credential,
+ @Nonnull @NotEmpty final String algorithm) {
+
+ return AlgorithmSupport.credentialSupportsAlgorithmForSigning(credential,
+ getAlgorithmRegistry().get(algorithm));
+ }
+
+ /**
+ * Get the effective list of signing credentials to consider.
+ *
+ * @param criteria the input criteria being evaluated
+ * @return the list of credentials
+ */
+ @Nonnull protected List<Credential> getEffectiveSigningCredentials(@Nonnull final CriteriaSet criteria) {
+ final ArrayList<Credential> accumulator = new ArrayList<>();
+ for (final SignatureSigningConfiguration config : criteria.get(SignatureSigningConfigurationCriterion.class)
+ .getConfigurations()) {
+
+ accumulator.addAll(config.getSigningCredentials());
+
+ }
+ return accumulator;
+ }
+
+ /**
+ * Get the effective list of signature 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> getEffectiveSignatureAlgorithms(@Nonnull final CriteriaSet criteria,
+ @Nonnull final Predicate<String> includeExcludePredicate) {
+ final ArrayList<String> accumulator = new ArrayList<>();
+ for (final SignatureSigningConfiguration config : criteria.get(SignatureSigningConfigurationCriterion.class)
+ .getConfigurations()) {
+
+ config.getSignatureAlgorithms()
+ .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 6217bd0..148d894 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
@@ -10,7 +10,7 @@ import org.opensaml.xmlsec.impl.AbstractSecurityParametersResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import net.shibboleth.oidc.security.DecryptionConfiguration;
+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;
@@ -68,7 +68,7 @@ public class DefaultJWTDecryptionParametersResolver extends AbstractSecurityPara
@Nullable protected JOSEObjectCredentialResolver resolveKEKCredentialResolver(
@Nonnull final CriteriaSet criteria) {
- for (final DecryptionConfiguration config : criteria.get(DecryptionConfigurationCriterion.class)
+ for (final JWTDecryptionConfiguration config : criteria.get(DecryptionConfigurationCriterion.class)
.getConfigurations()) {
if (config.getKEKCredentialResolver() != null) {
return config.getKEKCredentialResolver();
@@ -87,7 +87,7 @@ public class DefaultJWTDecryptionParametersResolver extends AbstractSecurityPara
@Nullable protected JOSEObjectCredentialResolver resolveContentEncryptionKeyCredentialResolver(
@Nonnull final CriteriaSet criteria) {
- for (final DecryptionConfiguration config : criteria.get(DecryptionConfigurationCriterion.class)
+ for (final JWTDecryptionConfiguration config : criteria.get(DecryptionConfigurationCriterion.class)
.getConfigurations()) {
if (config.getContentEncryptionKeyCredentialResolver() != null) {
return config.getContentEncryptionKeyCredentialResolver();
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/PopulateJWTSignatureSigningParameters.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/PopulateJWTSignatureSigningParameters.java
index c74f997..55f7e96 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/PopulateJWTSignatureSigningParameters.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/PopulateJWTSignatureSigningParameters.java
@@ -13,17 +13,15 @@ import org.opensaml.messaging.context.navigate.ContextDataLookupFunction;
import org.opensaml.profile.action.AbstractHandlerDelegatingProfileAction;
import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
import org.opensaml.xmlsec.SecurityConfigurationSupport;
import org.opensaml.xmlsec.SignatureSigningConfiguration;
-import org.opensaml.xmlsec.SignatureSigningParametersResolver;
-import org.opensaml.xmlsec.context.SecurityParametersContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.shibboleth.oidc.metadata.context.OIDCMetadataContext;
import net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext;
+import net.shibboleth.oidc.security.JWTSignatureSigningParametersResolver;
import net.shibboleth.oidc.security.context.JWTSecurityParametersContext;
import net.shibboleth.utilities.java.support.annotation.ParameterName;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
@@ -55,7 +53,7 @@ public class PopulateJWTSignatureSigningParameters
private Function<ProfileRequestContext, OIDCProviderMetadataContext> oidcProviderMetadataContextLookupStrategy;
/** Resolver for parameters to store into context. */
- @NonnullAfterInit private SignatureSigningParametersResolver resolver;
+ @NonnullAfterInit private JWTSignatureSigningParametersResolver resolver;
/** Whether failure to resolve parameters should be raised as an error. */
private boolean noResultIsError;
@@ -147,7 +145,8 @@ public class PopulateJWTSignatureSigningParameters
*
* @param newResolver resolver to use
*/
- public void setSignatureSigningParametersResolver(@Nonnull final SignatureSigningParametersResolver newResolver) {
+ public void setSignatureSigningParametersResolver(
+ @Nonnull final JWTSignatureSigningParametersResolver newResolver) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
resolver = Constraint.isNotNull(newResolver, "SignatureSigningParametersResolver cannot be null");
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/PopulateJWTSignatureSigningParametersHandler.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/PopulateJWTSignatureSigningParametersHandler.java
index d09da47..707fc29 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/PopulateJWTSignatureSigningParametersHandler.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/PopulateJWTSignatureSigningParametersHandler.java
@@ -32,8 +32,6 @@ import org.opensaml.messaging.handler.MessageHandlerException;
import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.xmlsec.SecurityConfigurationSupport;
import org.opensaml.xmlsec.SignatureSigningConfiguration;
-import org.opensaml.xmlsec.SignatureSigningParameters;
-import org.opensaml.xmlsec.SignatureSigningParametersResolver;
import org.opensaml.xmlsec.context.SecurityParametersContext;
import org.opensaml.xmlsec.criterion.SignatureSigningConfigurationCriterion;
import org.slf4j.Logger;
@@ -43,6 +41,8 @@ import net.shibboleth.idp.profile.context.RelyingPartyContext;
import net.shibboleth.oidc.metadata.context.OIDCMetadataContext;
import net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext;
import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+import net.shibboleth.oidc.security.JWTSignatureSigningParameters;
+import net.shibboleth.oidc.security.JWTSignatureSigningParametersResolver;
import net.shibboleth.oidc.security.context.JWTSecurityParametersContext;
import net.shibboleth.oidc.security.criterion.ClientInformationCriterion;
import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
@@ -80,7 +80,7 @@ public class PopulateJWTSignatureSigningParametersHandler extends AbstractMessag
@Nonnull private Function<MessageContext, RelyingPartyContext> relyingPartyContextLookupStrategy;
/** Resolver for parameters to store into context. */
- @NonnullAfterInit private SignatureSigningParametersResolver resolver;
+ @NonnullAfterInit private JWTSignatureSigningParametersResolver resolver;
/** Whether failure to resolve parameters should be raised as an error. */
private boolean noResultIsError;
@@ -151,7 +151,8 @@ public class PopulateJWTSignatureSigningParametersHandler extends AbstractMessag
*
* @param newResolver resolver to use
*/
- public void setSignatureSigningParametersResolver(@Nonnull final SignatureSigningParametersResolver newResolver) {
+ public void setSignatureSigningParametersResolver(
+ @Nonnull final JWTSignatureSigningParametersResolver newResolver) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
resolver = Constraint.isNotNull(newResolver, "SignatureSigningParametersResolver cannot be null");
@@ -297,7 +298,7 @@ public class PopulateJWTSignatureSigningParametersHandler extends AbstractMessag
}
try {
- final SignatureSigningParameters params = resolver.resolveSingle(criteria);
+ final JWTSignatureSigningParameters params = resolver.resolveSingle(criteria);
if (params == null && noResultIsError) {
log.error("Failed to resolve SignatureSigningParameters");
throw new MessageHandlerException("Failed to resolve SignatureSigningParameters");
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolverTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolverTest.java
new file mode 100644
index 0000000..a0a5b15
--- /dev/null
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolverTest.java
@@ -0,0 +1,400 @@
+/*
+ * 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.assertNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.security.KeyException;
+import java.time.Duration;
+import java.util.List;
+
+import org.opensaml.core.config.InitializationException;
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.security.crypto.KeySupport;
+import org.opensaml.xmlsec.config.GlobalAlgorithmRegistryInitializer;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.jose.Algorithm;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.jwk.Curve;
+import com.nimbusds.jose.jwk.ECKey;
+import com.nimbusds.jose.jwk.KeyUse;
+import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.jose.jwk.gen.ECKeyGenerator;
+import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
+
+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.BasicExpiringJWKCredential;
+import net.shibboleth.oidc.security.credential.JWKCredential;
+import net.shibboleth.oidc.security.criterion.JWTEncryptionConfigurationCriterion;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+public class BasicJWTEncryptionParametersResolverTest {
+
+ /** 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 BasicJWTEncryptionConfiguration config;
+
+
+ @BeforeMethod
+ public void setUp() {
+ //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 BasicJWTEncryptionParametersResolver();
+ }
+
+ 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)));
+ return criteria;
+ }
+
+ @Test
+ public void testBasicRSA() throws Exception {
+ final CriteriaSet criteria = buildBasicCriteriaSet();
+ final RSAKey key = new RSAKeyGenerator(2048)
+ .algorithm(JWEAlgorithm.RSA_OAEP_256)
+ .keyUse(KeyUse.ENCRYPTION)
+ .keyID("mock-key")
+ .generate();
+ config.setKeyTransportEncryptionCredentials(List.of(createKeyEncryptionCredential(key)));
+ 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);
+ assertNotNull(param.getKeyTransportEncryptionCredential());
+ assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+
+ }
+
+ @Test
+ public void testBasicEC() throws Exception {
+ final CriteriaSet criteria = buildBasicCriteriaSet();
+ config.setKeyTransportEncryptionAlgorithms(
+ List.of(KeyManagementConstants.ALGO_ID_ALG_ECDH_ES));
+ final ECKey key = new ECKeyGenerator(Curve.P_256)
+ .keyUse(KeyUse.ENCRYPTION)
+ .algorithm(JWEAlgorithm.ECDH_ES)
+ .keyID("mock-key")
+ .generate();
+ config.setKeyTransportEncryptionCredentials(List.of(createKeyAgreementCredential(key)));
+ 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);
+ assertNotNull(param.getKeyTransportEncryptionCredential());
+ assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+
+ }
+
+ @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(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());
+
+ }
+
+ /* Dir is not supported by the runtime.*/
+ @Test(enabled = false)
+ public void testBasicDirectEncryption() throws Exception {
+ final CriteriaSet criteria = buildBasicCriteriaSet();
+ config.setKeyTransportEncryptionAlgorithms(
+ List.of(KeyManagementConstants.ALGO_ID_ALG_DIR));
+ config.setDataEncryptionCredentials(List.of(createSharedSecretCredential("mock-key",
+ SYMMETRIC_KEY, JWEAlgorithm.DIR)));
+
+ 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());
+
+ }
+
+ /* Dir is not supported by the runtime.*/
+ @Test(enabled = false)
+ public void testBasicDirectEncryption_WhereOtherAlgsSupported() 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(createSharedSecretCredential("mock-key",
+ SYMMETRIC_KEY, JWEAlgorithm.DIR)));
+
+ 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());
+ }
+
+ /* The key is 256bit and does not support the 128bit enc. algo.*/
+ @Test
+ public void testBasicDirectEncryption_NoSupportedDataEncryptionMethods() throws Exception {
+ final CriteriaSet criteria = buildBasicCriteriaSet();
+ config.setKeyTransportEncryptionAlgorithms(
+ List.of(KeyManagementConstants.ALGO_ID_ALG_DIR));
+ config.setDataEncryptionCredentials(List.of(createSharedSecretCredential("mock-key",
+ SYMMETRIC_KEY, JWEAlgorithm.DIR)));
+ config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256));
+
+ 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 RSAKey key = new RSAKeyGenerator(2048)
+ .algorithm(JWEAlgorithm.RSA_OAEP_256)
+ .keyUse(KeyUse.ENCRYPTION)
+ .keyID("mock-key")
+ .generate();
+ config.setKeyTransportEncryptionCredentials(List.of(createKeyEncryptionCredential(key)));
+ config.setDataEncryptionCredentials(List.of(createSharedSecretCredential("mock-key",
+ SYMMETRIC_KEY, JWEAlgorithm.DIR)));
+ config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512));
+
+ 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());
+ }
+
+ @Test
+ public void testBasicDirectEncryption_WhereOtherKeysExist_RSAFirst() 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_256)
+ .keyUse(KeyUse.ENCRYPTION)
+ .keyID("mock-key")
+ .generate();
+ config.setKeyTransportEncryptionCredentials(List.of(createKeyEncryptionCredential(key)));
+ config.setDataEncryptionCredentials(List.of(createSharedSecretCredential("mock-key",
+ SYMMETRIC_KEY, JWEAlgorithm.DIR)));
+ config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512));
+
+ 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_RSA_OAEP_256);
+ assertNotNull(param.getKeyTransportEncryptionCredential());
+ assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+ }
+
+ @Test
+ public void testBasicRSA_WrongAlgorithmInKey() 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(createKeyEncryptionCredential(key)));
+ final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
+
+ 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(createKeyEncryptionCredential(key),
+ createKeyEncryptionCredential(keyCorrect)));
+ 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);
+ assertNotNull(param.getKeyTransportEncryptionCredential());
+ assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+ assertTrue(param.getKeyTransportEncryptionCredential().getKeyNames().contains("mock-key-correct-type"));
+ }
+
+ @Test
+ public void testBasicRSAAndDir_NoSuitableKeys() 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(createKeyEncryptionCredential(key)));
+ config.setDataEncryptionCredentials(List.of(createSharedSecretCredential("mock-key",
+ SYMMETRIC_KEY, JWEAlgorithm.A128KW)));
+ final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
+
+ assertNull(param);
+ }
+
+ @Test
+ public void testBasicRSA_WrongAlgorithmInConfig() throws Exception {
+ final CriteriaSet criteria = buildBasicCriteriaSet();
+ config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP));
+ final RSAKey key = new RSAKeyGenerator(2048)
+ .algorithm(JWEAlgorithm.RSA_OAEP_256)
+ .keyUse(KeyUse.ENCRYPTION)
+ .keyID("mock-key")
+ .generate();
+ config.setKeyTransportEncryptionCredentials(List.of(createKeyEncryptionCredential(key)));
+ final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
+
+ assertNull(param);
+ }
+
+ /**
+ * Create a key encryption {@link JWKCredential} from the given RSA key.
+ *
+ * @param secret the RSAKey to convert to a {@link JWKCredential}.
+ *
+ * @return the credential
+ * @throws JOSEException on error
+ */
+ public JWKCredential createKeyEncryptionCredential(final RSAKey secret) throws JOSEException {
+ final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+ jwkCredential.setPrivateKey(secret.toPrivateKey());
+ jwkCredential.setPublicKey(secret.toPublicKey());
+ jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+ jwkCredential.setUsageType(UsageType.ENCRYPTION);
+
+ jwkCredential.setKid(secret.getKeyID());
+ jwkCredential.getKeyNames().add(secret.getKeyID());
+ jwkCredential.setAlgorithm(secret.getAlgorithm());
+ return jwkCredential;
+ }
+
+ /**
+ * Create a key agreement encryption {@link JWKCredential} from the given EC key.
+ *
+ * @param secret the ECKey to convert to a {@link JWKCredential}.
+ *
+ * @return the credential
+ * @throws JOSEException on error
+ */
+ public JWKCredential createKeyAgreementCredential(final ECKey secret) throws JOSEException {
+ final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+ jwkCredential.setPrivateKey(secret.toPrivateKey());
+ jwkCredential.setPublicKey(secret.toPublicKey());
+ jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+ jwkCredential.setUsageType(UsageType.ENCRYPTION);
+
+ jwkCredential.setKid(secret.getKeyID());
+ jwkCredential.getKeyNames().add(secret.getKeyID());
+ jwkCredential.setAlgorithm(secret.getAlgorithm());
+ return jwkCredential;
+ }
+
+ /**
+ * Create a simple symmetric key client credential from from the given shared secret.
+ *
+ * @param kid the key ID
+ * @param secret the secret to convert to a {@link JWKCredential}.
+ * @param algorithm the JWA algorithm to set on the credential.
+ *
+ * @return the credential
+ * @throws KeyException on error creating the key
+ */
+ public JWKCredential createSharedSecretCredential(final String kid, final String secret,
+ final Algorithm algorithm)
+ throws KeyException {
+ final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+ jwkCredential.setSecretKey(KeySupport.decodeSecretKey(JWSAssemblyUtils.getSecretBytes(secret), "AES"));
+ jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+ jwkCredential.setUsageType(UsageType.UNSPECIFIED);
+ jwkCredential.setKid(kid);
+ jwkCredential.setAlgorithm(algorithm);
+ jwkCredential.getKeyNames().add("mockKey");
+ return jwkCredential;
+ }
+
+}
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ExplicitKeySignedJWTTrustEngineTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ExplicitKeySignedJWTTrustEngineTest.java
index 1bd1791..36e7cb2 100644
--- a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ExplicitKeySignedJWTTrustEngineTest.java
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ExplicitKeySignedJWTTrustEngineTest.java
@@ -98,7 +98,8 @@ public class ExplicitKeySignedJWTTrustEngineTest {
assertTrue(valid);
}
- @Test
+ /* JKU resolution not currently supported by the joseObjectCredResolver.*/
+ @Test(enabled = false)
public void testSuccess_WithInlineJKU() throws JOSEException, SecurityException, URISyntaxException {
// Create a new resolver which does not resolve any creds, the JKU ones need to be resolved.
diff --git a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/OIDCSecurityConfiguration.java b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/OIDCSecurityConfiguration.java
index 45d1c2b..24b72b8 100644
--- a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/OIDCSecurityConfiguration.java
+++ b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/OIDCSecurityConfiguration.java
@@ -25,6 +25,7 @@ import org.opensaml.xmlsec.SignatureSigningConfiguration;
import com.nimbusds.jwt.SignedJWT;
import net.shibboleth.idp.profile.config.SecurityConfiguration;
+import net.shibboleth.oidc.security.JWTEncryptionConfiguration;
/**
* Class extends SecurityConfiguration to support separate configuration for request object decryption and signature
@@ -34,10 +35,16 @@ import net.shibboleth.idp.profile.config.SecurityConfiguration;
public class OIDCSecurityConfiguration extends SecurityConfiguration {
/** Configuration used when decrypting request object information. */
+ //TODO Used by OP only? but should be decrypt config
@Nullable
private EncryptionConfiguration requestObjectDecryptConfig;
+
+ /** Configuration used when encrypting request object information. */
+ @Nullable
+ private JWTEncryptionConfiguration requestObjectEncryptionConfig;
/** Configuration used when validating request object information. */
+ //TODO JWT version of these?
@Nullable
private SignatureSigningConfiguration requestObjectSignatureValidationConfig;
@@ -55,11 +62,11 @@ public class OIDCSecurityConfiguration extends SecurityConfiguration {
/** Configuration used when decrypting id_tokens. */
@Nullable
- private net.shibboleth.oidc.security.DecryptionConfiguration idTokenJwtDecryptConfig;
+ private net.shibboleth.oidc.security.JWTDecryptionConfiguration idTokenJwtDecryptConfig;
/** Configuration used when decrypting UserInfo tokens. */
@Nullable
- private net.shibboleth.oidc.security.DecryptionConfiguration userInfoJwtDecryptConfig;
+ private net.shibboleth.oidc.security.JWTDecryptionConfiguration userInfoJwtDecryptConfig;
/** Configuration used when validating UserInfo JWT signatures. */
@Nullable
@@ -71,7 +78,7 @@ public class OIDCSecurityConfiguration extends SecurityConfiguration {
* @param config configuration used when decrypting id_tokens, or null
*/
public void setIdTokenJwtDecryptionConfiguration(@Nullable final
- net.shibboleth.oidc.security.DecryptionConfiguration config) {
+ net.shibboleth.oidc.security.JWTDecryptionConfiguration config) {
idTokenJwtDecryptConfig = config;
}
@@ -80,7 +87,7 @@ public class OIDCSecurityConfiguration extends SecurityConfiguration {
*
* @return configuration used when decrypting id_tokens, or null
*/
- @Nullable public net.shibboleth.oidc.security.DecryptionConfiguration getIdtokenJwtDecryptionConfiguration() {
+ @Nullable public net.shibboleth.oidc.security.JWTDecryptionConfiguration getIdtokenJwtDecryptionConfiguration() {
return idTokenJwtDecryptConfig;
}
@@ -90,16 +97,34 @@ public class OIDCSecurityConfiguration extends SecurityConfiguration {
* @param config configuration used when decrypting UserInfo JWTs, or null
*/
public void setUserInfoJwtDecryptionConfiguration(@Nullable final
- net.shibboleth.oidc.security.DecryptionConfiguration config) {
+ net.shibboleth.oidc.security.JWTDecryptionConfiguration config) {
userInfoJwtDecryptConfig = config;
}
+ /**
+ * Get the configuration used when encrypting RequestObject JWTs.
+ *
+ * @return configuration used when decrypting id_tokens, or null
+ */
+ @Nullable public JWTEncryptionConfiguration getRequestObjectEncryptionConfig() {
+ return requestObjectEncryptionConfig;
+ }
+
+ /**
+ * Set the configuration used to encrypt the RequestObject JWT.
+ *
+ * @param config configuration used when encrypting RequestObject JWTs, or null
+ */
+ public void setRequestObjectEncryptionConfig(@Nullable final JWTEncryptionConfiguration config) {
+ requestObjectEncryptionConfig = config;
+ }
+
/**
* Get the configuration used when decrypting UserInfo JWTs.
*
* @return configuration used when decrypting UserInfo JWTs, or null
*/
- @Nullable public net.shibboleth.oidc.security.DecryptionConfiguration getUserInfoJwtDecryptionConfiguration() {
+ @Nullable public net.shibboleth.oidc.security.JWTDecryptionConfiguration getUserInfoJwtDecryptionConfiguration() {
return userInfoJwtDecryptConfig;
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list