[java-opensaml] 18/19: Add key agreement support to EncryptionParametersResolver impls
Brent Putman
putmanb at georgetown.edu
Mon Mar 1 05:56:55 UTC 2021
This is an automated email from the git hooks/post-receive script.
putmanb pushed a commit to branch dev/OSJ-82
in repository java-opensaml.
View the commit online:
http://git.shibboleth.net/view/?p=java-opensaml.git;a=commit;h=4dc579bb622533dfefe0390666bbbdc670e6519e
commit 4dc579bb622533dfefe0390666bbbdc670e6519e
Author: Brent Putman <putmanb at georgetown.edu>
AuthorDate: Fri Feb 19 01:46:13 2021 -0500
Add key agreement support to EncryptionParametersResolver impls
---
...etadataKeyAgreementEncryptionConfiguration.java | 77 +++++
.../org/opensaml/saml/security/package-info.java | 22 ++
.../SAMLMetadataEncryptionParametersResolver.java | 208 +++++++++++-
.../saml/common/testing/SAMLTestSupport.java | 2 +
...MLMetadataEncryptionParametersResolverTest.java | 363 ++++++++++++++++++++-
.../src/test/resources/logback-test.xml | 8 +
.../opensaml/xmlsec/EncryptionConfiguration.java | 10 +
.../xmlsec/agreement/KeyAgreementSupport.java | 49 +++
.../xmlsec/algorithm/AlgorithmSupport.java | 34 ++
.../KeyAgreementEncryptionConfiguration.java | 85 +++++
.../xmlsec/algorithm/AlgorithmSupportTest.java | 31 ++
.../DefaultSecurityConfigurationBootstrap.java | 37 ++-
.../xmlsec/impl/BasicEncryptionConfiguration.java | 24 ++
.../impl/BasicEncryptionParametersResolver.java | 162 ++++++++-
.../BasicSignatureSigningParametersResolver.java | 3 +-
.../BasicEncryptionParametersResolverTest.java | 200 +++++++++++-
16 files changed, 1294 insertions(+), 21 deletions(-)
diff --git a/opensaml-saml-api/src/main/java/org/opensaml/saml/security/SAMLMetadataKeyAgreementEncryptionConfiguration.java b/opensaml-saml-api/src/main/java/org/opensaml/saml/security/SAMLMetadataKeyAgreementEncryptionConfiguration.java
new file mode 100644
index 000000000..9bd85f01b
--- /dev/null
+++ b/opensaml-saml-api/src/main/java/org/opensaml/saml/security/SAMLMetadataKeyAgreementEncryptionConfiguration.java
@@ -0,0 +1,77 @@
+/*
+ * 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 org.opensaml.saml.security;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.saml.saml2.metadata.EncryptionMethod;
+import org.opensaml.saml.saml2.metadata.KeyDescriptor;
+import org.opensaml.xmlsec.encryption.support.KeyAgreementEncryptionConfiguration;
+
+/**
+ * A specialization of {@link KeyAgreementEncryptionConfiguration} that can hold configuration
+ * specific to the user of SAML metadata.
+ */
+public class SAMLMetadataKeyAgreementEncryptionConfiguration extends KeyAgreementEncryptionConfiguration {
+
+ /** Options for whether to use symmetric key wrap with credentials from SAML metadata. */
+ public enum KeyWrap {
+
+ /** Always use key wrap for metadata credentials. */
+ Always,
+
+ /** Never use key wrap for metadata credentials. */
+ Never,
+
+ /** Use key wrap if no indication is given via {@link EncryptionMethod} elements within
+ * the associated {@link KeyDescriptor} element. See also {@link #Default}. */
+ IfNotIndicated,
+
+ /** Default behavior, which is to enable key wrap or not based on the presence or absence respectively of
+ * {@link EncryptionMethod} elements in the associated {@link KeyDescriptor} containing
+ * symmetric key wrap algorithms.
+ * The presence of any symmetric key wrap algorithms (after runtime support and include/exclude filtering)
+ * will enable key wrap. Otherwise, key wrap will be disabled.
+ */
+ Default;
+ }
+
+ /** Option which determines whether symmetric key wrap is to be used with metadata credentials. */
+ private KeyWrap metadataUseKeyWrap;
+
+ /**
+ * Get the option which determines whether symmetric key wrap is to be used with metadata credentials.
+ *
+ * @return the configured optiona value, or null if not explicitly configured
+ */
+ @Nullable public KeyWrap getMetadataUseKeyWrap() {
+ return metadataUseKeyWrap;
+ }
+
+ /**
+ * Set the option which determines whether symmetric key wrap is to be used with metadata credentials.
+ *
+ * @param option the new option value
+ */
+ public void setMetadataUseKeyWrap(@Nullable final KeyWrap option) {
+ metadataUseKeyWrap = option;
+ }
+
+}
+
+
diff --git a/opensaml-saml-api/src/main/java/org/opensaml/saml/security/package-info.java b/opensaml-saml-api/src/main/java/org/opensaml/saml/security/package-info.java
new file mode 100644
index 000000000..ffecede7d
--- /dev/null
+++ b/opensaml-saml-api/src/main/java/org/opensaml/saml/security/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+/**
+ * Classes related to general security components within a SAML system.
+ */
+
+package org.opensaml.saml.security;
\ No newline at end of file
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/security/impl/SAMLMetadataEncryptionParametersResolver.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/security/impl/SAMLMetadataEncryptionParametersResolver.java
index 4fc503bb6..5f71be2e1 100644
--- a/opensaml-saml-impl/src/main/java/org/opensaml/saml/security/impl/SAMLMetadataEncryptionParametersResolver.java
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/security/impl/SAMLMetadataEncryptionParametersResolver.java
@@ -18,8 +18,12 @@
package org.opensaml.saml.security.impl;
import java.security.Key;
+import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -28,23 +32,31 @@ import net.shibboleth.utilities.java.support.annotation.ParameterName;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.collection.Pair;
import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.logic.PredicateSupport;
import net.shibboleth.utilities.java.support.primitive.StringSupport;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
import net.shibboleth.utilities.java.support.resolver.ResolverException;
+import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.saml.saml2.metadata.EncryptionMethod;
+import org.opensaml.saml.security.SAMLMetadataKeyAgreementEncryptionConfiguration;
+import org.opensaml.saml.security.SAMLMetadataKeyAgreementEncryptionConfiguration.KeyWrap;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.credential.CredentialSupport;
import org.opensaml.security.credential.UsageType;
import org.opensaml.security.criteria.UsageCriterion;
import org.opensaml.security.crypto.KeySupport;
+import org.opensaml.xmlsec.EncryptionConfiguration;
import org.opensaml.xmlsec.EncryptionParameters;
import org.opensaml.xmlsec.KeyTransportAlgorithmPredicate;
+import org.opensaml.xmlsec.agreement.KeyAgreementSupport;
import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
+import org.opensaml.xmlsec.criterion.EncryptionConfigurationCriterion;
import org.opensaml.xmlsec.encryption.MGF;
import org.opensaml.xmlsec.encryption.OAEPparams;
import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
+import org.opensaml.xmlsec.encryption.support.KeyAgreementEncryptionConfiguration;
import org.opensaml.xmlsec.encryption.support.RSAOAEPParameters;
import org.opensaml.xmlsec.impl.BasicEncryptionParametersResolver;
import org.opensaml.xmlsec.signature.DigestMethod;
@@ -82,6 +94,9 @@ public class SAMLMetadataEncryptionParametersResolver extends BasicEncryptionPar
*/
private boolean mergeMetadataRSAOAEPParametersWithConfig;
+ /** Default for usage of key wrapping with key agreement if not otherwise configured. */
+ @Nonnull private KeyWrap defaultKeyAgreementUseKeyWrap = KeyWrap.Default;
+
/**
* Constructor.
*
@@ -117,6 +132,36 @@ public class SAMLMetadataEncryptionParametersResolver extends BasicEncryptionPar
public void setMergeMetadataRSAOAEPParametersWithConfig(final boolean flag) {
mergeMetadataRSAOAEPParametersWithConfig = flag;
}
+
+ /**
+ * Get the default for usage of key wrapping with key agreement if not otherwise configured.
+ *
+ * <p>
+ * The default is: {@link KeyWrap#Default}.
+ * </p>
+ *
+ * @return the default value
+ */
+ @Nonnull public KeyWrap getDefaultKeyAgreemenUseKeyWrap() {
+ return defaultKeyAgreementUseKeyWrap;
+ }
+
+ /**
+ * Set the default for usage of key wrapping with key agreement if not otherwise configured.
+ *
+ * <p>
+ * The default is: {@link KeyWrap#Default}.
+ * </p>
+ *
+ * @param keyWrap the value to set; null implies {@link KeyWrap#Default}
+ */
+ public void setDefaultKeyAgreementUseKeyWrap(@Nullable final KeyWrap keyWrap) {
+ if (keyWrap == null) {
+ defaultKeyAgreementUseKeyWrap = KeyWrap.Default;
+ } else {
+ defaultKeyAgreementUseKeyWrap = keyWrap;
+ }
+ }
/**
* Get the metadata credential resolver instance to use to resolve encryption credentials.
@@ -138,36 +183,41 @@ public class SAMLMetadataEncryptionParametersResolver extends BasicEncryptionPar
mdCredResolverCriteria.addAll(criteria);
mdCredResolverCriteria.add(new UsageCriterion(UsageType.ENCRYPTION), true);
- // Note: Here we assume that we will only ever resolve a key transport credential from metadata.
- // Even if it's a symmetric key credential (via a key agreement protocol, or resolved from a KeyName, etc),
- // it ought to be used for symmetric key wrap, not direct data encryption.
+ // Note: Here we primarily assume that we will resolve a key transport credential from metadata.
+ // Even if it's a symmetric key credential (e.g. resolved from a KeyName, etc),
+ // it will be used for symmetric key wrap, not direct data encryption.
+ // The exception is key agreement (e.g. ECDH), which is handled as a special case and may be
+ // either, determined by both metadata and local configuration.
try {
- for (final Credential keyTransportCredential :
- getMetadataCredentialResolver().resolve(mdCredResolverCriteria)) {
+ for (final Credential credential : getMetadataCredentialResolver().resolve(mdCredResolverCriteria)) {
if (log.isTraceEnabled()) {
- final Key key = CredentialSupport.extractEncryptionKey(keyTransportCredential);
- log.trace("Evaluating key transport encryption credential from SAML metadata of type: {}",
+ final Key key = CredentialSupport.extractEncryptionKey(credential);
+ log.trace("Evaluating candidate encryption credential from SAML metadata of type: {}",
key != null ? key.getAlgorithm() : "n/a");
}
+ if (checkAndProcessKeyAgreement(params, criteria, whitelistBlacklistPredicate, credential)) {
+ return;
+ }
+
final SAMLMDCredentialContext metadataCredContext =
- keyTransportCredential.getCredentialContextSet().get(SAMLMDCredentialContext.class);
+ credential.getCredentialContextSet().get(SAMLMDCredentialContext.class);
final Pair<String,EncryptionMethod> dataEncryptionAlgorithmAndMethod = resolveDataEncryptionAlgorithm(
criteria, whitelistBlacklistPredicate, metadataCredContext);
final Pair<String,EncryptionMethod> keyTransportAlgorithmAndMethod = resolveKeyTransportAlgorithm(
- keyTransportCredential, criteria, whitelistBlacklistPredicate,
+ credential, criteria, whitelistBlacklistPredicate,
dataEncryptionAlgorithmAndMethod.getFirst(), metadataCredContext);
if (keyTransportAlgorithmAndMethod.getFirst() == null) {
log.debug("Unable to resolve key transport algorithm for credential with key type '{}', "
+ "considering other credentials",
- CredentialSupport.extractEncryptionKey(keyTransportCredential).getAlgorithm());
+ CredentialSupport.extractEncryptionKey(credential).getAlgorithm());
continue;
}
- params.setKeyTransportEncryptionCredential(keyTransportCredential);
+ params.setKeyTransportEncryptionCredential(credential);
params.setKeyTransportEncryptionAlgorithm(keyTransportAlgorithmAndMethod.getFirst());
params.setDataEncryptionAlgorithm(dataEncryptionAlgorithmAndMethod.getFirst());
@@ -187,7 +237,141 @@ public class SAMLMetadataEncryptionParametersResolver extends BasicEncryptionPar
super.resolveAndPopulateCredentialsAndAlgorithms(params, criteria, whitelistBlacklistPredicate);
}
-
+
+ /**
+ *
+ * Check for a credential type that implies a key agreement operation, and process if so indicated.
+ *
+ * @param params the params instance being populated
+ * @param criteria the input criteria being evaluated
+ * @param whitelistBlacklistPredicate the whitelist/blacklist predicate
+ * @param credential the credential being evaluated
+ *
+ * @return true if all required parameters were supplied, key agreement was successfully performed,
+ * and the {@link EncryptionParameters} instance's credential and algorithms properties are fully populated,
+ * otherwise false
+ */
+ protected boolean checkAndProcessKeyAgreement(@Nonnull final EncryptionParameters params,
+ @Nonnull final CriteriaSet criteria, @Nonnull final Predicate<String> whitelistBlacklistPredicate,
+ @Nonnull final Credential credential) {
+
+ if (!KeyAgreementSupport.supportsKeyAgreement(credential) ) {
+ log.trace("Specified Credential does not support key agreement");
+ return false;
+ }
+
+ final SAMLMetadataKeyAgreementEncryptionConfiguration config =
+ getEffectiveKeyAgreementConfiguration(criteria, credential);
+ if (config == null) {
+ log.warn("Unable to get effective KeyAgreementEncryptionConfiguration for credential with key type: {}",
+ credential.getPublicKey().getAlgorithm());
+ return false;
+ }
+
+ final List<String> criteriaKeyTransportAlgorithms = getEffectiveKeyTransportAlgorithms(criteria,
+ whitelistBlacklistPredicate);
+
+ final List<String> criteriaDataEncryptionAlgorithms = getEffectiveDataEncryptionAlgorithms(criteria,
+ whitelistBlacklistPredicate);
+
+ final SAMLMDCredentialContext metadataCredContext =
+ credential.getCredentialContextSet().get(SAMLMDCredentialContext.class);
+
+ List<String> metadataKeyWrapAlgorithms = Collections.emptyList();
+ List<String> metadataDataEncryptionAlgorithms = Collections.emptyList();
+ if (metadataCredContext != null) {
+ final List<String> metadataAlgorithms = metadataCredContext.getEncryptionMethods().stream()
+ .map(EncryptionMethod::getAlgorithm)
+ .filter(Objects::nonNull)
+ .filter(PredicateSupport.and(getAlgorithmRuntimeSupportedPredicate(), whitelistBlacklistPredicate))
+ .collect(Collectors.toList());
+
+ metadataKeyWrapAlgorithms = metadataAlgorithms.stream()
+ .filter(AlgorithmSupport::isSymmetricKeyWrap)
+ .collect(Collectors.toList());
+
+ metadataDataEncryptionAlgorithms = metadataAlgorithms.stream()
+ .filter(AlgorithmSupport::isBlockEncryption)
+ .collect(Collectors.toList());
+ }
+
+ log.debug("Evaling useKeyWrap: # key wrap algos '{}', # direct data algos '{}', config '{}'",
+ metadataKeyWrapAlgorithms.size(), metadataDataEncryptionAlgorithms.size(),
+ config.getMetadataUseKeyWrap());
+
+ boolean useKeyWrap = false;
+ if (KeyWrap.Never == config.getMetadataUseKeyWrap()) {
+ useKeyWrap = false;
+ } else if (KeyWrap.Always == config.getMetadataUseKeyWrap() || !metadataKeyWrapAlgorithms.isEmpty()) {
+ useKeyWrap = true;
+ } else {
+ useKeyWrap = metadataDataEncryptionAlgorithms.isEmpty()
+ && KeyWrap.IfNotIndicated == config.getMetadataUseKeyWrap();
+ }
+
+ return checkAndProcessKeyAgreement(params, criteria, credential,
+ concatLists(metadataDataEncryptionAlgorithms, criteriaDataEncryptionAlgorithms),
+ useKeyWrap ? concatLists(metadataKeyWrapAlgorithms, criteriaKeyTransportAlgorithms)
+ : Collections.emptyList());
+ }
+
+ /**
+ * Get the effective {@link SAMLMetadataKeyAgreementEncryptionConfiguration} to use with the specified credential.
+ *
+ * @param criteria the criteria
+ * @param credential the credential to evaluate
+ * @return the key agreement configuration for the credential, or null if could not be resolved
+ */
+ @Nullable protected SAMLMetadataKeyAgreementEncryptionConfiguration getEffectiveKeyAgreementConfiguration(
+ @Nonnull final CriteriaSet criteria, @Nonnull final Credential credential) {
+
+ final KeyAgreementEncryptionConfiguration baseConfig =
+ super.getEffectiveKeyAgreementConfiguration(criteria, credential);
+ if (baseConfig == null) {
+ return null;
+ }
+
+ final SAMLMetadataKeyAgreementEncryptionConfiguration config =
+ new SAMLMetadataKeyAgreementEncryptionConfiguration();
+
+ config.setAlgorithm(baseConfig.getAlgorithm());
+ config.setParameters(baseConfig.getParameters());
+
+ final String keyType = credential.getPublicKey().getAlgorithm();
+
+ final List<EncryptionConfiguration> encConfigs = criteria.get(EncryptionConfigurationCriterion.class)
+ .getConfigurations();
+
+ config.setMetadataUseKeyWrap(
+ encConfigs.stream()
+ .map(c -> c.getKeyAgreementConfigurations().get(keyType))
+ .filter(Objects::nonNull)
+ .filter(SAMLMetadataKeyAgreementEncryptionConfiguration.class::isInstance)
+ .map(SAMLMetadataKeyAgreementEncryptionConfiguration.class::cast)
+ .map(SAMLMetadataKeyAgreementEncryptionConfiguration::getMetadataUseKeyWrap)
+ .filter(Objects::nonNull)
+ .findFirst().orElse(getDefaultKeyAgreemenUseKeyWrap())
+ );
+
+ return config;
+
+ }
+
+ /**
+ * Concatenate multiple lists into one list.
+ *
+ * @param lists the lists to process
+ *
+ * @return the concatenation of the supplied lists
+ */
+ @SafeVarargs
+ private List<String> concatLists(@Nonnull final List<String> ... lists) {
+ return Stream.of(lists)
+ .filter(Objects::nonNull)
+ .flatMap(x -> x.stream())
+ .collect(Collectors.toList());
+ }
+
/**
* Resolve and populate an instance of {@link RSAOAEPParameters}, if appropriate for the selected
* key transport encryption algorithm.
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/common/testing/SAMLTestSupport.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/common/testing/SAMLTestSupport.java
index 5364d877e..255209dea 100644
--- a/opensaml-saml-impl/src/test/java/org/opensaml/saml/common/testing/SAMLTestSupport.java
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/common/testing/SAMLTestSupport.java
@@ -24,6 +24,7 @@ import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
import org.opensaml.xmlsec.keyinfo.impl.BasicProviderKeyInfoCredentialResolver;
import org.opensaml.xmlsec.keyinfo.impl.KeyInfoProvider;
import org.opensaml.xmlsec.keyinfo.impl.provider.DSAKeyValueProvider;
+import org.opensaml.xmlsec.keyinfo.impl.provider.ECKeyValueProvider;
import org.opensaml.xmlsec.keyinfo.impl.provider.InlineX509DataProvider;
import org.opensaml.xmlsec.keyinfo.impl.provider.RSAKeyValueProvider;
@@ -45,6 +46,7 @@ public final class SAMLTestSupport {
List<KeyInfoProvider> providers = new ArrayList<>();
providers.add( new RSAKeyValueProvider() );
providers.add( new DSAKeyValueProvider() );
+ providers.add( new ECKeyValueProvider() );
providers.add( new InlineX509DataProvider() );
return new BasicProviderKeyInfoCredentialResolver(providers);
}
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/security/impl/SAMLMetadataEncryptionParametersResolverTest.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/security/impl/SAMLMetadataEncryptionParametersResolverTest.java
index a4b0e3811..654435bcf 100644
--- a/opensaml-saml-impl/src/test/java/org/opensaml/saml/security/impl/SAMLMetadataEncryptionParametersResolverTest.java
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/security/impl/SAMLMetadataEncryptionParametersResolverTest.java
@@ -17,18 +17,21 @@
package org.opensaml.saml.security.impl;
+import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
+import java.security.spec.ECGenParameterSpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import javax.annotation.Nullable;
@@ -48,6 +51,8 @@ import org.opensaml.saml.saml2.metadata.EntityDescriptor;
import org.opensaml.saml.saml2.metadata.KeyDescriptor;
import org.opensaml.saml.saml2.metadata.RoleDescriptor;
import org.opensaml.saml.saml2.metadata.SPSSODescriptor;
+import org.opensaml.saml.security.SAMLMetadataKeyAgreementEncryptionConfiguration;
+import org.opensaml.saml.security.SAMLMetadataKeyAgreementEncryptionConfiguration.KeyWrap;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.credential.CredentialSupport;
import org.opensaml.security.credential.UsageType;
@@ -56,20 +61,26 @@ import org.opensaml.security.crypto.KeySupport;
import org.opensaml.security.testing.SecurityProviderTestSupport;
import org.opensaml.xmlsec.EncryptionParameters;
import org.opensaml.xmlsec.KeyTransportAlgorithmPredicate;
+import org.opensaml.xmlsec.agreement.KeyAgreementCredential;
import org.opensaml.xmlsec.algorithm.AlgorithmRegistry;
import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
import org.opensaml.xmlsec.config.GlobalAlgorithmRegistryInitializer;
import org.opensaml.xmlsec.criterion.EncryptionConfigurationCriterion;
import org.opensaml.xmlsec.criterion.KeyInfoGenerationProfileCriterion;
+import org.opensaml.xmlsec.derivation.impl.ConcatKDF;
+import org.opensaml.xmlsec.derivation.impl.PBKDF2;
import org.opensaml.xmlsec.encryption.MGF;
import org.opensaml.xmlsec.encryption.OAEPparams;
import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
+import org.opensaml.xmlsec.encryption.support.KeyAgreementEncryptionConfiguration;
import org.opensaml.xmlsec.encryption.support.RSAOAEPParameters;
import org.opensaml.xmlsec.impl.BasicEncryptionConfiguration;
import org.opensaml.xmlsec.keyinfo.KeyInfoSupport;
import org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;
import org.opensaml.xmlsec.keyinfo.impl.BasicKeyInfoGeneratorFactory;
+import org.opensaml.xmlsec.keyinfo.impl.KeyAgreementKeyInfoGeneratorFactory;
import org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;
+import org.opensaml.xmlsec.keyinfo.impl.KeyAgreementKeyInfoGeneratorFactory.KeyAgreementKeyInfoGenerator;
import org.opensaml.xmlsec.signature.DigestMethod;
import org.opensaml.xmlsec.signature.KeyInfo;
import org.opensaml.xmlsec.signature.support.SignatureConstants;
@@ -96,6 +107,9 @@ public class SAMLMetadataEncryptionParametersResolverTest extends XMLObjectBaseT
private Credential dsaCred1;
private String dsaCred1KeyName = "DSACred1";
+ private Credential ecCred1;
+ private String ecCred1KeyName = "ECCred1";
+
private String defaultRSAKeyTransportAlgo = EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP;
private String defaultAES128DataAlgo = EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128;
private String defaultAES192DataAlgo = EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES192;
@@ -117,7 +131,7 @@ public class SAMLMetadataEncryptionParametersResolverTest extends XMLObjectBaseT
}
@BeforeClass
- public void buildCredentials() throws NoSuchAlgorithmException, NoSuchProviderException {
+ public void buildCredentials() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
KeyPair rsaKeyPair = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_RSA, 2048, null);
rsaCred1 = CredentialSupport.getSimpleCredential(rsaKeyPair.getPublic(), null);
rsaCred1.getKeyNames().add(rsaCred1KeyName);
@@ -125,6 +139,10 @@ public class SAMLMetadataEncryptionParametersResolverTest extends XMLObjectBaseT
KeyPair dsaKeyPair = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DSA, 1024, null);
dsaCred1 = CredentialSupport.getSimpleCredential(dsaKeyPair.getPublic(), null);
dsaCred1.getKeyNames().add(dsaCred1KeyName);
+
+ KeyPair ecKeyPair = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_EC, new ECGenParameterSpec("secp256r1"), null);
+ ecCred1 = CredentialSupport.getSimpleCredential(ecKeyPair.getPublic(), ecKeyPair.getPrivate());
+ ecCred1.getKeyNames().add(ecCred1KeyName);
}
@BeforeMethod
@@ -159,18 +177,32 @@ public class SAMLMetadataEncryptionParametersResolverTest extends XMLObjectBaseT
EncryptionConstants.ALGO_ID_KEYWRAP_TRIPLEDES
));
+ SAMLMetadataKeyAgreementEncryptionConfiguration ecConfig = new SAMLMetadataKeyAgreementEncryptionConfiguration();
+ ecConfig.setMetadataUseKeyWrap(KeyWrap.Default);
+ ecConfig.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+ ConcatKDF concatKDF = new ConcatKDF();
+ concatKDF.setAlgorithmID("00");
+ concatKDF.setPartyUInfo("00");
+ concatKDF.setPartyVInfo("00");
+ ecConfig.setParameters(Set.of(concatKDF));
+ config3.setKeyAgreementConfigurations(Map.of("EC", ecConfig));
+
BasicKeyInfoGeneratorFactory basicFactory1 = new BasicKeyInfoGeneratorFactory();
X509KeyInfoGeneratorFactory x509Factory1 = new X509KeyInfoGeneratorFactory();
+ KeyAgreementKeyInfoGeneratorFactory kaFactory1 = new KeyAgreementKeyInfoGeneratorFactory();
defaultKeyTransportKeyInfoGeneratorManager = new NamedKeyInfoGeneratorManager();
defaultKeyTransportKeyInfoGeneratorManager.registerDefaultFactory(basicFactory1);
defaultKeyTransportKeyInfoGeneratorManager.registerDefaultFactory(x509Factory1);
+ defaultKeyTransportKeyInfoGeneratorManager.registerDefaultFactory(kaFactory1);
config3.setKeyTransportKeyInfoGeneratorManager(defaultKeyTransportKeyInfoGeneratorManager);
BasicKeyInfoGeneratorFactory basicFactory2 = new BasicKeyInfoGeneratorFactory();
X509KeyInfoGeneratorFactory x509Factory2 = new X509KeyInfoGeneratorFactory();
+ KeyAgreementKeyInfoGeneratorFactory kaFactory2 = new KeyAgreementKeyInfoGeneratorFactory();
defaultDataEncryptionKeyInfoGeneratorManager = new NamedKeyInfoGeneratorManager();
defaultDataEncryptionKeyInfoGeneratorManager.registerDefaultFactory(basicFactory2);
defaultDataEncryptionKeyInfoGeneratorManager.registerDefaultFactory(x509Factory2);
+ defaultDataEncryptionKeyInfoGeneratorManager.registerDefaultFactory(kaFactory2);
config3.setDataKeyInfoGeneratorManager(defaultDataEncryptionKeyInfoGeneratorManager);
configCriterion = new EncryptionConfigurationCriterion(config1, config2, config3);
@@ -181,6 +213,7 @@ public class SAMLMetadataEncryptionParametersResolverTest extends XMLObjectBaseT
criteriaSet = new CriteriaSet(configCriterion, roleDescCriterion);
}
+
@Test
public void testBasic() throws ResolverException {
roleDesc.getKeyDescriptors().add(buildKeyDescriptor(rsaCred1KeyName, UsageType.ENCRYPTION, rsaCred1.getPublicKey()));
@@ -544,6 +577,334 @@ public class SAMLMetadataEncryptionParametersResolverTest extends XMLObjectBaseT
Assert.assertNull(params.getDataKeyInfoGenerator());
}
+ @Test
+ public void testECDHWithNoEncryptionMethodsAndKeyWrapDefault() throws ResolverException {
+ KeyDescriptor kd = buildKeyDescriptor(ecCred1KeyName, UsageType.ENCRYPTION, ecCred1.getPublicKey());
+ roleDesc.getKeyDescriptors().add(kd);
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ Assert.assertNotNull(params);
+ Assert.assertNull(params.getKeyTransportEncryptionCredential());
+ Assert.assertNull(params.getKeyTransportEncryptionAlgorithm());
+ Assert.assertNull(params.getKeyTransportKeyInfoGenerator());
+
+ Assert.assertNotNull(params.getDataEncryptionCredential());
+ Assert.assertTrue(KeyAgreementCredential.class.isInstance(params.getDataEncryptionCredential()));
+ Assert.assertNotNull(params.getDataEncryptionCredential().getSecretKey());
+ Assert.assertEquals(params.getDataEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ Assert.assertEquals(KeySupport.getKeyLength(params.getDataEncryptionCredential().getSecretKey()), Integer.valueOf(128));
+ Assert.assertEquals(params.getDataEncryptionAlgorithm(), defaultAES128DataAlgo);
+ Assert.assertNotNull(params.getDataKeyInfoGenerator());
+ Assert.assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getDataKeyInfoGenerator()));
+ }
+
+ @Test
+ public void testECDHWithNoEncryptionMethodsAndKeyWrapAlways() throws ResolverException {
+ KeyDescriptor kd = buildKeyDescriptor(ecCred1KeyName, UsageType.ENCRYPTION, ecCred1.getPublicKey());
+ roleDesc.getKeyDescriptors().add(kd);
+
+ SAMLMetadataKeyAgreementEncryptionConfiguration ecConfig = new SAMLMetadataKeyAgreementEncryptionConfiguration();
+ ecConfig.setMetadataUseKeyWrap(KeyWrap.Always);
+ config2.setKeyAgreementConfigurations(Map.of("EC", ecConfig));
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ Assert.assertNotNull(params);
+ Assert.assertNotNull(params.getKeyTransportEncryptionCredential());
+ Assert.assertTrue(KeyAgreementCredential.class.isInstance(params.getKeyTransportEncryptionCredential()));
+ Assert.assertNotNull(params.getKeyTransportEncryptionCredential().getSecretKey());
+ Assert.assertEquals(params.getKeyTransportEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ Assert.assertEquals(KeySupport.getKeyLength(params.getKeyTransportEncryptionCredential().getSecretKey()), Integer.valueOf(128));
+ Assert.assertEquals(params.getKeyTransportEncryptionAlgorithm(), EncryptionConstants.ALGO_ID_KEYWRAP_AES128);
+ Assert.assertNotNull(params.getKeyTransportKeyInfoGenerator());
+ Assert.assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getKeyTransportKeyInfoGenerator()));
+
+ Assert.assertNull(params.getDataEncryptionCredential());
+ Assert.assertEquals(params.getDataEncryptionAlgorithm(), defaultAES128DataAlgo);
+ Assert.assertNull(params.getDataKeyInfoGenerator());
+ }
+
+ @Test
+ public void testECDHWithNoEncryptionMethodsAndKeyWrapNever() throws ResolverException {
+ KeyDescriptor kd = buildKeyDescriptor(ecCred1KeyName, UsageType.ENCRYPTION, ecCred1.getPublicKey());
+ roleDesc.getKeyDescriptors().add(kd);
+
+ SAMLMetadataKeyAgreementEncryptionConfiguration ecConfig = new SAMLMetadataKeyAgreementEncryptionConfiguration();
+ ecConfig.setMetadataUseKeyWrap(KeyWrap.Never);
+ config2.setKeyAgreementConfigurations(Map.of("EC", ecConfig));
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ Assert.assertNull(params.getKeyTransportEncryptionCredential());
+ Assert.assertNull(params.getKeyTransportEncryptionAlgorithm());
+ Assert.assertNull(params.getKeyTransportKeyInfoGenerator());
+
+ Assert.assertNotNull(params.getDataEncryptionCredential());
+ Assert.assertTrue(KeyAgreementCredential.class.isInstance(params.getDataEncryptionCredential()));
+ Assert.assertNotNull(params.getDataEncryptionCredential().getSecretKey());
+ Assert.assertEquals(params.getDataEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ Assert.assertEquals(KeySupport.getKeyLength(params.getDataEncryptionCredential().getSecretKey()), Integer.valueOf(128));
+ Assert.assertEquals(params.getDataEncryptionAlgorithm(), defaultAES128DataAlgo);
+ Assert.assertNotNull(params.getDataKeyInfoGenerator());
+ Assert.assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getDataKeyInfoGenerator()));
+ }
+
+ @Test
+ public void testECDHWithNoEncryptionMethodsAndKeyWrapIfNotIndicated() throws ResolverException {
+ KeyDescriptor kd = buildKeyDescriptor(ecCred1KeyName, UsageType.ENCRYPTION, ecCred1.getPublicKey());
+ roleDesc.getKeyDescriptors().add(kd);
+
+ SAMLMetadataKeyAgreementEncryptionConfiguration ecConfig = new SAMLMetadataKeyAgreementEncryptionConfiguration();
+ ecConfig.setMetadataUseKeyWrap(KeyWrap.IfNotIndicated);
+ config2.setKeyAgreementConfigurations(Map.of("EC", ecConfig));
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ Assert.assertNotNull(params);
+ Assert.assertNotNull(params.getKeyTransportEncryptionCredential());
+ Assert.assertTrue(KeyAgreementCredential.class.isInstance(params.getKeyTransportEncryptionCredential()));
+ Assert.assertNotNull(params.getKeyTransportEncryptionCredential().getSecretKey());
+ Assert.assertEquals(params.getKeyTransportEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ Assert.assertEquals(KeySupport.getKeyLength(params.getKeyTransportEncryptionCredential().getSecretKey()), Integer.valueOf(128));
+ Assert.assertEquals(params.getKeyTransportEncryptionAlgorithm(), EncryptionConstants.ALGO_ID_KEYWRAP_AES128);
+ Assert.assertNotNull(params.getKeyTransportKeyInfoGenerator());
+ Assert.assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getKeyTransportKeyInfoGenerator()));
+
+ Assert.assertNull(params.getDataEncryptionCredential());
+ Assert.assertEquals(params.getDataEncryptionAlgorithm(), defaultAES128DataAlgo);
+ Assert.assertNull(params.getDataKeyInfoGenerator());
+ }
+
+ @Test
+ public void testECDHWithBlockEncryptionMethodAndKeyWrapDefault() throws ResolverException {
+ KeyDescriptor kd = buildKeyDescriptor(ecCred1KeyName, UsageType.ENCRYPTION, ecCred1.getPublicKey());
+ kd.getEncryptionMethods().add(buildEncryptionMethod(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM));
+ roleDesc.getKeyDescriptors().add(kd);
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ Assert.assertNotNull(params);
+ Assert.assertNull(params.getKeyTransportEncryptionCredential());
+ Assert.assertNull(params.getKeyTransportEncryptionAlgorithm());
+ Assert.assertNull(params.getKeyTransportKeyInfoGenerator());
+
+ Assert.assertNotNull(params.getDataEncryptionCredential());
+ Assert.assertTrue(KeyAgreementCredential.class.isInstance(params.getDataEncryptionCredential()));
+ Assert.assertNotNull(params.getDataEncryptionCredential().getSecretKey());
+ Assert.assertEquals(params.getDataEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ Assert.assertEquals(KeySupport.getKeyLength(params.getDataEncryptionCredential().getSecretKey()), Integer.valueOf(256));
+ Assert.assertEquals(params.getDataEncryptionAlgorithm(), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM);
+ Assert.assertNotNull(params.getDataKeyInfoGenerator());
+ Assert.assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getDataKeyInfoGenerator()));
+ }
+
+ @Test
+ public void testECDHWithBlockEncryptionMethodAndKeyWrapAlways() throws ResolverException {
+ KeyDescriptor kd = buildKeyDescriptor(ecCred1KeyName, UsageType.ENCRYPTION, ecCred1.getPublicKey());
+ kd.getEncryptionMethods().add(buildEncryptionMethod(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM));
+ roleDesc.getKeyDescriptors().add(kd);
+
+ SAMLMetadataKeyAgreementEncryptionConfiguration ecConfig = new SAMLMetadataKeyAgreementEncryptionConfiguration();
+ ecConfig.setMetadataUseKeyWrap(KeyWrap.Always);
+ config2.setKeyAgreementConfigurations(Map.of("EC", ecConfig));
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ Assert.assertNotNull(params);
+ Assert.assertNotNull(params.getKeyTransportEncryptionCredential());
+ Assert.assertTrue(KeyAgreementCredential.class.isInstance(params.getKeyTransportEncryptionCredential()));
+ Assert.assertNotNull(params.getKeyTransportEncryptionCredential().getSecretKey());
+ Assert.assertEquals(params.getKeyTransportEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ Assert.assertEquals(KeySupport.getKeyLength(params.getKeyTransportEncryptionCredential().getSecretKey()), Integer.valueOf(128));
+ Assert.assertEquals(params.getKeyTransportEncryptionAlgorithm(), EncryptionConstants.ALGO_ID_KEYWRAP_AES128);
+ Assert.assertNotNull(params.getKeyTransportKeyInfoGenerator());
+ Assert.assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getKeyTransportKeyInfoGenerator()));
+
+
+ Assert.assertNull(params.getDataEncryptionCredential());
+ Assert.assertEquals(params.getDataEncryptionAlgorithm(), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM);
+ Assert.assertNull(params.getDataKeyInfoGenerator());
+ }
+
+ @Test
+ public void testECDHWithKeyWrapEncryptionMethodAndKeyWrapDefault() throws ResolverException {
+ KeyDescriptor kd = buildKeyDescriptor(ecCred1KeyName, UsageType.ENCRYPTION, ecCred1.getPublicKey());
+ kd.getEncryptionMethods().add(buildEncryptionMethod(EncryptionConstants.ALGO_ID_KEYWRAP_AES256));
+ roleDesc.getKeyDescriptors().add(kd);
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ Assert.assertNotNull(params.getKeyTransportEncryptionCredential());
+ Assert.assertTrue(KeyAgreementCredential.class.isInstance(params.getKeyTransportEncryptionCredential()));
+ Assert.assertNotNull(params.getKeyTransportEncryptionCredential().getSecretKey());
+ Assert.assertEquals(params.getKeyTransportEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ Assert.assertEquals(KeySupport.getKeyLength(params.getKeyTransportEncryptionCredential().getSecretKey()), Integer.valueOf(256));
+ Assert.assertEquals(params.getKeyTransportEncryptionAlgorithm(), EncryptionConstants.ALGO_ID_KEYWRAP_AES256);
+ Assert.assertNotNull(params.getKeyTransportKeyInfoGenerator());
+ Assert.assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getKeyTransportKeyInfoGenerator()));
+
+ Assert.assertNull(params.getDataEncryptionCredential());
+ Assert.assertEquals(params.getDataEncryptionAlgorithm(), defaultAES128DataAlgo);
+ Assert.assertNull(params.getDataKeyInfoGenerator());
+ }
+
+ @Test
+ public void testECDHWithKeyWrapEncryptionMethodAndKeyWrapNever() throws ResolverException {
+ KeyDescriptor kd = buildKeyDescriptor(ecCred1KeyName, UsageType.ENCRYPTION, ecCred1.getPublicKey());
+ kd.getEncryptionMethods().add(buildEncryptionMethod(EncryptionConstants.ALGO_ID_KEYWRAP_AES256));
+ roleDesc.getKeyDescriptors().add(kd);
+
+ SAMLMetadataKeyAgreementEncryptionConfiguration ecConfig = new SAMLMetadataKeyAgreementEncryptionConfiguration();
+ ecConfig.setMetadataUseKeyWrap(KeyWrap.Never);
+ config2.setKeyAgreementConfigurations(Map.of("EC", ecConfig));
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ Assert.assertNull(params.getKeyTransportEncryptionCredential());
+ Assert.assertNull(params.getKeyTransportEncryptionAlgorithm());
+ Assert.assertNull(params.getKeyTransportKeyInfoGenerator());
+
+ Assert.assertNotNull(params.getDataEncryptionCredential());
+ Assert.assertTrue(KeyAgreementCredential.class.isInstance(params.getDataEncryptionCredential()));
+ Assert.assertNotNull(params.getDataEncryptionCredential().getSecretKey());
+ Assert.assertEquals(params.getDataEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ Assert.assertEquals(KeySupport.getKeyLength(params.getDataEncryptionCredential().getSecretKey()), Integer.valueOf(128));
+ Assert.assertEquals(params.getDataEncryptionAlgorithm(), defaultAES128DataAlgo);
+ Assert.assertNotNull(params.getDataKeyInfoGenerator());
+ Assert.assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getDataKeyInfoGenerator()));
+ }
+
+ @Test
+ public void testECDHWithBlockAndKeyWrapEncryptionMethods() throws ResolverException {
+ KeyDescriptor kd = buildKeyDescriptor(ecCred1KeyName, UsageType.ENCRYPTION, ecCred1.getPublicKey());
+ kd.getEncryptionMethods().add(buildEncryptionMethod(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM));
+ kd.getEncryptionMethods().add(buildEncryptionMethod(EncryptionConstants.ALGO_ID_KEYWRAP_AES256));
+ roleDesc.getKeyDescriptors().add(kd);
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ Assert.assertNotNull(params.getKeyTransportEncryptionCredential());
+ Assert.assertTrue(KeyAgreementCredential.class.isInstance(params.getKeyTransportEncryptionCredential()));
+ Assert.assertNotNull(params.getKeyTransportEncryptionCredential().getSecretKey());
+ Assert.assertEquals(params.getKeyTransportEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ Assert.assertEquals(KeySupport.getKeyLength(params.getKeyTransportEncryptionCredential().getSecretKey()), Integer.valueOf(256));
+ Assert.assertEquals(params.getKeyTransportEncryptionAlgorithm(), EncryptionConstants.ALGO_ID_KEYWRAP_AES256);
+ Assert.assertNotNull(params.getKeyTransportKeyInfoGenerator());
+ Assert.assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getKeyTransportKeyInfoGenerator()));
+
+ Assert.assertNull(params.getDataEncryptionCredential());
+ Assert.assertEquals(params.getDataEncryptionAlgorithm(), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM);
+ Assert.assertNull(params.getDataKeyInfoGenerator());
+ }
+
+ @Test
+ public void testECDHWithKeyWrapEncryptionMethodAndGeneratedDataCredential() throws ResolverException {
+ KeyDescriptor kd = buildKeyDescriptor(ecCred1KeyName, UsageType.ENCRYPTION, ecCred1.getPublicKey());
+ kd.getEncryptionMethods().add(buildEncryptionMethod(EncryptionConstants.ALGO_ID_KEYWRAP_AES256));
+ roleDesc.getKeyDescriptors().add(kd);
+
+ resolver.setAutoGenerateDataEncryptionCredential(true);
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ Assert.assertNotNull(params.getKeyTransportEncryptionCredential());
+ Assert.assertTrue(KeyAgreementCredential.class.isInstance(params.getKeyTransportEncryptionCredential()));
+ Assert.assertNotNull(params.getKeyTransportEncryptionCredential().getSecretKey());
+ Assert.assertEquals(params.getKeyTransportEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ Assert.assertEquals(KeySupport.getKeyLength(params.getKeyTransportEncryptionCredential().getSecretKey()), Integer.valueOf(256));
+ Assert.assertEquals(params.getKeyTransportEncryptionAlgorithm(), EncryptionConstants.ALGO_ID_KEYWRAP_AES256);
+ Assert.assertNotNull(params.getKeyTransportKeyInfoGenerator());
+ Assert.assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getKeyTransportKeyInfoGenerator()));
+
+ Assert.assertNotNull(params.getDataEncryptionCredential());
+ Assert.assertNotNull(params.getDataEncryptionCredential().getSecretKey());
+ Assert.assertEquals(params.getDataEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ Assert.assertEquals(KeySupport.getKeyLength(params.getDataEncryptionCredential().getSecretKey()), Integer.valueOf(128));
+ Assert.assertEquals(params.getDataEncryptionAlgorithm(), defaultAES128DataAlgo);
+ Assert.assertNotNull(params.getDataKeyInfoGenerator());
+ }
+
+ @Test
+ public void testECDHWithKDFOverride() throws ResolverException {
+ KeyDescriptor kd = buildKeyDescriptor(ecCred1KeyName, UsageType.ENCRYPTION, ecCred1.getPublicKey());
+ kd.getEncryptionMethods().add(buildEncryptionMethod(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM));
+ roleDesc.getKeyDescriptors().add(kd);
+
+ SAMLMetadataKeyAgreementEncryptionConfiguration ecConfig = new SAMLMetadataKeyAgreementEncryptionConfiguration();
+ PBKDF2 kdf = new PBKDF2();
+ ecConfig.setParameters(Set.of(kdf));
+ config2.setKeyAgreementConfigurations(Map.of("EC", ecConfig));
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ Assert.assertNotNull(params);
+ Assert.assertNull(params.getKeyTransportEncryptionCredential());
+ Assert.assertNull(params.getKeyTransportEncryptionAlgorithm());
+ Assert.assertNull(params.getKeyTransportKeyInfoGenerator());
+
+ Assert.assertNotNull(params.getDataEncryptionCredential());
+ Assert.assertTrue(KeyAgreementCredential.class.isInstance(params.getDataEncryptionCredential()));
+ Assert.assertNotNull(params.getDataEncryptionCredential().getSecretKey());
+ Assert.assertEquals(params.getDataEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ Assert.assertEquals(KeySupport.getKeyLength(params.getDataEncryptionCredential().getSecretKey()), Integer.valueOf(256));
+ Assert.assertEquals(params.getDataEncryptionAlgorithm(), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM);
+ Assert.assertNotNull(params.getDataKeyInfoGenerator());
+ Assert.assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getDataKeyInfoGenerator()));
+
+ KeyAgreementCredential kaCred = KeyAgreementCredential.class.cast(params.getDataEncryptionCredential());
+ Assert.assertEquals(kaCred.getParameters().size(), 1);
+ Assert.assertTrue(kaCred.getParameters().contains(PBKDF2.class));
+ }
+
+ @Test
+ public void testGetEffectiveKeyAgreementConfiguration() {
+ SAMLMetadataKeyAgreementEncryptionConfiguration ecConfig1 = new SAMLMetadataKeyAgreementEncryptionConfiguration();
+ ecConfig1.setMetadataUseKeyWrap(KeyWrap.Always);
+ config1.setKeyAgreementConfigurations(Map.of("EC", ecConfig1));
+
+ SAMLMetadataKeyAgreementEncryptionConfiguration ecConfig2 = new SAMLMetadataKeyAgreementEncryptionConfiguration();
+ ecConfig2.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+ ecConfig2.setParameters(Set.of(new PBKDF2()));
+ ecConfig2.setMetadataUseKeyWrap(KeyWrap.IfNotIndicated);
+ config2.setKeyAgreementConfigurations(Map.of("EC", ecConfig2));
+
+ SAMLMetadataKeyAgreementEncryptionConfiguration ecConfig3 = new SAMLMetadataKeyAgreementEncryptionConfiguration();
+ ecConfig3.setAlgorithm("SomeAlgo");
+ ecConfig3.setParameters(Set.of(new ConcatKDF()));
+ ecConfig3.setMetadataUseKeyWrap(KeyWrap.Default);
+ config3.setKeyAgreementConfigurations(Map.of("EC", ecConfig3));
+
+ SAMLMetadataKeyAgreementEncryptionConfiguration config = resolver.getEffectiveKeyAgreementConfiguration(criteriaSet, ecCred1);
+
+ Assert.assertEquals(config.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+ Assert.assertEquals(config.getMetadataUseKeyWrap(), KeyWrap.Always);
+ Assert.assertEquals(config.getParameters().size(), 1);
+ Assert.assertTrue(PBKDF2.class.isInstance(config.getParameters().iterator().next()));
+ }
+
+ @Test
+ public void testDefaultKeyAgreementUseKeyWrap() {
+ KeyAgreementEncryptionConfiguration ecConfig = new KeyAgreementEncryptionConfiguration();
+ ecConfig.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+ ecConfig.setParameters(Set.of());
+ BasicEncryptionConfiguration encConfig = new BasicEncryptionConfiguration();
+ encConfig.setKeyAgreementConfigurations(Map.of("EC", ecConfig));
+ CriteriaSet criteria = new CriteriaSet(new EncryptionConfigurationCriterion(encConfig));
+
+ // Check default value
+ Assert.assertEquals(resolver.getDefaultKeyAgreemenUseKeyWrap(), KeyWrap.Default);
+
+ SAMLMetadataKeyAgreementEncryptionConfiguration config = resolver.getEffectiveKeyAgreementConfiguration(criteria, ecCred1);
+ Assert.assertEquals(config.getMetadataUseKeyWrap(), KeyWrap.Default);
+
+ resolver.setDefaultKeyAgreementUseKeyWrap(KeyWrap.Always);
+ Assert.assertEquals(resolver.getDefaultKeyAgreemenUseKeyWrap(), KeyWrap.Always);
+
+ config = resolver.getEffectiveKeyAgreementConfiguration(criteria, ecCred1);
+ Assert.assertEquals(config.getMetadataUseKeyWrap(), KeyWrap.Always);
+ }
+
@Test
public void testMultipleKeyDescriptors() throws ResolverException {
roleDesc.getKeyDescriptors().add(buildKeyDescriptor(dsaCred1KeyName, UsageType.SIGNING, dsaCred1.getPublicKey()));
diff --git a/opensaml-saml-impl/src/test/resources/logback-test.xml b/opensaml-saml-impl/src/test/resources/logback-test.xml
index ac9ab81d9..5933e433c 100644
--- a/opensaml-saml-impl/src/test/resources/logback-test.xml
+++ b/opensaml-saml-impl/src/test/resources/logback-test.xml
@@ -16,6 +16,14 @@
<level value="WARN"/>
</logger>
+ <logger name="org.opensaml.xmlsec.impl.BasicEncryptionParametersResolver">
+ <level value="TRACE"/>
+ </logger>
+
+ <logger name="org.opensaml.saml.security.impl.SAMLMetadataEncryptionParametersResolver">
+ <level value="TRACE"/>
+ </logger>
+
<logger name="org.opensaml.security.x509.tls.impl.ThreadLocalX509TrustManager">
<level value="INFO"/>
</logger>
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/EncryptionConfiguration.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/EncryptionConfiguration.java
index 1d0f295c4..5a70942c7 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/EncryptionConfiguration.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/EncryptionConfiguration.java
@@ -18,6 +18,7 @@
package org.opensaml.xmlsec;
import java.util.List;
+import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -27,6 +28,7 @@ import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
import org.opensaml.security.credential.Credential;
+import org.opensaml.xmlsec.encryption.support.KeyAgreementEncryptionConfiguration;
import org.opensaml.xmlsec.encryption.support.RSAOAEPParameters;
import org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;
@@ -101,4 +103,12 @@ public interface EncryptionConfiguration extends WhitelistBlacklistConfiguration
*/
@Nullable public KeyTransportAlgorithmPredicate getKeyTransportAlgorithmPredicate();
+ /**
+ * Get the map of {@link KeyAgreementEncryptionConfiguration} instances.
+ *
+ * @return the
+ */
+ @Nonnull @Unmodifiable @NotLive
+ public Map<String, KeyAgreementEncryptionConfiguration> getKeyAgreementConfigurations();
+
}
\ No newline at end of file
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementSupport.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementSupport.java
index 241ac63e6..a8db46532 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementSupport.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementSupport.java
@@ -17,10 +17,14 @@
package org.opensaml.xmlsec.agreement;
+import java.util.Set;
+
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opensaml.core.config.ConfigurationService;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.crypto.JCAConstants;
import org.opensaml.xmlsec.encryption.AgreementMethod;
import org.opensaml.xmlsec.encryption.EncryptedType;
import org.opensaml.xmlsec.encryption.EncryptionMethod;
@@ -31,6 +35,9 @@ import org.opensaml.xmlsec.encryption.KeySize;
*/
public final class KeyAgreementSupport {
+ /** JCA key algorithms that support key agreement. */
+ public static final Set<String> KEY_ALGORITHMS = Set.of(JCAConstants.KEY_ALGO_EC);
+
/** Constructor. */
private KeyAgreementSupport() {}
@@ -44,6 +51,34 @@ public final class KeyAgreementSupport {
return ConfigurationService.get(KeyAgreementProcessorRegistry.class);
}
+ /**
+ * Lookup and return the {@link KeyAgreementProcessor} to use for the specified key
+ * agreement algorithm.
+ *
+ * @param algorithm the key agreement algorithm
+ *
+ * @return the processor for that algorithm
+ *
+ * @throws KeyAgreementException if global {@link KeyAgreementProcessorRegistry} is not configured
+ * or if no processor is registered for the specified algorithm
+ */
+ @Nonnull public static KeyAgreementProcessor getProcessor(@Nonnull final String algorithm)
+ throws KeyAgreementException {
+
+ final KeyAgreementProcessorRegistry registry = getGlobalProcessorRegistry();
+ if (registry == null) {
+ throw new KeyAgreementException("Global KeyAgreementProcessorRegistry not configured");
+ }
+
+ final KeyAgreementProcessor processor = registry.getProcessor(algorithm);
+ if (processor == null) {
+ throw new KeyAgreementException("No KeyAgreementProcessor registered for specified algorithm: "
+ + algorithm);
+ }
+
+ return processor;
+ }
+
/**
* Look for an explicit key size via an {@link AgreementMethod}'s grandparent's {@link EncryptionMethod}
* child's {@link KeySize} child element.
@@ -65,4 +100,18 @@ public final class KeyAgreementSupport {
return et.getEncryptionMethod().getKeySize().getValue();
}
+
+ /**
+ * Evaluate whether the specified credential contains a public key which supports key agreement.
+ *
+ * @param credential the credential to evaluate
+ * @return true if supports key agreement, false if does not
+ */
+ public static boolean supportsKeyAgreement(@Nullable final Credential credential) {
+ if (credential == null) {
+ return false;
+ }
+
+ return credential.getPublicKey() != null && KEY_ALGORITHMS.contains(credential.getPublicKey().getAlgorithm());
+ }
}
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/algorithm/AlgorithmSupport.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/algorithm/AlgorithmSupport.java
index 51cb6d508..6e1dad94d 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/algorithm/AlgorithmSupport.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/algorithm/AlgorithmSupport.java
@@ -249,6 +249,40 @@ public final class AlgorithmSupport {
|| EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP11.equals(keyTransportAlgorithm);
}
+ /**
+ * Check whether the algorithm URI indicates block encryption.
+ *
+ * @param algorithm the algorithm URI
+ * @return true if URI indicates symmetric key wrap, false otherwise
+ */
+ public static boolean isBlockEncryption(@Nonnull final String algorithm) {
+ final AlgorithmRegistry registry = getGlobalAlgorithmRegistry();
+ if (registry != null){
+ final AlgorithmDescriptor descriptor = registry.get(algorithm);
+ if (descriptor != null) {
+ return descriptor.getType().equals(AlgorithmDescriptor.AlgorithmType.BlockEncryption);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check whether the algorithm URI indicates symmetric key wrap.
+ *
+ * @param algorithm the algorithm URI
+ * @return true if URI indicates symmetric key wrap, false otherwise
+ */
+ public static boolean isSymmetricKeyWrap(@Nonnull final String algorithm) {
+ final AlgorithmRegistry registry = getGlobalAlgorithmRegistry();
+ if (registry != null){
+ final AlgorithmDescriptor descriptor = registry.get(algorithm);
+ if (descriptor != null) {
+ return descriptor.getType().equals(AlgorithmDescriptor.AlgorithmType.SymmetricKeyWrap);
+ }
+ }
+ return false;
+ }
+
/**
* Check whether the signature method algorithm URI indicates HMAC.
*
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/KeyAgreementEncryptionConfiguration.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/KeyAgreementEncryptionConfiguration.java
new file mode 100644
index 000000000..e8cd2da83
--- /dev/null
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/KeyAgreementEncryptionConfiguration.java
@@ -0,0 +1,85 @@
+/*
+ * 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 org.opensaml.xmlsec.encryption.support;
+
+import java.util.Collection;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.xmlsec.agreement.KeyAgreementParameter;
+
+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;
+
+/**
+ * A component representing the specific configuration for a key agreement encryption operation.
+ */
+public class KeyAgreementEncryptionConfiguration {
+
+ /** The key agreement algorithm URI. */
+ private String algorithm;
+
+ /** The collection of {@link KeyAgreementParameter}. */
+ @Nullable private Collection<KeyAgreementParameter> parameters;
+
+ /**
+ * Get the algorithm URI.
+ *
+ * @return the algorithm URI
+ */
+ @Nullable public String getAlgorithm() {
+ return algorithm;
+ }
+
+ /**
+ * Set the algorithm URI.
+ *
+ * @param uri the algorithm URI
+ */
+ public void setAlgorithm(@Nullable final String uri) {
+ algorithm = StringSupport.trimOrNull(uri);
+ }
+
+ /**
+ * Get the collection of {@link KeyAgreementParameter}.
+ *
+ * @return the collection of parameters
+ */
+ @Nullable @NonnullElements @NotLive @Unmodifiable
+ public Collection<KeyAgreementParameter> getParameters() {
+ return parameters;
+ }
+
+ /**
+ * Set the collection of {@link KeyAgreementParameter}.
+ *
+ * @param params the collection of parameters
+ */
+ public void setParameters(@Nullable final Collection<KeyAgreementParameter> params) {
+ if (params == null) {
+ parameters = null;
+ } else {
+ parameters = params.stream().filter(Objects::nonNull).collect(Collectors.toUnmodifiableSet());
+ }
+ }
+
+}
diff --git a/opensaml-xmlsec-api/src/test/java/org/opensaml/xmlsec/algorithm/AlgorithmSupportTest.java b/opensaml-xmlsec-api/src/test/java/org/opensaml/xmlsec/algorithm/AlgorithmSupportTest.java
index 7b231b1c9..a7a048a28 100644
--- a/opensaml-xmlsec-api/src/test/java/org/opensaml/xmlsec/algorithm/AlgorithmSupportTest.java
+++ b/opensaml-xmlsec-api/src/test/java/org/opensaml/xmlsec/algorithm/AlgorithmSupportTest.java
@@ -107,6 +107,37 @@ public class AlgorithmSupportTest extends OpenSAMLInitBaseTestCase {
Assert.assertFalse(AlgorithmSupport.isDataEncryptionAlgorithm(new DigestSHA256()));
}
+ @Test
+ public void testIsSymmetricKeyWrap() {
+ Assert.assertTrue(AlgorithmSupport.isSymmetricKeyWrap(EncryptionConstants.ALGO_ID_KEYWRAP_AES128));
+ Assert.assertTrue(AlgorithmSupport.isSymmetricKeyWrap(EncryptionConstants.ALGO_ID_KEYWRAP_AES192));
+ Assert.assertTrue(AlgorithmSupport.isSymmetricKeyWrap(EncryptionConstants.ALGO_ID_KEYWRAP_AES256));
+ Assert.assertTrue(AlgorithmSupport.isSymmetricKeyWrap(EncryptionConstants.ALGO_ID_KEYWRAP_TRIPLEDES));
+
+ //Test some failure cases
+ Assert.assertFalse(AlgorithmSupport.isSymmetricKeyWrap(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128));
+ Assert.assertFalse(AlgorithmSupport.isSymmetricKeyWrap(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM));
+ Assert.assertFalse(AlgorithmSupport.isSymmetricKeyWrap(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256));
+ Assert.assertFalse(AlgorithmSupport.isSymmetricKeyWrap(SignatureConstants.ALGO_ID_DIGEST_SHA256));
+ }
+
+ @Test
+ public void testIsBlockEncryption() {
+ Assert.assertTrue(AlgorithmSupport.isBlockEncryption(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128));
+ Assert.assertTrue(AlgorithmSupport.isBlockEncryption(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM));
+ Assert.assertTrue(AlgorithmSupport.isBlockEncryption(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES192));
+ Assert.assertTrue(AlgorithmSupport.isBlockEncryption(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES192_GCM));
+ Assert.assertTrue(AlgorithmSupport.isBlockEncryption(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256));
+ Assert.assertTrue(AlgorithmSupport.isBlockEncryption(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM));
+ Assert.assertTrue(AlgorithmSupport.isBlockEncryption(EncryptionConstants.ALGO_ID_BLOCKCIPHER_TRIPLEDES));
+
+ //Test some failure cases
+ Assert.assertFalse(AlgorithmSupport.isBlockEncryption(EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSA15));
+ Assert.assertFalse(AlgorithmSupport.isBlockEncryption(EncryptionConstants.ALGO_ID_KEYWRAP_AES128));
+ Assert.assertFalse(AlgorithmSupport.isBlockEncryption(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256));
+ Assert.assertFalse(AlgorithmSupport.isBlockEncryption(SignatureConstants.ALGO_ID_DIGEST_SHA224));
+ }
+
@Test
public void testCredentialSupportsAlgorithmForSigning() throws NoSuchAlgorithmException, KeyException, NoSuchProviderException {
Credential credential;
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/config/impl/DefaultSecurityConfigurationBootstrap.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/config/impl/DefaultSecurityConfigurationBootstrap.java
index 9b81de8a7..1dbbd939c 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/config/impl/DefaultSecurityConfigurationBootstrap.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/config/impl/DefaultSecurityConfigurationBootstrap.java
@@ -19,14 +19,20 @@ package org.opensaml.xmlsec.config.impl;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import javax.annotation.Nonnull;
+import org.opensaml.security.crypto.JCAConstants;
+import org.opensaml.xmlsec.derivation.impl.ConcatKDF;
import org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver;
import org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;
import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
import org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver;
+import org.opensaml.xmlsec.encryption.support.KeyAgreementEncryptionConfiguration;
import org.opensaml.xmlsec.encryption.support.RSAOAEPParameters;
import org.opensaml.xmlsec.encryption.support.SimpleKeyInfoReferenceEncryptedKeyResolver;
import org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyResolver;
@@ -48,6 +54,10 @@ import org.opensaml.xmlsec.keyinfo.impl.provider.ECKeyValueProvider;
import org.opensaml.xmlsec.keyinfo.impl.provider.InlineX509DataProvider;
import org.opensaml.xmlsec.keyinfo.impl.provider.RSAKeyValueProvider;
import org.opensaml.xmlsec.signature.support.SignatureConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
/**
* A utility class which programmatically builds basic instances of various components
@@ -56,6 +66,9 @@ import org.opensaml.xmlsec.signature.support.SignatureConstants;
*/
public class DefaultSecurityConfigurationBootstrap {
+ /** Logger. */
+ private static final Logger LOG = LoggerFactory.getLogger(DefaultSecurityConfigurationBootstrap.class);
+
/** Constructor. */
protected DefaultSecurityConfigurationBootstrap() {}
@@ -83,8 +96,8 @@ public class DefaultSecurityConfigurationBootstrap {
// The order of the RSA algos is significant.
EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP,
- // The order of these is not significant.
- // These aren't really "preferences" per se. They just need to be registered
+ // The order of these is only significant when doing key agreement with key wrap.
+ // Otherwise the order is not significant, they just need to be registered
// so that they can be used if a credential with a key of that type and size is seen.
EncryptionConstants.ALGO_ID_KEYWRAP_AES128,
EncryptionConstants.ALGO_ID_KEYWRAP_AES192,
@@ -98,6 +111,26 @@ public class DefaultSecurityConfigurationBootstrap {
null
));
+ try {
+ final Map<String, KeyAgreementEncryptionConfiguration> kaConfigs = new HashMap<>();
+
+ final KeyAgreementEncryptionConfiguration ecConfig = new KeyAgreementEncryptionConfiguration();
+ ecConfig.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+ final ConcatKDF ecConcatKDF = new ConcatKDF();
+ // Need to set these 3 to something to confirm to NIST spec requirements. Actual deployments
+ // can and should override in a custom config with specific parameter values, if needed.
+ ecConcatKDF.setAlgorithmID("00");
+ ecConcatKDF.setPartyUInfo("00");
+ ecConcatKDF.setPartyVInfo("00");
+ ecConcatKDF.initialize();
+ ecConfig.setParameters(Set.of(ecConcatKDF));
+ kaConfigs.put(JCAConstants.KEY_ALGO_EC, ecConfig);
+
+ config.setKeyAgreementConfigurations(kaConfigs);
+ } catch (final ComponentInitializationException e) {
+ LOG.error("Initialization failure on global key agreement encryption configuration, will be unusable", e);
+ }
+
config.setDataKeyInfoGeneratorManager(buildDataEncryptionKeyInfoGeneratorManager());
config.setKeyTransportKeyInfoGeneratorManager(buildKeyTransportEncryptionKeyInfoGeneratorManager());
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicEncryptionConfiguration.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicEncryptionConfiguration.java
index ac85ba2e1..6c9d061f9 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicEncryptionConfiguration.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicEncryptionConfiguration.java
@@ -19,6 +19,7 @@ package org.opensaml.xmlsec.impl;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -31,6 +32,7 @@ import net.shibboleth.utilities.java.support.primitive.StringSupport;
import org.opensaml.security.credential.Credential;
import org.opensaml.xmlsec.EncryptionConfiguration;
import org.opensaml.xmlsec.KeyTransportAlgorithmPredicate;
+import org.opensaml.xmlsec.encryption.support.KeyAgreementEncryptionConfiguration;
import org.opensaml.xmlsec.encryption.support.RSAOAEPParameters;
import org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;
import org.slf4j.Logger;
@@ -74,6 +76,9 @@ public class BasicEncryptionConfiguration extends BasicWhitelistBlacklistConfigu
/** Key transport algorithm predicate. */
@Nullable private KeyTransportAlgorithmPredicate keyTransportPredicate;
+ /** Key agreement configurations. */
+ @Nonnull @NonnullElements private Map<String, KeyAgreementEncryptionConfiguration> keyAgreementConfigurations;
+
//TODO chaining to parent config instance on getters? or use a wrapping proxy, etc?
//TODO update for modern coding conventions, Guava, etc
@@ -84,6 +89,7 @@ public class BasicEncryptionConfiguration extends BasicWhitelistBlacklistConfigu
dataEncryptionAlgorithms = Collections.emptyList();
keyTransportEncryptionCredentials = Collections.emptyList();
keyTransportEncryptionAlgorithms = Collections.emptyList();
+ keyAgreementConfigurations = Collections.emptyMap();
rsaOAEPParametersMerge = true;
}
@@ -245,4 +251,22 @@ public class BasicEncryptionConfiguration extends BasicWhitelistBlacklistConfigu
keyTransportPredicate = predicate;
}
+ /** {@inheritDoc} */
+ @Nonnull public Map<String, KeyAgreementEncryptionConfiguration> getKeyAgreementConfigurations() {
+ return keyAgreementConfigurations;
+ }
+
+ /**
+ * Set the map of {@link KeyAgreementEncryptionConfiguration} instances.
+ *
+ * @param configs the new map of instances
+ */
+ public void setKeyAgreementConfigurations(@Nullable final Map<String,KeyAgreementEncryptionConfiguration> configs) {
+ if (configs == null) {
+ keyAgreementConfigurations = Collections.emptyMap();
+ } else {
+ keyAgreementConfigurations = Map.copyOf(configs);
+ }
+ }
+
}
\ No newline at end of file
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicEncryptionParametersResolver.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicEncryptionParametersResolver.java
index 2387b9e79..379af0a9a 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicEncryptionParametersResolver.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicEncryptionParametersResolver.java
@@ -23,6 +23,7 @@ import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
@@ -34,11 +35,17 @@ import org.opensaml.xmlsec.EncryptionConfiguration;
import org.opensaml.xmlsec.EncryptionParameters;
import org.opensaml.xmlsec.EncryptionParametersResolver;
import org.opensaml.xmlsec.KeyTransportAlgorithmPredicate;
+import org.opensaml.xmlsec.agreement.KeyAgreementCredential;
+import org.opensaml.xmlsec.agreement.KeyAgreementException;
+import org.opensaml.xmlsec.agreement.KeyAgreementParameters;
+import org.opensaml.xmlsec.agreement.KeyAgreementProcessor;
+import org.opensaml.xmlsec.agreement.KeyAgreementSupport;
import org.opensaml.xmlsec.algorithm.AlgorithmRegistry;
import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
import org.opensaml.xmlsec.criterion.EncryptionConfigurationCriterion;
import org.opensaml.xmlsec.criterion.EncryptionOptionalCriterion;
import org.opensaml.xmlsec.criterion.KeyInfoGenerationProfileCriterion;
+import org.opensaml.xmlsec.encryption.support.KeyAgreementEncryptionConfiguration;
import org.opensaml.xmlsec.encryption.support.RSAOAEPParameters;
import org.opensaml.xmlsec.keyinfo.KeyInfoGenerator;
import org.slf4j.Logger;
@@ -196,7 +203,8 @@ public class BasicEncryptionParametersResolver extends AbstractSecurityParameter
}
log.debug("\tKey transport KeyInfoGenerator: {}",
- params.getKeyTransportKeyInfoGenerator() != null ? "present" : "null");
+ params.getKeyTransportKeyInfoGenerator() != null ?
+ params.getKeyTransportKeyInfoGenerator().getClass().getName() : "null");
final Key dataKey = CredentialSupport.extractEncryptionKey(params.getDataEncryptionCredential());
if (dataKey != null) {
@@ -208,7 +216,8 @@ public class BasicEncryptionParametersResolver extends AbstractSecurityParameter
log.debug("\tData encryption algorithm URI: {}", params.getDataEncryptionAlgorithm());
log.debug("\tData encryption KeyInfoGenerator: {}",
- params.getDataKeyInfoGenerator() != null ? "present" : "null");
+ params.getDataKeyInfoGenerator() != null ?
+ params.getDataKeyInfoGenerator().getClass().getName() : "null");
}
}
@@ -326,6 +335,11 @@ public class BasicEncryptionParametersResolver extends AbstractSecurityParameter
params.setDataEncryptionAlgorithm(resolveDataEncryptionAlgorithm(null, dataEncryptionAlgorithms));
} else {
for (final Credential dataEncryptionCredential : dataEncryptionCredentials) {
+ if (checkAndProcessKeyAgreement(params, criteria, dataEncryptionCredential, dataEncryptionAlgorithms,
+ Collections.emptyList())) {
+ return;
+ }
+
final String dataEncryptionAlgorithm = resolveDataEncryptionAlgorithm(dataEncryptionCredential,
dataEncryptionAlgorithms);
if (dataEncryptionAlgorithm != null) {
@@ -343,6 +357,11 @@ public class BasicEncryptionParametersResolver extends AbstractSecurityParameter
// Select key encryption cred and algorithm
for (final Credential keyTransportCredential : keyTransportCredentials) {
+ if (checkAndProcessKeyAgreement(params, criteria, keyTransportCredential, dataEncryptionAlgorithms,
+ keyTransportAlgorithms)) {
+ return;
+ }
+
final String keyTransportAlgorithm = resolveKeyTransportAlgorithm(keyTransportCredential,
keyTransportAlgorithms, params.getDataEncryptionAlgorithm(), keyTransportPredicate);
@@ -362,6 +381,145 @@ public class BasicEncryptionParametersResolver extends AbstractSecurityParameter
processDataEncryptionCredentialAutoGeneration(params);
}
+ /**
+ * Check for a credential type that implies a key agreement operation, and process if so indicated.
+ *
+ * <p>
+ * For both algorithm list arguments, they are assumed to already have had runtime support and include/exclude
+ * filtering applied.
+ * </p>
+ *
+ * <p>
+ * If symmetric key wrap should NOT be considered, pass an empty list for <code>keyTransportAlgorithms</code>.
+ * Otherwise, if the <code>keyTransportAlgorithms</code> list contains a symmetric key wrap algorithm, then
+ * key wrapping will be indicated in the produced parameters. If it does not then direct data encryption
+ * will be indicated.
+ * </p>
+ *
+ * @param params the params instance being populated
+ * @param criteria the input criteria being evaluated
+ * @param credential the credential being evaluated
+ * @param dataEncryptionAlgorithms the effective data encryption credentials
+ * @param keyTransportAlgorithms the effective key transport credentials
+ *
+ * @return true if all required parameters were supplied, key agreement was successfully performed,
+ * and the {@link EncryptionParameters} instance's credential and algorithms properties are fully populated,
+ * otherwise false
+ */
+ protected boolean checkAndProcessKeyAgreement(@Nonnull final EncryptionParameters params,
+ @Nonnull final CriteriaSet criteria, @Nonnull final Credential credential,
+ @Nonnull final List<String> dataEncryptionAlgorithms, @Nonnull final List<String> keyTransportAlgorithms) {
+
+ if (!KeyAgreementSupport.supportsKeyAgreement(credential) ) {
+ log.trace("Specified Credential does not support key agreement");
+ return false;
+ }
+
+ log.debug("Processing key agreement for credential with key type: {}",
+ credential.getPublicKey().getAlgorithm());
+
+ final KeyAgreementEncryptionConfiguration config = getEffectiveKeyAgreementConfiguration(criteria, credential);
+ if (config == null) {
+ log.warn("Unable to get effective KeyAgreementEncryptionConfiguration for credential with key type: {}",
+ credential.getPublicKey().getAlgorithm());
+ return false;
+ }
+
+ final String dataEncryptionAlgorithm = dataEncryptionAlgorithms.stream()
+ .filter(AlgorithmSupport::isBlockEncryption)
+ .findFirst().orElse(null);
+ if (dataEncryptionAlgorithm == null) {
+ log.warn("Unable to resolve data encryption algorithm for key agreement, skipping");
+ return false;
+ }
+
+ final String keyTransportAlgorithm = keyTransportAlgorithms.stream()
+ .filter(AlgorithmSupport::isSymmetricKeyWrap)
+ .findFirst().orElse(null);
+
+ final String keyAlgorithm = keyTransportAlgorithm != null ? keyTransportAlgorithm : dataEncryptionAlgorithm;
+
+ final KeyAgreementParameters parameters = new KeyAgreementParameters(config.getParameters(), true);
+ try {
+ parameters.initializeAll();
+ parameters.forEach(p -> {
+ log.debug("Saw KeyAgreementParameter of type: {}", p.getClass().getName());
+ });
+ } catch (final KeyAgreementException e) {
+ log.warn("Fatal error initing configured parameters for key agreement", e);
+ return false;
+ }
+
+ try {
+ final KeyAgreementProcessor processor = KeyAgreementSupport.getProcessor(config.getAlgorithm());
+
+ final KeyAgreementCredential agreementCredential = processor.execute(credential, keyAlgorithm, parameters);
+
+ params.setDataEncryptionAlgorithm(dataEncryptionAlgorithm);
+
+ if (keyTransportAlgorithm != null) {
+ params.setKeyTransportEncryptionAlgorithm(keyTransportAlgorithm);
+ params.setKeyTransportEncryptionCredential(agreementCredential);
+ } else {
+ params.setDataEncryptionCredential(agreementCredential);
+ }
+
+ processDataEncryptionCredentialAutoGeneration(params);
+
+ log.debug("Successfully processed key agreement for credential with key type: {}",
+ credential.getPublicKey().getAlgorithm());
+
+ return true;
+ } catch (final KeyAgreementException e) {
+ log.warn("Fatal error processing key agreement for credential", e);
+ return false;
+ }
+
+ }
+
+ /**
+ * Get the effective {@link KeyAgreementEncryptionConfiguration} to use with the specified credential.
+ *
+ * @param criteria the criteria
+ * @param credential the credential to evaluate
+ * @return the key agreement configuration for the credential, or null if could not be resolved
+ */
+ @Nullable protected KeyAgreementEncryptionConfiguration getEffectiveKeyAgreementConfiguration(
+ @Nonnull final CriteriaSet criteria, @Nonnull final Credential credential) {
+
+ final String keyType = credential.getPublicKey().getAlgorithm();
+
+ final KeyAgreementEncryptionConfiguration config = new KeyAgreementEncryptionConfiguration();
+
+ final List<EncryptionConfiguration> encConfigs = criteria.get(EncryptionConfigurationCriterion.class)
+ .getConfigurations();
+
+ config.setAlgorithm(
+ encConfigs.stream()
+ .map(c -> c.getKeyAgreementConfigurations().get(keyType))
+ .filter(Objects::nonNull)
+ .map(KeyAgreementEncryptionConfiguration::getAlgorithm)
+ .filter(Objects::nonNull)
+ .findFirst().orElse(null)
+ );
+
+ config.setParameters(
+ encConfigs.stream()
+ .map(c -> c.getKeyAgreementConfigurations().get(keyType))
+ .filter(Objects::nonNull)
+ .map(KeyAgreementEncryptionConfiguration::getParameters)
+ .filter(Objects::nonNull)
+ .findFirst().orElse(Collections.emptySet())
+ );
+
+ if (config.getAlgorithm() == null) {
+ log.warn("Failed to resolve a key agreement algorithm for key type: {}", keyType);
+ return null;
+ }
+
+ return config;
+ }
+
/**
* Resolve and populate an instance of {@link RSAOAEPParameters}, if appropriate for the selected
* key transport encryption algorithm.
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicSignatureSigningParametersResolver.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicSignatureSigningParametersResolver.java
index 77eeb4ee8..5cebfe214 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicSignatureSigningParametersResolver.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/impl/BasicSignatureSigningParametersResolver.java
@@ -154,7 +154,8 @@ public class BasicSignatureSigningParametersResolver
log.debug("\tSignature algorithm URI: {}", params.getSignatureAlgorithm());
- log.debug("\tSignature KeyInfoGenerator: {}", params.getKeyInfoGenerator() != null ? "present" : "null");
+ log.debug("\tSignature KeyInfoGenerator: {}", params.getKeyInfoGenerator() != null ?
+ params.getKeyInfoGenerator().getClass().getName() : "null");
log.debug("\tReference digest method algorithm URI: {}", params.getSignatureReferenceDigestMethod());
log.debug("\tReference canonicalization algorithm URI: {}",
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/impl/BasicEncryptionParametersResolverTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/impl/BasicEncryptionParametersResolverTest.java
index a4ba046e5..52d3ff865 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/impl/BasicEncryptionParametersResolverTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/impl/BasicEncryptionParametersResolverTest.java
@@ -19,15 +19,18 @@ package org.opensaml.xmlsec.impl;
import static org.testng.Assert.*;
+import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
+import java.security.spec.ECGenParameterSpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import javax.annotation.Nullable;
import javax.crypto.SecretKey;
@@ -43,14 +46,21 @@ import org.opensaml.security.crypto.JCAConstants;
import org.opensaml.security.crypto.KeySupport;
import org.opensaml.xmlsec.EncryptionParameters;
import org.opensaml.xmlsec.KeyTransportAlgorithmPredicate;
+import org.opensaml.xmlsec.agreement.KeyAgreementCredential;
import org.opensaml.xmlsec.criterion.EncryptionConfigurationCriterion;
import org.opensaml.xmlsec.criterion.KeyInfoGenerationProfileCriterion;
+import org.opensaml.xmlsec.derivation.impl.ConcatKDF;
+import org.opensaml.xmlsec.derivation.impl.PBKDF2;
import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
+import org.opensaml.xmlsec.encryption.support.KeyAgreementEncryptionConfiguration;
import org.opensaml.xmlsec.encryption.support.RSAOAEPParameters;
import org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;
import org.opensaml.xmlsec.keyinfo.impl.BasicKeyInfoGeneratorFactory;
+import org.opensaml.xmlsec.keyinfo.impl.KeyAgreementKeyInfoGeneratorFactory;
+import org.opensaml.xmlsec.keyinfo.impl.KeyAgreementKeyInfoGeneratorFactory.KeyAgreementKeyInfoGenerator;
import org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;
import org.opensaml.xmlsec.signature.support.SignatureConstants;
+import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -68,9 +78,10 @@ public class BasicEncryptionParametersResolverTest extends XMLObjectBaseTestCase
private BasicEncryptionConfiguration config1, config2, config3;
- private Credential rsaCred1, aes128Cred1, aes192Cred1, aes256Cred1;
+ private Credential rsaCred1, ecCred1, aes128Cred1, aes192Cred1, aes256Cred1;
private String rsaCred1KeyName = "RSACred1";
+ private String ecCred1KeyName = "ECCred1";
private String aes128Cred1KeyName = "AES128Cred1";
private String aes192Cred1KeyName = "AES192Cred1";
private String aes256Cred1KeyName = "AES256Cred1";
@@ -84,11 +95,15 @@ public class BasicEncryptionParametersResolverTest extends XMLObjectBaseTestCase
private NamedKeyInfoGeneratorManager defaultDataEncryptionKeyInfoGeneratorManager = new NamedKeyInfoGeneratorManager();
@BeforeClass
- public void buildCredentials() throws NoSuchAlgorithmException, NoSuchProviderException {
+ public void buildCredentials() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
KeyPair rsaKeyPair = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_RSA, 2048, null);
rsaCred1 = CredentialSupport.getSimpleCredential(rsaKeyPair.getPublic(), rsaKeyPair.getPrivate());
rsaCred1.getKeyNames().add(rsaCred1KeyName);
+ KeyPair ecKeyPair = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_EC, new ECGenParameterSpec("secp256r1"), null);
+ ecCred1 = CredentialSupport.getSimpleCredential(ecKeyPair.getPublic(), ecKeyPair.getPrivate());
+ ecCred1.getKeyNames().add(ecCred1KeyName);
+
SecretKey aes128Key = KeySupport.generateKey(JCAConstants.KEY_ALGO_AES, 128, null);
aes128Cred1 = CredentialSupport.getSimpleCredential(aes128Key);
aes128Cred1.getKeyNames().add(aes128Cred1KeyName);
@@ -130,25 +145,38 @@ public class BasicEncryptionParametersResolverTest extends XMLObjectBaseTestCase
EncryptionConstants.ALGO_ID_KEYWRAP_TRIPLEDES
));
+ KeyAgreementEncryptionConfiguration ecConfig = new KeyAgreementEncryptionConfiguration();
+ ecConfig.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+ ConcatKDF concatKDF = new ConcatKDF();
+ concatKDF.setAlgorithmID("00");
+ concatKDF.setPartyUInfo("00");
+ concatKDF.setPartyVInfo("00");
+ ecConfig.setParameters(Set.of(concatKDF));
+ config3.setKeyAgreementConfigurations(Map.of("EC", ecConfig));
+
BasicKeyInfoGeneratorFactory basicFactory1 = new BasicKeyInfoGeneratorFactory();
X509KeyInfoGeneratorFactory x509Factory1 = new X509KeyInfoGeneratorFactory();
+ KeyAgreementKeyInfoGeneratorFactory kaFactory1 = new KeyAgreementKeyInfoGeneratorFactory();
defaultKeyTransportKeyInfoGeneratorManager = new NamedKeyInfoGeneratorManager();
defaultKeyTransportKeyInfoGeneratorManager.registerDefaultFactory(basicFactory1);
defaultKeyTransportKeyInfoGeneratorManager.registerDefaultFactory(x509Factory1);
+ defaultKeyTransportKeyInfoGeneratorManager.registerDefaultFactory(kaFactory1);
config3.setKeyTransportKeyInfoGeneratorManager(defaultKeyTransportKeyInfoGeneratorManager);
BasicKeyInfoGeneratorFactory basicFactory2 = new BasicKeyInfoGeneratorFactory();
X509KeyInfoGeneratorFactory x509Factory2 = new X509KeyInfoGeneratorFactory();
+ KeyAgreementKeyInfoGeneratorFactory kaFactory2 = new KeyAgreementKeyInfoGeneratorFactory();
defaultDataEncryptionKeyInfoGeneratorManager = new NamedKeyInfoGeneratorManager();
defaultDataEncryptionKeyInfoGeneratorManager.registerDefaultFactory(basicFactory2);
defaultDataEncryptionKeyInfoGeneratorManager.registerDefaultFactory(x509Factory2);
+ defaultDataEncryptionKeyInfoGeneratorManager.registerDefaultFactory(kaFactory2);
config3.setDataKeyInfoGeneratorManager(defaultDataEncryptionKeyInfoGeneratorManager);
criterion = new EncryptionConfigurationCriterion(config1, config2, config3);
criteriaSet = new CriteriaSet(criterion);
}
-
+
@Test
public void testBasicRSA() throws ResolverException {
config1.setKeyTransportEncryptionCredentials(Collections.singletonList(rsaCred1));
@@ -295,6 +323,172 @@ public class BasicEncryptionParametersResolverTest extends XMLObjectBaseTestCase
assertTrue(params.getRSAOAEPParameters().isEmpty());
}
+ @Test
+ public void testECDHWithDirectDataEncryption() throws ResolverException {
+ config1.setDataEncryptionCredentials(Collections.singletonList(ecCred1));
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ assertNotNull(params);
+ assertNull(params.getKeyTransportEncryptionCredential());
+ assertNull(params.getKeyTransportEncryptionAlgorithm());
+ assertNull(params.getKeyTransportKeyInfoGenerator());
+
+ assertNotNull(params.getDataEncryptionCredential());
+ assertTrue(KeyAgreementCredential.class.isInstance(params.getDataEncryptionCredential()));
+ assertNotNull(params.getDataEncryptionCredential().getSecretKey());
+ assertEquals(params.getDataEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ assertEquals(KeySupport.getKeyLength(params.getDataEncryptionCredential().getSecretKey()), Integer.valueOf(128));
+ assertEquals(params.getDataEncryptionAlgorithm(), defaultAES128DataAlgo);
+ assertNotNull(params.getDataKeyInfoGenerator());
+ assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getDataKeyInfoGenerator()));
+ }
+
+ @Test
+ public void testECDHWithDirectDataEncryptionAndAlgorithmOverrides() throws ResolverException {
+ config1.setDataEncryptionCredentials(Collections.singletonList(ecCred1));
+
+ config2.setDataEncryptionAlgorithms(Collections.singletonList(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256));
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ assertNotNull(params);
+ assertNull(params.getKeyTransportEncryptionCredential());
+ assertNull(params.getKeyTransportEncryptionAlgorithm());
+ assertNull(params.getKeyTransportKeyInfoGenerator());
+
+ assertNotNull(params.getDataEncryptionCredential());
+ assertTrue(KeyAgreementCredential.class.isInstance(params.getDataEncryptionCredential()));
+ assertNotNull(params.getDataEncryptionCredential().getSecretKey());
+ assertEquals(params.getDataEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ assertEquals(KeySupport.getKeyLength(params.getDataEncryptionCredential().getSecretKey()), Integer.valueOf(256));
+ assertEquals(params.getDataEncryptionAlgorithm(), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256);
+ assertNotNull(params.getDataKeyInfoGenerator());
+ assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getDataKeyInfoGenerator()));
+ }
+
+ @Test
+ public void testECDHWithKeyWrap() throws ResolverException {
+ config1.setKeyTransportEncryptionCredentials(Collections.singletonList(ecCred1));
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ assertNotNull(params.getKeyTransportEncryptionCredential());
+ assertTrue(KeyAgreementCredential.class.isInstance(params.getKeyTransportEncryptionCredential()));
+ assertNotNull(params.getKeyTransportEncryptionCredential().getSecretKey());
+ assertEquals(params.getKeyTransportEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ assertEquals(KeySupport.getKeyLength(params.getKeyTransportEncryptionCredential().getSecretKey()), Integer.valueOf(128));
+ assertEquals(params.getKeyTransportEncryptionAlgorithm(), EncryptionConstants.ALGO_ID_KEYWRAP_AES128);
+ assertNotNull(params.getKeyTransportKeyInfoGenerator());
+ assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getKeyTransportKeyInfoGenerator()));
+
+ assertNull(params.getDataEncryptionCredential());
+ assertEquals(params.getDataEncryptionAlgorithm(), defaultAES128DataAlgo);
+ assertNull(params.getDataKeyInfoGenerator());
+ }
+
+ @Test
+ public void testECDHWithKeyWrapAndAlgorithmOverrides() throws ResolverException {
+ config1.setKeyTransportEncryptionCredentials(Collections.singletonList(ecCred1));
+
+ config2.setKeyTransportEncryptionAlgorithms(Collections.singletonList(EncryptionConstants.ALGO_ID_KEYWRAP_AES256));
+ config2.setDataEncryptionAlgorithms(Collections.singletonList(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES192));
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ assertNotNull(params.getKeyTransportEncryptionCredential());
+ assertTrue(KeyAgreementCredential.class.isInstance(params.getKeyTransportEncryptionCredential()));
+ assertNotNull(params.getKeyTransportEncryptionCredential().getSecretKey());
+ assertEquals(params.getKeyTransportEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ assertEquals(KeySupport.getKeyLength(params.getKeyTransportEncryptionCredential().getSecretKey()), Integer.valueOf(256));
+ assertEquals(params.getKeyTransportEncryptionAlgorithm(), EncryptionConstants.ALGO_ID_KEYWRAP_AES256);
+ assertNotNull(params.getKeyTransportKeyInfoGenerator());
+ assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getKeyTransportKeyInfoGenerator()));
+
+ assertNull(params.getDataEncryptionCredential());
+ assertEquals(params.getDataEncryptionAlgorithm(), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES192);
+ assertNull(params.getDataKeyInfoGenerator());
+ }
+
+ @Test
+ public void testECDHWithKeyWrapAndGeneratedDataCredential() throws ResolverException {
+ config1.setKeyTransportEncryptionCredentials(Collections.singletonList(ecCred1));
+
+ resolver.setAutoGenerateDataEncryptionCredential(true);
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ assertNotNull(params.getKeyTransportEncryptionCredential());
+ assertTrue(KeyAgreementCredential.class.isInstance(params.getKeyTransportEncryptionCredential()));
+ assertNotNull(params.getKeyTransportEncryptionCredential().getSecretKey());
+ assertEquals(params.getKeyTransportEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ assertEquals(KeySupport.getKeyLength(params.getKeyTransportEncryptionCredential().getSecretKey()), Integer.valueOf(128));
+ assertEquals(params.getKeyTransportEncryptionAlgorithm(), EncryptionConstants.ALGO_ID_KEYWRAP_AES128);
+ assertNotNull(params.getKeyTransportKeyInfoGenerator());
+ assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getKeyTransportKeyInfoGenerator()));
+
+ assertNotNull(params.getDataEncryptionCredential());
+ assertNotNull(params.getDataEncryptionCredential().getSecretKey());
+ assertEquals(params.getDataEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ assertEquals(KeySupport.getKeyLength(params.getDataEncryptionCredential().getSecretKey()), Integer.valueOf(128));
+ assertEquals(params.getDataEncryptionAlgorithm(), defaultAES128DataAlgo);
+ assertNotNull(params.getDataKeyInfoGenerator());
+ }
+
+ @Test
+ public void testECDHWithKDFOverride() throws ResolverException {
+ config1.setDataEncryptionCredentials(Collections.singletonList(ecCred1));
+
+ KeyAgreementEncryptionConfiguration ecConfig = new KeyAgreementEncryptionConfiguration();
+ PBKDF2 kdf = new PBKDF2();
+ ecConfig.setParameters(Set.of(kdf));
+ config2.setKeyAgreementConfigurations(Map.of("EC", ecConfig));
+
+ EncryptionParameters params = resolver.resolveSingle(criteriaSet);
+
+ assertNotNull(params);
+ assertNull(params.getKeyTransportEncryptionCredential());
+ assertNull(params.getKeyTransportEncryptionAlgorithm());
+ assertNull(params.getKeyTransportKeyInfoGenerator());
+
+ assertNotNull(params.getDataEncryptionCredential());
+ assertTrue(KeyAgreementCredential.class.isInstance(params.getDataEncryptionCredential()));
+ assertNotNull(params.getDataEncryptionCredential().getSecretKey());
+ assertEquals(params.getDataEncryptionCredential().getSecretKey().getAlgorithm(), "AES");
+ assertEquals(KeySupport.getKeyLength(params.getDataEncryptionCredential().getSecretKey()), Integer.valueOf(128));
+ assertEquals(params.getDataEncryptionAlgorithm(), defaultAES128DataAlgo);
+ assertNotNull(params.getDataKeyInfoGenerator());
+ assertTrue(KeyAgreementKeyInfoGenerator.class.isInstance(params.getDataKeyInfoGenerator()));
+
+ KeyAgreementCredential kaCred = KeyAgreementCredential.class.cast(params.getDataEncryptionCredential());
+ assertEquals(kaCred.getParameters().size(), 1);
+ assertTrue(kaCred.getParameters().contains(PBKDF2.class));
+ }
+
+ @Test
+ public void testGetEffectiveKeyAgreementConfiguration() {
+ KeyAgreementEncryptionConfiguration ecConfig1 = new KeyAgreementEncryptionConfiguration();
+ ecConfig1.setParameters(Set.of(new ConcatKDF()));
+ config1.setKeyAgreementConfigurations(Map.of("EC", ecConfig1));
+
+ KeyAgreementEncryptionConfiguration ecConfig2 = new KeyAgreementEncryptionConfiguration();
+ ecConfig2.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+ ecConfig2.setParameters(Set.of(new PBKDF2()));
+ config2.setKeyAgreementConfigurations(Map.of("EC", ecConfig2));
+
+ KeyAgreementEncryptionConfiguration ecConfig3 = new KeyAgreementEncryptionConfiguration();
+ ecConfig3.setAlgorithm("SomeAlgo");
+ ecConfig3.setParameters(Set.of(new ConcatKDF()));
+ config3.setKeyAgreementConfigurations(Map.of("EC", ecConfig3));
+
+ KeyAgreementEncryptionConfiguration config = resolver.getEffectiveKeyAgreementConfiguration(criteriaSet, ecCred1);
+
+ Assert.assertEquals(config.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+ Assert.assertEquals(config.getParameters().size(), 1);
+ Assert.assertTrue(ConcatKDF.class.isInstance(config.getParameters().iterator().next()));
+ Assert.assertSame(config.getParameters().iterator().next(), ecConfig1.getParameters().iterator().next());
+ }
+
@Test
public void testAES128KeyWrap() throws ResolverException {
config1.setKeyTransportEncryptionCredentials(Collections.singletonList(aes128Cred1));
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list