[java-opensaml] 12/19: KeyInfo support for KeyAgreementCredential.
Brent Putman
putmanb at georgetown.edu
Mon Mar 1 05:56:49 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=c416c456f62dba05a19ac1fbe2f1cdd5e0c1e82d
commit c416c456f62dba05a19ac1fbe2f1cdd5e0c1e82d
Author: Brent Putman <putmanb at georgetown.edu>
AuthorDate: Tue Jan 5 17:34:15 2021 -0500
KeyInfo support for KeyAgreementCredential.
---
.../xmlsec/agreement/KeyAgreementParameters.java | 14 +-
.../agreement/KeyAgreementProcessorRegistry.java | 98 ++++++
.../xmlsec/agreement/KeyAgreementSupport.java | 41 +++
...alKeyAgreementProcessorRegistryInitializer.java | 55 +++
.../keyinfo/KeyInfoCredentialResolutionMode.java | 133 ++++++++
.../xmlsec/keyinfo/KeyInfoGeneratorFactory.java | 15 +
.../services/org.opensaml.core.config.Initializer | 3 +-
.../KeyAgreementProcessorRegistryTest.java | 95 ++++++
.../xmlsec/agreement/impl/DigestMethod.java | 71 +++-
.../opensaml/xmlsec/agreement/impl/KANonce.java | 68 +++-
.../impl/KeyAgreementParameterParser.java | 51 +++
.../impl/KeyAgreementParametersParser.java | 104 ++++++
.../DefaultSecurityConfigurationBootstrap.java | 15 +-
.../opensaml/xmlsec/derivation/impl/ConcatKDF.java | 135 ++++++--
.../opensaml/xmlsec/derivation/impl/PBKDF2.java | 134 +++++++-
.../keyinfo/impl/BasicKeyInfoGeneratorFactory.java | 85 ++++-
.../impl/KeyAgreementKeyInfoGeneratorFactory.java | 379 +++++++++++++++++++++
.../impl/LocalKeyInfoCredentialResolver.java | 47 ++-
.../keyinfo/impl/X509KeyInfoGeneratorFactory.java | 27 +-
.../provider/AgreementMethodKeyInfoProvider.java | 239 +++++++++++++
...opensaml.xmlsec.agreement.KeyAgreementProcessor | 1 +
...lsec.agreement.impl.KeyAgreementParameterParser | 4 +
.../xmlsec/agreement/impl/DigestMethodTest.java | 25 +-
.../xmlsec/agreement/impl/KANonceTest.java | 25 +-
.../impl/KeyAgreementParametersParserTest.java | 259 ++++++++++++++
.../GlobalKeyAgreementProcessorRegistryTest.java | 48 +++
.../xmlsec/derivation/impl/ConcatKDFTest.java | 110 +++++-
.../xmlsec/derivation/impl/PBKDF2Test.java | 121 ++++++-
.../xmlsec/keyinfo/impl/AgreementMethodTest.java | 308 +++++++++++++++++
.../keyinfo/impl/BasicKeyInfoGeneratorTest.java | 43 ++-
.../impl/KeyAgreementKeyInfoGeneratorTest.java | 272 +++++++++++++++
.../impl/LocalKeyInfoCredentialResolverTest.java | 55 +++
.../keyinfo/impl/X509KeyInfoGeneratorTest.java | 38 +++
.../src/test/resources/logback-test.xml | 4 +
34 files changed, 3013 insertions(+), 109 deletions(-)
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementParameters.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementParameters.java
index caa1123d5..05fe2807e 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementParameters.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementParameters.java
@@ -68,12 +68,20 @@ public class KeyAgreementParameters extends ClassIndexedSet<KeyAgreementParamete
/**
* A convenience method for initializing all parameters which are initializable.
*
- * @throws ComponentInitializationException
+ * @throws KeyAgreementException
*/
- public void initializeAll() throws ComponentInitializationException {
+ public void initializeAll() throws KeyAgreementException {
for (final KeyAgreementParameter param : this) {
if (InitializableComponent.class.isInstance(param)) {
- InitializableComponent.class.cast(param).initialize();
+ final InitializableComponent component = InitializableComponent.class.cast(param);
+ if (!component.isInitialized()) {
+ try {
+ component.initialize();
+ } catch (final ComponentInitializationException e) {
+ throw new KeyAgreementException("Error initializing KeyAgreementParameter: "
+ + component.getClass().getName(), e);
+ }
+ }
}
}
}
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementProcessorRegistry.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementProcessorRegistry.java
new file mode 100644
index 000000000..8418b0040
--- /dev/null
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementProcessorRegistry.java
@@ -0,0 +1,98 @@
+/*
+ * 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.agreement;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * A component which provides access to registered instances of {@link KeyAgreementProcessor}.
+ */
+public class KeyAgreementProcessorRegistry {
+
+ /** The registered processors. */
+ @Nonnull private Map<String, KeyAgreementProcessor> processors;
+
+ /** Constructor. */
+ public KeyAgreementProcessorRegistry() {
+ processors = new HashMap<>();
+ }
+
+ /**
+ * Register a processor.
+ *
+ * <p>
+ * Any existing processor registered for the given algorithm will be overwritten.
+ * </p>
+ *
+ * @param processor the processor to register
+ */
+ public void register(@Nonnull final KeyAgreementProcessor processor) {
+ Constraint.isNotNull(processor, "KeyAgreementProcessor was null");
+ Constraint.isNotNull(processor.getAlgorithm(), "KeyAgreementProcessor algorithm was null");
+ processors.put(processor.getAlgorithm(), processor);
+ }
+
+ /**
+ * Deregister a processor.
+ *
+ * @param algorithm the algorithm of the processor to deregister
+ */
+ public void deregister(@Nonnull final String algorithm) {
+ final String alg = Constraint.isNotNull(StringSupport.trimOrNull(algorithm),
+ "KeyAgreementProcessor algorithm was null or empty");
+ processors.remove(alg);
+ }
+
+ /**
+ * Clear all registered processors.
+ */
+ public void clear() {
+ processors.clear();
+ }
+
+ /**
+ * Get the set of all registered algorithms.
+ *
+ * @return the set of registered algorithms
+ */
+ @Nonnull public Set<String> getRegisteredAlgorithms() {
+ return Set.copyOf(processors.keySet());
+ }
+
+ /**
+ * Get the processor registered for the specified algorithm.
+ *
+ * @param algorithm the processor algorithm
+ *
+ * @return the processor registered for that algorithm
+ */
+ @Nullable public KeyAgreementProcessor getProcessor(@Nonnull final String algorithm) {
+ final String alg = Constraint.isNotNull(StringSupport.trimOrNull(algorithm),
+ "KeyAgreementProcessor algorithm was null or empty");
+ return processors.get(alg);
+ }
+
+}
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
new file mode 100644
index 000000000..39a4c4a17
--- /dev/null
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementSupport.java
@@ -0,0 +1,41 @@
+/*
+ * 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.agreement;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.core.config.ConfigurationService;
+
+/**
+ * Support for key agreement operations.
+ */
+public final class KeyAgreementSupport {
+
+ /** Constructor. */
+ private KeyAgreementSupport() {}
+
+
+ /**
+ * Get the global {@link KeyAgreementProcessorRegistry} instance.
+ *
+ * @return the global procesor registry, or null if nothing registered
+ */
+ @Nullable public static KeyAgreementProcessorRegistry getGlobalProcessorRegistry() {
+ return ConfigurationService.get(KeyAgreementProcessorRegistry.class);
+ }
+}
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/config/GlobalKeyAgreementProcessorRegistryInitializer.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/config/GlobalKeyAgreementProcessorRegistryInitializer.java
new file mode 100644
index 000000000..295fa1f5d
--- /dev/null
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/config/GlobalKeyAgreementProcessorRegistryInitializer.java
@@ -0,0 +1,55 @@
+/*
+ * 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.config;
+
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+import org.opensaml.core.config.ConfigurationService;
+import org.opensaml.core.config.InitializationException;
+import org.opensaml.core.config.Initializer;
+import org.opensaml.xmlsec.agreement.KeyAgreementProcessor;
+import org.opensaml.xmlsec.agreement.KeyAgreementProcessorRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * OpenSAML {@link Initializer} implementation for key agreement processors.
+ */
+public class GlobalKeyAgreementProcessorRegistryInitializer implements Initializer {
+
+ /** Logger. */
+ private Logger log = LoggerFactory.getLogger(GlobalKeyAgreementProcessorRegistryInitializer.class);
+
+ /** {@inheritDoc} */
+ public void init() throws InitializationException {
+ final KeyAgreementProcessorRegistry registry = new KeyAgreementProcessorRegistry();
+
+ final ServiceLoader<KeyAgreementProcessor> descriptorsLoader = ServiceLoader.load(KeyAgreementProcessor.class);
+ final Iterator<KeyAgreementProcessor> iter = descriptorsLoader.iterator();
+ while (iter.hasNext()) {
+ final KeyAgreementProcessor processor = iter.next();
+ log.debug("Registering KeyAgreementProcessor for algorithm '{}': {}",
+ processor.getAlgorithm(), processor.getClass().getName());
+ registry.register(processor);
+ }
+
+ ConfigurationService.register(KeyAgreementProcessorRegistry.class, registry);
+ }
+
+}
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/keyinfo/KeyInfoCredentialResolutionMode.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/keyinfo/KeyInfoCredentialResolutionMode.java
new file mode 100644
index 000000000..310bf5ba5
--- /dev/null
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/keyinfo/KeyInfoCredentialResolutionMode.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.xmlsec.keyinfo;
+
+import java.security.PrivateKey;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.crypto.SecretKey;
+
+import org.apache.xml.security.encryption.AgreementMethod;
+import org.opensaml.xmlsec.signature.KeyInfo;
+
+import com.google.common.base.MoreObjects;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.Criterion;
+
+/**
+ * An implementation of {@link Criterion} which specifies criteria
+ * to a {@link KeyInfoCredentialResolver} about whether to resolve
+ * public credentials, local credentials, or both.
+ *
+ * <p>
+ * A local credential is defined as one carrying either a {@link PrivateKey} or a {@link SecretKey}.
+ * </p>
+ *
+ * <p>
+ * This criterion is used with resolver implementations which are
+ * capable of local credential resolution using the (usually public) information available
+ * directly within {@link KeyInfo}.
+ * </p>
+ *
+ * <p>
+ * If <code>PUBLIC</code> then resolver implementations may skip
+ * local resolution and return any credentials extracted directly from
+ * {@link KeyInfo}. This mode does not mean that a local credential will
+ * absolutely not be returned, merely that the resolver is not obligated to
+ * do so. For example, an {@link AgreementMethod} might produce a credential
+ * containing a {@link SecretKey}, and this would be returned in this mode.
+ * </p>
+ *
+ * <p>
+ * If <code>LOCAL</code> then resolver implementations which are capable
+ * of resolving local credentials should attempt that local credential resolution,
+ * and only those local credentials should be returned.
+ * </p>
+ *
+ * <p>
+ * If <code>BOTH</code> then local credential resolution should be attempted
+ * as in <code>LOCAL</code>. If a local credential based on a given public credential's info is
+ * resolved it will be returned, otherwise the public credential itself will be returned.
+ * </p>
+ */
+public final class KeyInfoCredentialResolutionMode implements Criterion {
+
+ /** Credential resolution mode. */
+ public enum Mode {
+ /** Public credential mode. */
+ PUBLIC,
+ /** Local credential mode. */
+ LOCAL,
+ /** Public and local credential mode. */
+ BOTH
+ }
+
+ /** Resolution mode. */
+ private Mode mode;
+
+
+ /**
+ * Constructor.
+ *
+ * @param resolutionMode the resolution mode
+ */
+ public KeyInfoCredentialResolutionMode(@Nonnull final Mode resolutionMode) {
+ mode = Constraint.isNotNull(resolutionMode, "Resolution mode was null");
+ }
+
+ /**
+ * Gets the resolution mode.
+ *
+ * @return the resolution mode
+ */
+ @Nullable public Mode getMode() {
+ return mode;
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("mode", mode)
+ .toString();
+ }
+
+ /** {@inheritDoc} */
+ public int hashCode() {
+ return mode.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null) {
+ return false;
+ }
+
+ if (obj instanceof KeyInfoCredentialResolutionMode) {
+ return mode.equals(((KeyInfoCredentialResolutionMode) obj).mode);
+ }
+
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/keyinfo/KeyInfoGeneratorFactory.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/keyinfo/KeyInfoGeneratorFactory.java
index 6d8ad7378..fcb31d361 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/keyinfo/KeyInfoGeneratorFactory.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/keyinfo/KeyInfoGeneratorFactory.java
@@ -18,8 +18,10 @@
package org.opensaml.xmlsec.keyinfo;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.opensaml.security.credential.Credential;
+import org.opensaml.xmlsec.signature.KeyInfo;
/**
* Interface for factories which produce {@link KeyInfoGenerator} instances.
@@ -33,6 +35,19 @@ public interface KeyInfoGeneratorFactory {
*/
@Nonnull public KeyInfoGenerator newInstance();
+ /**
+ * Get a new instance of the generator type produced by the factory, and which generates
+ * {@link KeyInfo} instances of the specified type, for example {@link OriginatorKeyInfo}
+ * or {@link RecipientKeyInfo}.
+ *
+ * @param type the type of element to produce. Null is interpreted as a standard {@link KeyInfo}.
+ *
+ * @return a new KeyInfoGenerator instance
+ */
+ @Nonnull public default KeyInfoGenerator newInstance(@Nullable final Class<? extends KeyInfo> type) {
+ throw new UnsupportedOperationException("Method not implemented");
+ }
+
/**
* Check whether the generators produced by this factory can handle the specified credential.
*
diff --git a/opensaml-xmlsec-api/src/main/resources/META-INF/services/org.opensaml.core.config.Initializer b/opensaml-xmlsec-api/src/main/resources/META-INF/services/org.opensaml.core.config.Initializer
index 07932f47f..eeb295fcb 100644
--- a/opensaml-xmlsec-api/src/main/resources/META-INF/services/org.opensaml.core.config.Initializer
+++ b/opensaml-xmlsec-api/src/main/resources/META-INF/services/org.opensaml.core.config.Initializer
@@ -1,2 +1,3 @@
+org.opensaml.xmlsec.config.DecryptionParserPoolInitializer
org.opensaml.xmlsec.config.GlobalAlgorithmRegistryInitializer
-org.opensaml.xmlsec.config.DecryptionParserPoolInitializer
\ No newline at end of file
+org.opensaml.xmlsec.config.GlobalKeyAgreementProcessorRegistryInitializer
diff --git a/opensaml-xmlsec-api/src/test/java/org/opensaml/xmlsec/agreement/KeyAgreementProcessorRegistryTest.java b/opensaml-xmlsec-api/src/test/java/org/opensaml/xmlsec/agreement/KeyAgreementProcessorRegistryTest.java
new file mode 100644
index 000000000..f71b6b67d
--- /dev/null
+++ b/opensaml-xmlsec-api/src/test/java/org/opensaml/xmlsec/agreement/KeyAgreementProcessorRegistryTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.agreement;
+
+import java.util.Set;
+
+import org.opensaml.security.credential.Credential;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ *
+ */
+public class KeyAgreementProcessorRegistryTest {
+
+ @Test
+ public void basic() {
+ KeyAgreementProcessorRegistry registry = new KeyAgreementProcessorRegistry();
+
+ Assert.assertEquals(registry.getRegisteredAlgorithms().size(), 0);
+
+ KeyAgreementProcessor foo1 = new MockProcessorFoo();
+ KeyAgreementProcessor foo2 = new MockProcessorFoo();
+ KeyAgreementProcessor bar = new MockProcessorBar();
+
+ registry.register(foo1);
+ Assert.assertEquals(registry.getRegisteredAlgorithms().size(), 1);
+ Assert.assertEquals(registry.getRegisteredAlgorithms(), Set.of("urn:test:KeyAgreementProcessor:Foo"));
+ Assert.assertSame(registry.getProcessor("urn:test:KeyAgreementProcessor:Foo"), foo1);
+
+ registry.register(foo2);
+ Assert.assertEquals(registry.getRegisteredAlgorithms().size(), 1);
+ Assert.assertEquals(registry.getRegisteredAlgorithms(), Set.of("urn:test:KeyAgreementProcessor:Foo"));
+ Assert.assertSame(registry.getProcessor("urn:test:KeyAgreementProcessor:Foo"), foo2);
+
+ registry.register(bar);
+ Assert.assertEquals(registry.getRegisteredAlgorithms().size(), 2);
+ Assert.assertEquals(registry.getRegisteredAlgorithms(), Set.of("urn:test:KeyAgreementProcessor:Foo", "urn:test:KeyAgreementProcessor:Bar"));
+ Assert.assertSame(registry.getProcessor("urn:test:KeyAgreementProcessor:Foo"), foo2);
+ Assert.assertSame(registry.getProcessor("urn:test:KeyAgreementProcessor:Bar"), bar);
+
+ registry.deregister("urn:test:KeyAgreementProcessor:Foo");
+ Assert.assertEquals(registry.getRegisteredAlgorithms().size(), 1);
+ Assert.assertEquals(registry.getRegisteredAlgorithms(), Set.of("urn:test:KeyAgreementProcessor:Bar"));
+ Assert.assertNull(registry.getProcessor("urn:test:KeyAgreementProcessor:Foo"));
+ Assert.assertSame(registry.getProcessor("urn:test:KeyAgreementProcessor:Bar"), bar);
+
+ registry.clear();
+ Assert.assertEquals(registry.getRegisteredAlgorithms().size(), 0);
+ Assert.assertEquals(registry.getRegisteredAlgorithms(), Set.of());
+ Assert.assertNull(registry.getProcessor("urn:test:KeyAgreementProcessor:Foo"));
+ Assert.assertNull(registry.getProcessor("urn:test:KeyAgreementProcessor:Bar"));
+ }
+
+ public static class MockProcessorFoo implements KeyAgreementProcessor {
+
+ /** {@inheritDoc} */
+ public String getAlgorithm() {
+ return "urn:test:KeyAgreementProcessor:Foo";
+ }
+
+ /** {@inheritDoc} */
+ public KeyAgreementCredential execute(Credential publicCredential, String keyAlgorithm, KeyAgreementParameters parameters) throws KeyAgreementException {
+ return null;
+ }
+ }
+
+ public static class MockProcessorBar implements KeyAgreementProcessor {
+
+ /** {@inheritDoc} */
+ public String getAlgorithm() {
+ return "urn:test:KeyAgreementProcessor:Bar";
+ }
+
+ /** {@inheritDoc} */
+ public KeyAgreementCredential execute(Credential publicCredential, String keyAlgorithm, KeyAgreementParameters parameters) throws KeyAgreementException {
+ return null;
+ }
+ }
+}
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/DigestMethod.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/DigestMethod.java
index d5a7334e6..2f4697c44 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/DigestMethod.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/DigestMethod.java
@@ -17,16 +17,20 @@
package org.opensaml.xmlsec.agreement.impl;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.core.xml.util.XMLObjectSupport;
import org.opensaml.xmlsec.agreement.CloneableKeyAgreementParameter;
+import org.opensaml.xmlsec.agreement.KeyAgreementException;
+import org.opensaml.xmlsec.agreement.KeyAgreementParameter;
import org.opensaml.xmlsec.agreement.XMLExpressableKeyAgreementParameter;
import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.primitive.StringSupport;
/**
@@ -64,16 +68,6 @@ public class DigestMethod extends AbstractInitializableComponent
algorithm = StringSupport.trimOrNull(newAlgorithm);
}
- /** {@inheritDoc} */
- public XMLObject buildXMLObject() {
- final org.opensaml.xmlsec.signature.DigestMethod digestMethod =
- (org.opensaml.xmlsec.signature.DigestMethod) XMLObjectSupport
- .buildXMLObject(org.opensaml.xmlsec.signature.DigestMethod.DEFAULT_ELEMENT_NAME);
-
- digestMethod.setAlgorithm(getAlgorithm());
- return digestMethod;
- }
-
/** {@inheritDoc} */
public DigestMethod clone() {
ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
@@ -85,4 +79,61 @@ public class DigestMethod extends AbstractInitializableComponent
}
}
+ /** {@inheritDoc} */
+ @Nonnull public XMLObject buildXMLObject() {
+ final org.opensaml.xmlsec.signature.DigestMethod digestMethod =
+ (org.opensaml.xmlsec.signature.DigestMethod) XMLObjectSupport
+ .buildXMLObject(org.opensaml.xmlsec.signature.DigestMethod.DEFAULT_ELEMENT_NAME);
+
+ digestMethod.setAlgorithm(getAlgorithm());
+ return digestMethod;
+ }
+
+ /**
+ * Create and initialize a new instance from the specified {@link XMLObject}.
+ *
+ * @param xmlObject the XML object
+ *
+ * @return new parameter instance
+ *
+ * @throws ComponentInitializationException
+ */
+ @Nonnull public static DigestMethod fromXMLObject(
+ @Nonnull final org.opensaml.xmlsec.signature.DigestMethod xmlObject)
+ throws ComponentInitializationException {
+ Constraint.isNotNull(xmlObject, "XMLObject was null");
+
+ final DigestMethod parameter = new DigestMethod();
+ parameter.setAlgorithm(xmlObject.getAlgorithm());
+ parameter.initialize();
+ return parameter;
+ }
+
+ /**
+ * Implementation of {@link KeyAgreementParameterParser}.
+ */
+ public static class Parser implements KeyAgreementParameterParser {
+
+ /** {@inheritDoc} */
+ public boolean handles(@Nonnull final XMLObject xmlObject) {
+ return org.opensaml.xmlsec.signature.DigestMethod.class.isInstance(xmlObject);
+ }
+
+ /** {@inheritDoc} */
+ public KeyAgreementParameter parse(@Nonnull final XMLObject xmlObject) throws KeyAgreementException {
+ // Sanity check
+ if (!handles(xmlObject)) {
+ throw new KeyAgreementException("This implementation does not handle: "
+ + xmlObject.getClass().getName());
+ }
+
+ try {
+ return fromXMLObject(org.opensaml.xmlsec.signature.DigestMethod.class.cast(xmlObject));
+ } catch (final ComponentInitializationException e) {
+ throw new KeyAgreementException(e);
+ }
+ }
+
+ }
+
}
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/KANonce.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/KANonce.java
index 838efbb47..73850aa01 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/KANonce.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/KANonce.java
@@ -17,16 +17,20 @@
package org.opensaml.xmlsec.agreement.impl;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.core.xml.util.XMLObjectSupport;
import org.opensaml.xmlsec.agreement.CloneableKeyAgreementParameter;
+import org.opensaml.xmlsec.agreement.KeyAgreementException;
+import org.opensaml.xmlsec.agreement.KeyAgreementParameter;
import org.opensaml.xmlsec.agreement.XMLExpressableKeyAgreementParameter;
import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.primitive.StringSupport;
/**
@@ -65,7 +69,18 @@ public class KANonce extends AbstractInitializableComponent
}
/** {@inheritDoc} */
- public XMLObject buildXMLObject() {
+ public KANonce clone() {
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+ try {
+ return (KANonce ) super.clone();
+ } catch (final CloneNotSupportedException e) {
+ // We know we are, so this will never happen
+ return null;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Nonnull public XMLObject buildXMLObject() {
final org.opensaml.xmlsec.encryption.KANonce nonce =
(org.opensaml.xmlsec.encryption.KANonce) XMLObjectSupport
.buildXMLObject(org.opensaml.xmlsec.encryption.KANonce.DEFAULT_ELEMENT_NAME);
@@ -74,15 +89,50 @@ public class KANonce extends AbstractInitializableComponent
return nonce;
}
- /** {@inheritDoc} */
- public KANonce clone() {
- ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
- try {
- return (KANonce ) super.clone();
- } catch (final CloneNotSupportedException e) {
- // We know we are, so this will never happen
- return null;
+ /**
+ * Create and initialize a new instance from the specified {@link XMLObject}.
+ *
+ * @param xmlObject the XML object
+ *
+ * @return new parameter instance
+ *
+ * @throws ComponentInitializationException
+ */
+ @Nonnull public static KANonce fromXMLObject(@Nonnull final org.opensaml.xmlsec.encryption.KANonce xmlObject)
+ throws ComponentInitializationException {
+ Constraint.isNotNull(xmlObject, "XMLObject was null");
+
+ final KANonce parameter = new KANonce();
+ parameter.setValue(xmlObject.getValue());
+ parameter.initialize();
+ return parameter;
+ }
+
+ /**
+ * Implementation of {@link KeyAgreementParameterParser}.
+ */
+ public static class Parser implements KeyAgreementParameterParser {
+
+ /** {@inheritDoc} */
+ public boolean handles(@Nonnull final XMLObject xmlObject) {
+ return org.opensaml.xmlsec.encryption.KANonce.class.isInstance(xmlObject);
+ }
+
+ /** {@inheritDoc} */
+ public KeyAgreementParameter parse(@Nonnull final XMLObject xmlObject) throws KeyAgreementException {
+ // Sanity check
+ if (!handles(xmlObject)) {
+ throw new KeyAgreementException("This implementation does not handle: "
+ + xmlObject.getClass().getName());
+ }
+
+ try {
+ return fromXMLObject(org.opensaml.xmlsec.encryption.KANonce.class.cast(xmlObject));
+ } catch (final ComponentInitializationException e) {
+ throw new KeyAgreementException(e);
+ }
}
+
}
}
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/KeyAgreementParameterParser.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/KeyAgreementParameterParser.java
new file mode 100644
index 000000000..8528e170b
--- /dev/null
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/KeyAgreementParameterParser.java
@@ -0,0 +1,51 @@
+/*
+ * 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.agreement.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.xmlsec.agreement.KeyAgreementException;
+import org.opensaml.xmlsec.agreement.KeyAgreementParameter;
+
+/**
+ * A component which parses an {@link XMLObject} into an instance of {@link KeyAgreementParameter}.
+ */
+public interface KeyAgreementParameterParser {
+
+ /**
+ * Evaluate whether the specified {@link XMLObject} is handled by the implementation.
+ *
+ * @param xmlObject the XML object to evaluate
+ *
+ * @return true if handles, false if not
+ */
+ boolean handles(@Nonnull final XMLObject xmlObject);
+
+ /**
+ * Parse the specified {@link XMLObject} into a {@link KeyAgreementParameter}.
+ *
+ * @param xmlObject the XMLObject to be parsed
+ *
+ * @return the new key agreement parameter instance
+ *
+ * @throws KeyAgreementException
+ */
+ KeyAgreementParameter parse(@Nonnull final XMLObject xmlObject) throws KeyAgreementException;
+
+}
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/KeyAgreementParametersParser.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/KeyAgreementParametersParser.java
new file mode 100644
index 000000000..e74f5fb1a
--- /dev/null
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/KeyAgreementParametersParser.java
@@ -0,0 +1,104 @@
+/*
+ * 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.agreement.impl;
+
+import java.util.List;
+import java.util.ServiceLoader;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.xmlsec.agreement.KeyAgreementException;
+import org.opensaml.xmlsec.agreement.KeyAgreementParameters;
+import org.opensaml.xmlsec.encryption.AgreementMethod;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Lists;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * A component which parses an instance of {@link AgreementMethod} and produces a new instance
+ * of {@link KeyAgreementParameters}.
+ */
+public class KeyAgreementParametersParser {
+
+ /** Logger. */
+ private Logger log = LoggerFactory.getLogger(KeyAgreementParametersParser.class);
+
+ /**
+ * Parse the specified {@link AgreementMethod} into an instance of {@link KeyAgreementParameters}.
+ *
+ * @param agreementMethod the AgreementMethod to process
+ *
+ * @return the new instance of key agreement parameters
+ *
+ * @throws KeyAgreementException
+ */
+ @Nonnull public KeyAgreementParameters parse(@Nonnull final AgreementMethod agreementMethod)
+ throws KeyAgreementException {
+ Constraint.isNotNull(agreementMethod, "AgreementMethod was null");
+
+ final KeyAgreementParameters parameters = new KeyAgreementParameters();
+
+ final List<KeyAgreementParameterParser> parsers = getParsers();
+
+ final List<XMLObject> xmlChildren = Lists.newArrayList(agreementMethod.getUnknownXMLObjects());
+
+ // KANonce is the only parameter with a "named" slot on AgreementMethod, so handle it specifically
+ if (agreementMethod.getKANonce() != null) {
+ xmlChildren.add(agreementMethod.getKANonce());
+ }
+
+ for (final XMLObject xmlChild : xmlChildren) {
+ boolean handled = false;
+ for (final KeyAgreementParameterParser parser : parsers) {
+ if (parser.handles(xmlChild)) {
+ log.debug("AgreementMethod child '{}' was indicated to be handled by: {}",
+ xmlChild.getElementQName(), parser.getClass().getName());
+ parameters.add(parser.parse(xmlChild));
+ handled = true;
+ continue;
+ }
+ }
+ if (!handled) {
+ throw new KeyAgreementException("AgreementMethod child is not a supported parameter type: "
+ + xmlChild.getElementQName());
+ }
+ }
+
+ parameters.initializeAll();
+
+ return parameters;
+ }
+
+ /**
+ * Obtain the list of {@link KeyAgreementParameterParser} instances to use.
+ *
+ * <p>
+ * This implementation uses the Java Service API to load the instances. Subclasses may override.
+ * </p>
+ *
+ * @return the list of parser instances
+ */
+ @Nonnull protected List<KeyAgreementParameterParser> getParsers() {
+ final ServiceLoader<KeyAgreementParameterParser> loader = ServiceLoader.load(KeyAgreementParameterParser.class);
+ return Lists.newArrayList(loader);
+ }
+}
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 991b6ebd2..8c182d5ac 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
@@ -39,6 +39,7 @@ import org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorManager;
import org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;
import org.opensaml.xmlsec.keyinfo.impl.BasicKeyInfoGeneratorFactory;
import org.opensaml.xmlsec.keyinfo.impl.BasicProviderKeyInfoCredentialResolver;
+import org.opensaml.xmlsec.keyinfo.impl.KeyAgreementKeyInfoGeneratorFactory;
import org.opensaml.xmlsec.keyinfo.impl.KeyInfoProvider;
import org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;
import org.opensaml.xmlsec.keyinfo.impl.provider.DEREncodedKeyValueProvider;
@@ -229,7 +230,12 @@ public class DefaultSecurityConfigurationBootstrap {
* @return a named KeyInfo generator manager instance
*/
protected static NamedKeyInfoGeneratorManager buildDataEncryptionKeyInfoGeneratorManager() {
- return buildBasicKeyInfoGeneratorManager();
+ // Generator for KeyAgreementCredentials. This factory already defaults the usually desired settings.
+ final KeyAgreementKeyInfoGeneratorFactory keyAgreementFactory = new KeyAgreementKeyInfoGeneratorFactory();
+
+ final NamedKeyInfoGeneratorManager manager = buildBasicKeyInfoGeneratorManager();
+ manager.getDefaultManager().registerFactory(keyAgreementFactory);
+ return manager;
}
/**
@@ -239,7 +245,12 @@ public class DefaultSecurityConfigurationBootstrap {
* @return a named KeyInfo generator manager instance
*/
protected static NamedKeyInfoGeneratorManager buildKeyTransportEncryptionKeyInfoGeneratorManager() {
- return buildBasicKeyInfoGeneratorManager();
+ // Generator for KeyAgreementCredentials. This factory already defaults the usually desired settings.
+ final KeyAgreementKeyInfoGeneratorFactory keyAgreementFactory = new KeyAgreementKeyInfoGeneratorFactory();
+
+ final NamedKeyInfoGeneratorManager manager = buildBasicKeyInfoGeneratorManager();
+ manager.getDefaultManager().registerFactory(keyAgreementFactory);
+ return manager;
}
/**
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/ConcatKDF.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/ConcatKDF.java
index cf006c323..32d8d7ebb 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/ConcatKDF.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/ConcatKDF.java
@@ -36,13 +36,17 @@ import org.bouncycastle.crypto.params.KDFParameters;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.core.xml.util.XMLObjectSupport;
import org.opensaml.xmlsec.agreement.CloneableKeyAgreementParameter;
+import org.opensaml.xmlsec.agreement.KeyAgreementException;
+import org.opensaml.xmlsec.agreement.KeyAgreementParameter;
import org.opensaml.xmlsec.agreement.XMLExpressableKeyAgreementParameter;
+import org.opensaml.xmlsec.agreement.impl.KeyAgreementParameterParser;
import org.opensaml.xmlsec.algorithm.AlgorithmDescriptor;
import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
import org.opensaml.xmlsec.algorithm.DigestAlgorithm;
import org.opensaml.xmlsec.derivation.KeyDerivation;
import org.opensaml.xmlsec.derivation.KeyDerivationException;
import org.opensaml.xmlsec.encryption.ConcatKDFParams;
+import org.opensaml.xmlsec.encryption.EncryptedType;
import org.opensaml.xmlsec.encryption.KeyDerivationMethod;
import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
import org.opensaml.xmlsec.signature.DigestMethod;
@@ -265,6 +269,7 @@ public class ConcatKDF extends AbstractInitializableComponent
throws KeyDerivationException {
Constraint.isNotNull(secret, "Secret byte[] was null");
Constraint.isNotNull(keyAlgorithm, "Key algorithm was null");
+ ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
final String jcaKeyAlgorithm = AlgorithmSupport.getKeyAlgorithm(keyAlgorithm);
if (jcaKeyAlgorithm == null) {
@@ -350,31 +355,6 @@ public class ConcatKDF extends AbstractInitializableComponent
}
}
- /** {@inheritDoc} */
- public XMLObject buildXMLObject() {
- final KeyDerivationMethod method =
- (KeyDerivationMethod) XMLObjectSupport.buildXMLObject(KeyDerivationMethod.DEFAULT_ELEMENT_NAME);
- method.setAlgorithm(getAlgorithm());
-
- final ConcatKDFParams params =
- (ConcatKDFParams) XMLObjectSupport.buildXMLObject(ConcatKDFParams.DEFAULT_ELEMENT_NAME);
-
- final DigestMethod xmlDigestMethod =
- (DigestMethod) XMLObjectSupport.buildXMLObject(DigestMethod.DEFAULT_ELEMENT_NAME);
- xmlDigestMethod.setAlgorithm(digestMethod);
- params.setDigestMethod(xmlDigestMethod);
-
- params.setAlgorithmID(padParam(algorithmID));
- params.setPartyUInfo(padParam(partyUInfo));
- params.setPartyVInfo(padParam(partyVInfo));
- params.setSuppPubInfo(padParam(suppPubInfo));
- params.setSuppPrivInfo(padParam(suppPrivInfo));
-
- method.getUnknownXMLObjects().add(params);
-
- return method;
- }
-
/** {@inheritDoc} */
public ConcatKDF clone() {
try {
@@ -478,4 +458,109 @@ public class ConcatKDF extends AbstractInitializableComponent
return trimmed.substring(2);
}
+ /** {@inheritDoc} */
+ public XMLObject buildXMLObject() {
+ ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+
+ final KeyDerivationMethod method =
+ (KeyDerivationMethod) XMLObjectSupport.buildXMLObject(KeyDerivationMethod.DEFAULT_ELEMENT_NAME);
+ method.setAlgorithm(getAlgorithm());
+
+ final ConcatKDFParams params =
+ (ConcatKDFParams) XMLObjectSupport.buildXMLObject(ConcatKDFParams.DEFAULT_ELEMENT_NAME);
+
+ final DigestMethod xmlDigestMethod =
+ (DigestMethod) XMLObjectSupport.buildXMLObject(DigestMethod.DEFAULT_ELEMENT_NAME);
+ xmlDigestMethod.setAlgorithm(digestMethod);
+ params.setDigestMethod(xmlDigestMethod);
+
+ params.setAlgorithmID(padParam(algorithmID));
+ params.setPartyUInfo(padParam(partyUInfo));
+ params.setPartyVInfo(padParam(partyVInfo));
+ params.setSuppPubInfo(padParam(suppPubInfo));
+ params.setSuppPrivInfo(padParam(suppPrivInfo));
+
+ method.getUnknownXMLObjects().add(params);
+
+ return method;
+ }
+
+ /**
+ * Create and initialize a new instance from the specified {@link XMLObject}.
+ *
+ * @param xmlObject the XML object
+ *
+ * @return new parameter instance
+ *
+ * @throws ComponentInitializationException
+ */
+ @Nonnull public static ConcatKDF fromXMLObject(@Nonnull final KeyDerivationMethod xmlObject)
+ throws ComponentInitializationException {
+ Constraint.isNotNull(xmlObject, "XMLObject was null");
+
+ if (! EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF.equals(xmlObject.getAlgorithm())) {
+ throw new ComponentInitializationException("KeyDerivationMethod contains unsupported algorithm: "
+ + xmlObject.getAlgorithm());
+ }
+
+ if (xmlObject.getUnknownXMLObjects().size() != 1
+ || xmlObject.getUnknownXMLObjects(ConcatKDFParams.DEFAULT_ELEMENT_NAME).size() != 1) {
+ throw new ComponentInitializationException("KeyDerivationMethod contains unsupported children");
+ }
+
+ final ConcatKDFParams xmlParams =
+ (ConcatKDFParams) xmlObject.getUnknownXMLObjects(ConcatKDFParams.DEFAULT_ELEMENT_NAME).get(0);
+
+ final ConcatKDF parameter = new ConcatKDF();
+
+ if (xmlParams.getDigestMethod() == null || xmlParams.getDigestMethod().getAlgorithm() == null) {
+ throw new ComponentInitializationException("KeyDerivationMethod did not contain DigestMethod value");
+ }
+
+ parameter.setDigestMethod(xmlParams.getDigestMethod().getAlgorithm());
+
+ try {
+ parameter.setAlgorithmID(unpadParam(xmlParams.getAlgorithmID(), "AlgorithmID"));
+ parameter.setPartyUInfo(unpadParam(xmlParams.getPartyUInfo(), "PartyUInfo"));
+ parameter.setPartyVInfo(unpadParam(xmlParams.getPartyVInfo(), "PartyVInfo"));
+ parameter.setSuppPubInfo(unpadParam(xmlParams.getSuppPubInfo(), "SuppPubInfo"));
+ parameter.setSuppPrivInfo(unpadParam(xmlParams.getSuppPrivInfo(), "SuppPrivInfo"));
+ } catch (final KeyDerivationException e) {
+ throw new ComponentInitializationException("Invalid ConcatKDF param value", e);
+ }
+
+ parameter.initialize();
+
+ return parameter;
+ }
+
+ /**
+ * Implementation of {@link KeyAgreementParameterParser}.
+ */
+ public static class Parser implements KeyAgreementParameterParser {
+
+ /** {@inheritDoc} */
+ public boolean handles(@Nonnull final XMLObject xmlObject) {
+ return KeyDerivationMethod.class.isInstance(xmlObject)
+ && EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF.equals(
+ KeyDerivationMethod.class.cast(xmlObject).getAlgorithm());
+ }
+
+ /** {@inheritDoc} */
+ public KeyAgreementParameter parse(@Nonnull final XMLObject xmlObject) throws KeyAgreementException {
+ // Sanity check
+ if (!handles(xmlObject)) {
+ throw new KeyAgreementException("This implementation does not handle: "
+ + xmlObject.getClass().getName());
+ }
+
+ try {
+ return fromXMLObject(KeyDerivationMethod.class.cast(xmlObject));
+ } catch (final ComponentInitializationException e) {
+ throw new KeyAgreementException(e);
+ }
+ }
+
+ }
+
}
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/PBKDF2.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/PBKDF2.java
index a47c94bf4..05726289a 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/PBKDF2.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/PBKDF2.java
@@ -31,7 +31,10 @@ import javax.crypto.spec.SecretKeySpec;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.core.xml.util.XMLObjectSupport;
import org.opensaml.xmlsec.agreement.CloneableKeyAgreementParameter;
+import org.opensaml.xmlsec.agreement.KeyAgreementException;
+import org.opensaml.xmlsec.agreement.KeyAgreementParameter;
import org.opensaml.xmlsec.agreement.XMLExpressableKeyAgreementParameter;
+import org.opensaml.xmlsec.agreement.impl.KeyAgreementParameterParser;
import org.opensaml.xmlsec.algorithm.AlgorithmDescriptor;
import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
import org.opensaml.xmlsec.algorithm.MACAlgorithm;
@@ -277,6 +280,7 @@ public class PBKDF2 extends AbstractInitializableComponent
throws KeyDerivationException {
Constraint.isNotNull(secret, "Secret byte[] was null");
Constraint.isNotNull(keyAlgorithm, "Key algorithm was null");
+ ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
final String jcaKeyAlgorithm = AlgorithmSupport.getKeyAlgorithm(keyAlgorithm);
if (jcaKeyAlgorithm == null) {
@@ -360,8 +364,29 @@ public class PBKDF2 extends AbstractInitializableComponent
return keyLength;
}
+ /** {@inheritDoc} */
+ public PBKDF2 clone() {
+ try {
+ return (PBKDF2) super.clone();
+ } catch (final CloneNotSupportedException e) {
+ // We know we are, so this will never happen
+ return null;
+ }
+ }
+
/** {@inheritDoc} */
public XMLObject buildXMLObject() {
+ ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+
+ // If initialized, iterationCount and PRF are guaranteed to be non-null.
+ // These 2 would happen if initialized but derive(...) hasn't been called.
+ if (keyLength == null) {
+ throw new IllegalStateException("PBKDF2 is missing KeyLength element data");
+ }
+ if (salt == null) {
+ throw new IllegalStateException("PBKDF2 is missing Salt element data");
+ }
+
final KeyDerivationMethod method =
(KeyDerivationMethod) XMLObjectSupport.buildXMLObject(KeyDerivationMethod.DEFAULT_ELEMENT_NAME);
method.setAlgorithm(getAlgorithm());
@@ -369,8 +394,6 @@ public class PBKDF2 extends AbstractInitializableComponent
final PBKDF2Params params =
(PBKDF2Params) XMLObjectSupport.buildXMLObject(PBKDF2Params.DEFAULT_ELEMENT_NAME);
- //TODO do sanity checking on these - how to report out? Maybe method signature should have a thrown exception.
-
final Salt xmlSalt = (Salt) XMLObjectSupport.buildXMLObject(Salt.DEFAULT_ELEMENT_NAME);
final Specified specified = (Specified) XMLObjectSupport.buildXMLObject(Specified.DEFAULT_ELEMENT_NAME);
specified.setValue(salt);
@@ -397,14 +420,107 @@ public class PBKDF2 extends AbstractInitializableComponent
return method;
}
- /** {@inheritDoc} */
- public PBKDF2 clone() {
- try {
- return (PBKDF2) super.clone();
- } catch (final CloneNotSupportedException e) {
- // We know we are, so this will never happen
- return null;
+ /**
+ * Create and initialize a new instance from the specified {@link XMLObject}.
+ *
+ * @param xmlObject the XML object
+ *
+ * @return new parameter instance
+ *
+ * @throws ComponentInitializationException
+ */
+ @Nonnull public static PBKDF2 fromXMLObject(@Nonnull final KeyDerivationMethod xmlObject)
+ throws ComponentInitializationException {
+ Constraint.isNotNull(xmlObject, "XMLObject was null");
+
+ if (! EncryptionConstants.ALGO_ID_KEYDERIVATION_PBKDF2.equals(xmlObject.getAlgorithm())) {
+ throw new ComponentInitializationException("KeyDerivationMethod contains unsupported algorithm: "
+ + xmlObject.getAlgorithm());
+ }
+
+ if (xmlObject.getUnknownXMLObjects().size() != 1
+ || xmlObject.getUnknownXMLObjects(PBKDF2Params.DEFAULT_ELEMENT_NAME).size() != 1) {
+ throw new ComponentInitializationException("KeyDerivationMethod contains unsupported children");
+ }
+
+ final PBKDF2Params xmlParams =
+ (PBKDF2Params) xmlObject.getUnknownXMLObjects(PBKDF2Params.DEFAULT_ELEMENT_NAME).get(0);
+
+ validateXMLObjectParameters(xmlParams);
+
+ final PBKDF2 param = new PBKDF2();
+
+ param.setIterationCount(xmlParams.getIterationCount().getValue());
+ // Note: We're tracking this in # of bits, but the XML element uses # of bytes.
+ param.setKeyLength(xmlParams.getKeyLength().getValue() * 8);
+ param.setPRF(xmlParams.getPRF().getAlgorithm());
+ param.setSalt(xmlParams.getSalt().getSpecified().getValue());
+
+ param.initialize();
+
+ return param;
+ }
+
+ /**
+ * Validate the {@link PBKDF2Params} instance.
+ *
+ * @param xmlParams the instance to validate
+ *
+ * @throws ComponentInitializationException
+ */
+ // Checkstyle: CyclomaticComplexity OFF
+ private static void validateXMLObjectParameters(@Nonnull final PBKDF2Params xmlParams)
+ throws ComponentInitializationException {
+
+ if (xmlParams.getIterationCount() == null || xmlParams.getIterationCount().getValue() == null) {
+ throw new ComponentInitializationException("PBKDF2-params did not contain IterationCount value");
+ }
+
+ if (xmlParams.getKeyLength() == null || xmlParams.getKeyLength().getValue() == null) {
+ throw new ComponentInitializationException("PBKDF2-params did not contain KeyLength value");
+ }
+
+ if (xmlParams.getPRF() == null || xmlParams.getPRF().getAlgorithm() == null) {
+ throw new ComponentInitializationException("PBKDF2-params did not contain PRF value");
+ }
+ if (xmlParams.getPRF().getParameters() != null) {
+ throw new ComponentInitializationException("PBKDF2-params contained unsupported PRF parameters");
+ }
+
+ if (xmlParams.getSalt() == null || xmlParams.getSalt().getSpecified() == null
+ || xmlParams.getSalt().getSpecified().getValue() == null) {
+ throw new ComponentInitializationException("PBKDF2-params did not contain Salt Specified value");
}
}
+ // Checkstyle: CyclomaticComplexity ON
+
+ /**
+ * Implementation of {@link KeyAgreementParameterParser}.
+ */
+ public static class Parser implements KeyAgreementParameterParser {
+
+ /** {@inheritDoc} */
+ public boolean handles(@Nonnull final XMLObject xmlObject) {
+ return KeyDerivationMethod.class.isInstance(xmlObject)
+ && EncryptionConstants.ALGO_ID_KEYDERIVATION_PBKDF2.equals(
+ KeyDerivationMethod.class.cast(xmlObject).getAlgorithm());
+ }
+ /** {@inheritDoc} */
+ public KeyAgreementParameter parse(@Nonnull final XMLObject xmlObject) throws KeyAgreementException {
+ // Sanity check
+ if (!handles(xmlObject)) {
+ throw new KeyAgreementException("This implementation does not handle: "
+ + xmlObject.getClass().getName());
+ }
+
+ try {
+ return fromXMLObject(KeyDerivationMethod.class.cast(xmlObject));
+ } catch (final ComponentInitializationException e) {
+ throw new KeyAgreementException(e);
+ }
+ }
+
+ }
+
}
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/BasicKeyInfoGeneratorFactory.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/BasicKeyInfoGeneratorFactory.java
index 9e6344c26..9327c9aa9 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/BasicKeyInfoGeneratorFactory.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/BasicKeyInfoGeneratorFactory.java
@@ -19,16 +19,21 @@ package org.opensaml.xmlsec.keyinfo.impl;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import javax.xml.namespace.QName;
import org.opensaml.core.xml.XMLObject;
-import org.opensaml.core.xml.XMLObjectBuilder;
+import org.opensaml.core.xml.XMLObjectBuilderFactory;
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
import org.opensaml.security.SecurityException;
import org.opensaml.security.credential.Credential;
+import org.opensaml.xmlsec.encryption.OriginatorKeyInfo;
+import org.opensaml.xmlsec.encryption.RecipientKeyInfo;
import org.opensaml.xmlsec.keyinfo.KeyInfoGenerator;
import org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorFactory;
import org.opensaml.xmlsec.keyinfo.KeyInfoSupport;
@@ -47,6 +52,15 @@ import net.shibboleth.utilities.java.support.codec.EncodingException;
*/
public class BasicKeyInfoGeneratorFactory implements KeyInfoGeneratorFactory {
+ /** Mappings from KeyInfo Class to QNames.*/
+ private static final Map<Class<? extends KeyInfo>, QName> CLASS_TO_NAME;
+ static {
+ CLASS_TO_NAME = new HashMap<>();
+ CLASS_TO_NAME.put(KeyInfo.class, KeyInfo.DEFAULT_ELEMENT_NAME);
+ CLASS_TO_NAME.put(OriginatorKeyInfo.class, OriginatorKeyInfo.DEFAULT_ELEMENT_NAME);
+ CLASS_TO_NAME.put(RecipientKeyInfo.class, RecipientKeyInfo.DEFAULT_ELEMENT_NAME);
+ }
+
/** The set of options configured for the factory. */
private final BasicOptions options;
@@ -72,9 +86,12 @@ public class BasicKeyInfoGeneratorFactory implements KeyInfoGeneratorFactory {
/** {@inheritDoc} */
@Nonnull public KeyInfoGenerator newInstance() {
- //TODO lock options during cloning ?
- final BasicOptions newOptions = options.clone();
- return new BasicKeyInfoGenerator(newOptions);
+ return newInstance(null);
+ }
+
+ /** {@inheritDoc} */
+ @Nonnull public KeyInfoGenerator newInstance(@Nullable final Class<? extends KeyInfo> type) {
+ return new BasicKeyInfoGenerator(options.clone(), type);
}
/**
@@ -177,20 +194,25 @@ public class BasicKeyInfoGeneratorFactory implements KeyInfoGeneratorFactory {
public class BasicKeyInfoGenerator implements KeyInfoGenerator {
/** The set of options to be used by the generator.*/
- private final BasicOptions options;
+ @Nonnull private final BasicOptions options;
+
+ /** The specific type of KeyInfo to generate. */
+ @Nonnull private final Class<? extends KeyInfo> keyInfoType;
- /** Builder for KeyInfo objects. */
- private final XMLObjectBuilder<KeyInfo> keyInfoBuilder;
+ /** Builder factory for KeyInfo objects. */
+ @Nonnull private final XMLObjectBuilderFactory builderFactory;
/**
* Constructor.
*
* @param newOptions the options to be used by the generator
+ * @param type the type of element to produce
*/
- protected BasicKeyInfoGenerator(@Nonnull final BasicOptions newOptions) {
+ protected BasicKeyInfoGenerator(@Nonnull final BasicOptions newOptions,
+ @Nullable final Class<? extends KeyInfo> type) {
options = newOptions;
- keyInfoBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilderOrThrow(
- KeyInfo.DEFAULT_ELEMENT_NAME);
+ keyInfoType = type != null ? type : KeyInfo.class;
+ builderFactory = XMLObjectProviderRegistrySupport.getBuilderFactory();
}
/** {@inheritDoc} */
@@ -199,7 +221,7 @@ public class BasicKeyInfoGeneratorFactory implements KeyInfoGeneratorFactory {
return null;
}
- final KeyInfo keyInfo = keyInfoBuilder.buildObject(KeyInfo.DEFAULT_ELEMENT_NAME);
+ final KeyInfo keyInfo = buildKeyInfo();
processKeyNames(keyInfo, credential);
processEntityID(keyInfo, credential);
@@ -212,6 +234,45 @@ public class BasicKeyInfoGeneratorFactory implements KeyInfoGeneratorFactory {
return null;
}
+ /**
+ * Build a new KeyInfo instance.
+ *
+ * <p>
+ * The exact element type is determined by {@link BasicOptions#keyInfoElementType}, defaulting to
+ * {@link KeyInfo#DEFAULT_ELEMENT_NAME} if the option is null. It is a runtime error if the specified type
+ * is not a sub-type of {@link KeyInfo}.
+ * </p>
+ *
+ * @return a new KeyInfo instance
+ *
+ * @throws SecurityException
+ */
+ protected KeyInfo buildKeyInfo() throws SecurityException {
+ final QName elementName = classToElementName(keyInfoType);
+ if (elementName == null) {
+ throw new SecurityException("KeyInfo type not mapped to an element QName: "
+ + keyInfoType.getClass().getName());
+ }
+
+ final XMLObject xmlObject = builderFactory.getBuilderOrThrow(elementName).buildObject(elementName);
+ return KeyInfo.class.cast(xmlObject);
+ }
+
+ /**
+ * Map the specified KeyInfo type to an element {@link QName}.
+ *
+ * <p>
+ * Subclasses may override to implement new types or custom mappings.
+ * </p>
+ *
+ * @param type the KeyInfo element type
+ *
+ * @return the mapped element name
+ */
+ @Nullable protected QName classToElementName(@Nonnull final Class<? extends KeyInfo> type) {
+ return CLASS_TO_NAME.get(type);
+ }
+
/** Process the values of {@link Credential#getKeyNames()}.
*
* @param keyInfo the KeyInfo that is being built
@@ -298,5 +359,5 @@ public class BasicKeyInfoGeneratorFactory implements KeyInfoGeneratorFactory {
}
}
-
+
}
\ No newline at end of file
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/KeyAgreementKeyInfoGeneratorFactory.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/KeyAgreementKeyInfoGeneratorFactory.java
new file mode 100644
index 000000000..a33eba67c
--- /dev/null
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/KeyAgreementKeyInfoGeneratorFactory.java
@@ -0,0 +1,379 @@
+/*
+ * 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.keyinfo.impl;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.XMLObjectBuilder;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.security.SecurityException;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.xmlsec.agreement.KeyAgreementCredential;
+import org.opensaml.xmlsec.agreement.KeyAgreementParameter;
+import org.opensaml.xmlsec.agreement.XMLExpressableKeyAgreementParameter;
+import org.opensaml.xmlsec.encryption.AgreementMethod;
+import org.opensaml.xmlsec.encryption.KANonce;
+import org.opensaml.xmlsec.encryption.OriginatorKeyInfo;
+import org.opensaml.xmlsec.encryption.RecipientKeyInfo;
+import org.opensaml.xmlsec.keyinfo.KeyInfoGenerator;
+import org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorManager;
+import org.opensaml.xmlsec.signature.KeyInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A factory implementation which produces instances of {@link KeyInfoGenerator} capable of
+ * handling the information contained within an {@link KeyAgreementCredential}.
+ */
+public class KeyAgreementKeyInfoGeneratorFactory extends BasicKeyInfoGeneratorFactory {
+
+ /** The set of options configured for the factory. */
+ private final KeyAgreementOptions options;
+
+ /** Constructor. */
+ public KeyAgreementKeyInfoGeneratorFactory() {
+ super();
+ options = (KeyAgreementOptions) super.getOptions();
+ }
+
+ /** {@inheritDoc} */
+ @Nonnull public Class<? extends Credential> getCredentialType() {
+ return KeyAgreementCredential.class;
+ }
+
+ /** {@inheritDoc} */
+ public boolean handles(@Nonnull final Credential credential) {
+ return credential instanceof KeyAgreementCredential;
+ }
+
+ /** {@inheritDoc} */
+ @Nonnull public KeyInfoGenerator newInstance() {
+ return newInstance(null);
+ }
+
+ /** {@inheritDoc} */
+ @Nonnull public KeyInfoGenerator newInstance(@Nullable final Class<? extends KeyInfo> type) {
+ return new KeyAgreementKeyInfoGenerator(options.clone(), type);
+ }
+
+ /**
+ * Get the option to emit the OriginatorKeyInfo element within the AgreementMethod element.
+ *
+ * @return the option value
+ */
+ public boolean emitOriginatorKeyInfo() {
+ return options.emitOriginatorKeyInfo;
+ }
+
+ /**
+ * Set the option to emit the OriginatorKeyInfo element within the AgreementMethod element.
+ *
+ * @param newValue the new option value
+ */
+ public void setEmitOriginatorKeyInfo(final boolean newValue) {
+ options.emitOriginatorKeyInfo = newValue;
+ }
+
+ /**
+ * Get the option to emit the RecipientKeyInfo element within the AgreementMethod element.
+ *
+ * @return the option value
+ */
+ public boolean emitRecipientKeyInfo() {
+ return options.emitRecipientKeyInfo;
+ }
+
+ /**
+ * Set the option to emit the RecipientKeyInfo element within the AgreementMethod element.
+ *
+ * @param newValue the new option value
+ */
+ public void setEmitRecipientKeyInfo(final boolean newValue) {
+ options.emitRecipientKeyInfo = newValue;
+ }
+
+ /**
+ * Get the {@link KeyInfoGeneratorManager} instance to use to emit {@link OriginatorKeyInfo}.
+ *
+ * @return the manager
+ */
+ public KeyInfoGeneratorManager getOriginatorKeyInfoGeneratorManager() {
+ return options.originatorKeyInfoGeneratorManager;
+ }
+
+ /**
+ * Set the {@link KeyInfoGeneratorManager} instance to use to emit {@link OriginatorKeyInfo}.
+ *
+ * @param manager the manager instance
+ */
+ public void setOriginatorKeyInfoGeneratorManager(@Nullable final KeyInfoGeneratorManager manager) {
+ options.originatorKeyInfoGeneratorManager = manager;
+ }
+
+ /**
+ * Get the {@link KeyInfoGeneratorManager} instance to use to emit {@link RecipientKeyInfo}.
+ *
+ * @return the manager
+ */
+ public KeyInfoGeneratorManager getRecipientKeyInfoGeneratorManager() {
+ return options.recipientKeyInfoGeneratorManager;
+ }
+
+ /**
+ * Set the {@link KeyInfoGeneratorManager} instance to use to emit {@link RecipientKeyInfo}.
+ *
+ * @param manager the manager instance
+ */
+ public void setRecipientKeyInfoGeneratorManager(@Nullable final KeyInfoGeneratorManager manager) {
+ options.recipientKeyInfoGeneratorManager = manager;
+ }
+
+ /** {@inheritDoc} */
+ @Nonnull protected KeyAgreementOptions getOptions() {
+ return options;
+ }
+
+ /** {@inheritDoc} */
+ @Nonnull protected KeyAgreementOptions newOptions() {
+ return new KeyAgreementOptions();
+ }
+
+ /**
+ * An implementation of {@link KeyInfoGenerator} capable of handling the information
+ * contained within a {@link KeyAgreementCredential}.
+ */
+ public class KeyAgreementKeyInfoGenerator extends BasicKeyInfoGenerator {
+
+ /** Class logger. */
+ private final Logger log = LoggerFactory.getLogger(KeyAgreementKeyInfoGenerator.class);
+
+ /** The set of options to be used by the generator.*/
+ private KeyAgreementOptions options;
+
+ /** Builder for AgreementMethod objects. */
+ private final XMLObjectBuilder<AgreementMethod> agreementMethodBuilder;
+
+ /**
+ * Constructor.
+ *
+ * @param newOptions the options to be used by the generator
+ * @param type the KeyInfo element type
+ */
+ protected KeyAgreementKeyInfoGenerator(final KeyAgreementOptions newOptions,
+ final Class<? extends KeyInfo> type) {
+ super(newOptions, type);
+ options = newOptions;
+
+ agreementMethodBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilderOrThrow(
+ AgreementMethod.DEFAULT_ELEMENT_NAME);
+ }
+
+ /** {@inheritDoc} */
+ @Nullable public KeyInfo generate(@Nullable final Credential credential) throws SecurityException {
+ if (credential == null) {
+ log.warn("KeyAgreementKeyInfoGenerator was passed a null credential");
+ return null;
+ } else if (!(credential instanceof KeyAgreementCredential)) {
+ log.warn("KeyAgreementKeyInfoGenerator was passed a credential that was not an instance of "
+ + "KeyAgreementCredential: {}", credential.getClass().getName());
+ return null;
+ }
+ final KeyAgreementCredential keyAgreementCredential = (KeyAgreementCredential) credential;
+
+ KeyInfo keyInfo = super.generate(credential);
+ if (keyInfo == null) {
+ keyInfo = buildKeyInfo();
+ }
+
+ final AgreementMethod agreementMethod =
+ agreementMethodBuilder.buildObject(AgreementMethod.DEFAULT_ELEMENT_NAME);
+
+ agreementMethod.setAlgorithm(keyAgreementCredential.getAlgorithm());
+
+ processAgreementParameters(keyInfo, agreementMethod, keyAgreementCredential);
+ processOriginatorKeyInfo(keyInfo, agreementMethod, keyAgreementCredential);
+ processRecipientKeyInfo(keyInfo, agreementMethod, keyAgreementCredential);
+
+ keyInfo.getAgreementMethods().add(agreementMethod);
+
+ return keyInfo;
+ }
+
+ /**
+ * Process {@link KeyAgreementCredential#getParameters()}.
+ *
+ * @param keyInfo the KeyInfo that is being built
+ * @param agreementMethod the AgreementMethod that is being built
+ * @param credential the Credential that is being processed
+ *
+ * @throws SecurityException
+ */
+ private void processAgreementParameters(@Nonnull final KeyInfo keyInfo,
+ @Nonnull final AgreementMethod agreementMethod, @Nonnull final KeyAgreementCredential credential)
+ throws SecurityException {
+
+ // We emit these unconditionally, because key agreement on the recipient side
+ // realistically isn't possible without them
+
+ for (final KeyAgreementParameter param : credential.getParameters()) {
+ if (XMLExpressableKeyAgreementParameter.class.isInstance(param)) {
+ final XMLObject xmlParam = XMLExpressableKeyAgreementParameter.class.cast(param).buildXMLObject();
+ if (KANonce.class.isInstance(xmlParam)) {
+ agreementMethod.setKANonce(KANonce.class.cast(xmlParam));
+ } else if (xmlParam != null){
+ agreementMethod.getUnknownXMLObjects().add(xmlParam);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Process the {@link KeyAgreementCredential#getOriginatorCredential()}.
+ *
+ * @param keyInfo the KeyInfo that is being built
+ * @param agreementMethod the AgreementMethod that is being built
+ * @param credential the Credential that is being processed
+ *
+ * @throws SecurityException
+ */
+ private void processOriginatorKeyInfo(@Nonnull final KeyInfo keyInfo,
+ @Nonnull final AgreementMethod agreementMethod, @Nonnull final KeyAgreementCredential credential)
+ throws SecurityException {
+
+ if (options.emitOriginatorKeyInfo) {
+ if (options.originatorKeyInfoGeneratorManager == null) {
+ log.warn("KeyInfoGeneratorManager for OriginatorKeyInfo is null, can not process");
+ return;
+ }
+ if (credential.getOriginatorCredential() == null) {
+ log.warn("KeyAgreementCredential originator credential is null, can not process");
+ return;
+ }
+
+ final KeyInfo originatorKeyInfo = options.originatorKeyInfoGeneratorManager
+ .getFactory(credential.getOriginatorCredential())
+ .newInstance(OriginatorKeyInfo.class)
+ .generate(credential.getOriginatorCredential());
+ if (originatorKeyInfo == null) {
+ log.warn("Failed to generate KeyInfo from KeyAgreementCredential originator Credential");
+ return;
+ }
+ if (!OriginatorKeyInfo.class.isInstance(originatorKeyInfo)) {
+ log.warn("KeyInfo generated from KeyAgreementCredential was not OriginatorKeyInfo");
+ return;
+ }
+
+ agreementMethod.setOriginatorKeyInfo(OriginatorKeyInfo.class.cast(originatorKeyInfo));
+ }
+ }
+
+ /**
+ * Process {@link KeyAgreementCredential#getRecipientCredential()}.
+ *
+ * @param keyInfo the KeyInfo that is being built
+ * @param agreementMethod the AgreementMethod that is being built
+ * @param credential the Credential that is being processed
+ *
+ * @throws SecurityException
+ */
+ private void processRecipientKeyInfo(@Nonnull final KeyInfo keyInfo,
+ @Nonnull final AgreementMethod agreementMethod, @Nonnull final KeyAgreementCredential credential)
+ throws SecurityException {
+
+ if (options.emitRecipientKeyInfo) {
+ if (options.recipientKeyInfoGeneratorManager == null) {
+ log.warn("KeyInfoGeneratorManager for RecipientKeyInfo is null, can not process");
+ return;
+ }
+ if (credential.getRecipientCredential() == null) {
+ log.warn("KeyAgreementCredential recipient credential is null, can not process");
+ return;
+ }
+
+ final KeyInfo recipientKeyInfo = options.recipientKeyInfoGeneratorManager
+ .getFactory(credential.getRecipientCredential())
+ .newInstance(RecipientKeyInfo.class)
+ .generate(credential.getRecipientCredential());
+ if (recipientKeyInfo == null) {
+ log.warn("Failed to generate KeyInfo from KeyAgreementCredential recipient Credential");
+ return;
+ }
+ if (!RecipientKeyInfo.class.isInstance(recipientKeyInfo)) {
+ log.warn("KeyInfo generated from KeyAgreementCredential was not RecipientKeyInfo");
+ return;
+ }
+
+ agreementMethod.setRecipientKeyInfo(RecipientKeyInfo.class.cast(recipientKeyInfo));
+ }
+
+ }
+
+ }
+
+ /**
+ * Options to be used in the production of a {@link KeyInfo} from an {@link KeyAgreementCredential}.
+ */
+ protected class KeyAgreementOptions extends BasicOptions {
+
+ /** Emit the OriginatorKeyInfo element within AgreementMethod. */
+ private boolean emitOriginatorKeyInfo;
+
+ /** Emit the RecipientKeyInfo element within AgreementMethod. */
+ private boolean emitRecipientKeyInfo;
+
+ /** KeyInfo generator manager for OriginatorKeyInfo elements. */
+ private KeyInfoGeneratorManager originatorKeyInfoGeneratorManager;
+
+ /** KeyInfo generator manager for RecipientKeyInfo elements. */
+ private KeyInfoGeneratorManager recipientKeyInfoGeneratorManager;
+
+ /** Constructor. */
+ protected KeyAgreementOptions() {
+ emitOriginatorKeyInfo = true;
+ emitRecipientKeyInfo = true;
+
+ // TODO We can't default the general ones below until KeyInfoSupport supports PublicKey -> ECKeyValue
+ // For now limit defaults to emit DEREncodedKeyValue only
+ final KeyInfoGeneratorManager managerDEROnly = new KeyInfoGeneratorManager();
+ final BasicKeyInfoGeneratorFactory basicFactoryDEROnly = new BasicKeyInfoGeneratorFactory();
+ basicFactoryDEROnly.setEmitPublicDEREncodedKeyValue(true);
+ managerDEROnly.registerFactory(basicFactoryDEROnly);
+ originatorKeyInfoGeneratorManager = managerDEROnly;
+ recipientKeyInfoGeneratorManager = managerDEROnly;
+ /*
+ originatorKeyInfoGeneratorManager =
+ DefaultSecurityConfigurationBootstrap.buildBasicKeyInfoGeneratorManager().getDefaultManager();
+
+ recipientKeyInfoGeneratorManager =
+ DefaultSecurityConfigurationBootstrap.buildBasicKeyInfoGeneratorManager().getDefaultManager();
+ */
+ }
+
+ /** {@inheritDoc} */
+ protected KeyAgreementOptions clone() {
+ final KeyAgreementOptions clonedOptions = (KeyAgreementOptions) super.clone();
+
+ return clonedOptions;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/LocalKeyInfoCredentialResolver.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/LocalKeyInfoCredentialResolver.java
index 2ac6f4c97..2eb72b1a7 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/LocalKeyInfoCredentialResolver.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/LocalKeyInfoCredentialResolver.java
@@ -29,6 +29,10 @@ import org.opensaml.security.credential.Credential;
import org.opensaml.security.credential.CredentialResolver;
import org.opensaml.security.criteria.KeyNameCriterion;
import org.opensaml.security.criteria.PublicKeyCriterion;
+import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolutionMode;
+import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolutionMode.Mode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import net.shibboleth.utilities.java.support.annotation.ParameterName;
import net.shibboleth.utilities.java.support.logic.Constraint;
@@ -70,6 +74,9 @@ import net.shibboleth.utilities.java.support.resolver.ResolverException;
*/
public class LocalKeyInfoCredentialResolver extends BasicProviderKeyInfoCredentialResolver {
+ /** Logger. */
+ private final Logger log = LoggerFactory.getLogger(LocalKeyInfoCredentialResolver.class);
+
/** The resolver which is used to resolve local credentials. */
private final CredentialResolver localCredResolver;
@@ -105,23 +112,45 @@ public class LocalKeyInfoCredentialResolver extends BasicProviderKeyInfoCredenti
@Nullable final CriteriaSet criteriaSet, @Nonnull final List<Credential> credentials)
throws ResolverException {
- final ArrayList<Credential> localCreds = new ArrayList<>();
+ final Mode mode = criteriaSet.contains(KeyInfoCredentialResolutionMode.class)
+ ? criteriaSet.get(KeyInfoCredentialResolutionMode.class).getMode() : Mode.LOCAL;
+
+ log.debug("Resolution mode in effect is: {}", mode);
+
+ if (Mode.PUBLIC == mode) {
+ log.debug("Criteria indicates PUBLIC resolution mode, skipping explicit local credential resolution");
+ return;
+ }
- for (final Credential cred : credentials) {
- if (isLocalCredential(cred)) {
- localCreds.add(cred);
- } else if (cred.getPublicKey() != null) {
- localCreds.addAll(resolveByPublicKey(cred.getPublicKey()));
+ final ArrayList<Credential> results = new ArrayList<>();
+
+ for (final Credential inputCred : credentials) {
+ if (isLocalCredential(inputCred)) {
+ log.debug("Input credential was local, including in results");
+ results.add(inputCred);
+ } else if (inputCred.getPublicKey() != null) {
+ final Collection<? extends Credential> localCreds = resolveByPublicKey(inputCred.getPublicKey());
+ if (!localCreds.isEmpty()) {
+ log.debug("Input credential was public, resolved to local credential(s), adding to results");
+ results.addAll(localCreds);
+ } else if (Mode.BOTH == mode) {
+ log.debug("Input credential was public, did not resolve to local credential(s), "
+ + "BOTH mode in effect, including in results");
+ results.add(inputCred);
+ } else {
+ log.debug("Input credential was public, did not resolve to local credential(s), "
+ + "LOCAL mode in effect, omitting from results");
+ }
}
}
- // Also resolve local creds based on any key names that are known
+ // Also resolve local credentials based on any key names that are known
for (final String keyName : kiContext.getKeyNames()) {
- localCreds.addAll(resolveByKeyName(keyName));
+ results.addAll(resolveByKeyName(keyName));
}
credentials.clear();
- credentials.addAll(localCreds);
+ credentials.addAll(results);
}
/**
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/X509KeyInfoGeneratorFactory.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/X509KeyInfoGeneratorFactory.java
index fa2d748ba..f452dbf24 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/X509KeyInfoGeneratorFactory.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/X509KeyInfoGeneratorFactory.java
@@ -26,9 +26,6 @@ import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import net.shibboleth.utilities.java.support.collection.LazySet;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-
import org.opensaml.core.xml.XMLObject;
import org.opensaml.core.xml.XMLObjectBuilder;
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
@@ -51,6 +48,9 @@ import org.slf4j.LoggerFactory;
import com.google.common.base.Strings;
+import net.shibboleth.utilities.java.support.collection.LazySet;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
/**
* A factory implementation which produces instances of {@link KeyInfoGenerator} capable of
* handling the information contained within an {@link X509Credential}.
@@ -82,9 +82,12 @@ public class X509KeyInfoGeneratorFactory extends BasicKeyInfoGeneratorFactory {
/** {@inheritDoc} */
@Nonnull public KeyInfoGenerator newInstance() {
- //TODO lock options during cloning ?
- final X509Options newOptions = options.clone();
- return new X509KeyInfoGenerator(newOptions);
+ return newInstance(null);
+ }
+
+ /** {@inheritDoc} */
+ @Nonnull public KeyInfoGenerator newInstance(@Nullable final Class<? extends KeyInfo> type) {
+ return new X509KeyInfoGenerator(options.clone(), type);
}
/**
@@ -396,9 +399,6 @@ public class X509KeyInfoGeneratorFactory extends BasicKeyInfoGeneratorFactory {
/** The set of options to be used by the generator.*/
private X509Options options;
- /** Builder for KeyInfo objects. */
- private final XMLObjectBuilder<KeyInfo> keyInfoBuilder;
-
/** Builder for X509Data objects. */
private final XMLObjectBuilder<X509Data> x509DataBuilder;
@@ -406,13 +406,12 @@ public class X509KeyInfoGeneratorFactory extends BasicKeyInfoGeneratorFactory {
* Constructor.
*
* @param newOptions the options to be used by the generator
+ * @param type the KeyInfo elemet type
*/
- protected X509KeyInfoGenerator(final X509Options newOptions) {
- super(newOptions);
+ protected X509KeyInfoGenerator(final X509Options newOptions, final Class<? extends KeyInfo> type) {
+ super(newOptions, type);
options = newOptions;
- keyInfoBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilderOrThrow(
- KeyInfo.DEFAULT_ELEMENT_NAME);
x509DataBuilder = XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilderOrThrow(
X509Data.DEFAULT_ELEMENT_NAME);
}
@@ -431,7 +430,7 @@ public class X509KeyInfoGeneratorFactory extends BasicKeyInfoGeneratorFactory {
KeyInfo keyInfo = super.generate(credential);
if (keyInfo == null) {
- keyInfo = keyInfoBuilder.buildObject(KeyInfo.DEFAULT_ELEMENT_NAME);
+ keyInfo = buildKeyInfo();
}
final X509Data x509Data = x509DataBuilder.buildObject(X509Data.DEFAULT_ELEMENT_NAME);
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/provider/AgreementMethodKeyInfoProvider.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/provider/AgreementMethodKeyInfoProvider.java
new file mode 100644
index 000000000..9a77047d3
--- /dev/null
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/provider/AgreementMethodKeyInfoProvider.java
@@ -0,0 +1,239 @@
+/*
+ * 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.keyinfo.impl.provider;
+
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.security.SecurityException;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.CredentialContext;
+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.KeyAgreementProcessorRegistry;
+import org.opensaml.xmlsec.agreement.KeyAgreementSupport;
+import org.opensaml.xmlsec.agreement.impl.KeyAgreementParametersParser;
+import org.opensaml.xmlsec.agreement.impl.PrivateCredential;
+import org.opensaml.xmlsec.encryption.AgreementMethod;
+import org.opensaml.xmlsec.encryption.EncryptedType;
+import org.opensaml.xmlsec.encryption.OriginatorKeyInfo;
+import org.opensaml.xmlsec.encryption.RecipientKeyInfo;
+import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolutionMode;
+import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolutionMode.Mode;
+import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
+import org.opensaml.xmlsec.keyinfo.KeyInfoCriterion;
+import org.opensaml.xmlsec.keyinfo.impl.KeyInfoResolutionContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.utilities.java.support.collection.LazySet;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+/**
+ * Implementation of {@link org.opensaml.xmlsec.keyinfo.impl.KeyInfoProvider} which supports {@link AgreementMethod}.
+ */
+public class AgreementMethodKeyInfoProvider extends AbstractKeyInfoProvider {
+
+ /** Logger. */
+ private final Logger log = LoggerFactory.getLogger(AgreementMethodKeyInfoProvider.class);
+
+ /** Parser for AgreementMethod parameters. */
+ private final KeyAgreementParametersParser parametersParser = new KeyAgreementParametersParser();
+
+ /** {@inheritDoc} */
+ public boolean handles(@Nonnull final XMLObject keyInfoChild) {
+ if (!AgreementMethod.class.isInstance(keyInfoChild)) {
+ log.debug("XMLObject is not an AgreementMethod");
+ return false;
+ }
+ final AgreementMethod agreementMethod = AgreementMethod.class.cast(keyInfoChild);
+
+ final KeyAgreementProcessorRegistry registry = KeyAgreementSupport.getGlobalProcessorRegistry();
+ if (registry == null) {
+ log.debug("Global KeyAgreementProcessorRegistry is not configured");
+ return false;
+ }
+
+ if (!registry.getRegisteredAlgorithms().contains(agreementMethod.getAlgorithm())) {
+ log.debug("No KeyAgreementProcessor registered for algorithm: {}", agreementMethod.getAlgorithm());
+ return false;
+ }
+
+ if (agreementMethod.getParent() == null || agreementMethod.getParent().getParent() == null
+ || !EncryptedType.class.isInstance(agreementMethod.getParent().getParent())) {
+ log.debug("AgreementMethod is not the grandchild of an EncryptedType element");
+ return false;
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Nullable public Collection<Credential> process(@Nonnull final KeyInfoCredentialResolver resolver,
+ @Nonnull final XMLObject keyInfoChild, @Nullable final CriteriaSet criteriaSet,
+ @Nonnull final KeyInfoResolutionContext kiContext) throws SecurityException {
+
+ // Sanity check
+ if (!handles(keyInfoChild)) {
+ return null;
+ }
+
+ final AgreementMethod agreementMethod = AgreementMethod.class.cast(keyInfoChild);
+ final KeyAgreementProcessor processor =
+ KeyAgreementSupport.getGlobalProcessorRegistry().getProcessor(agreementMethod.getAlgorithm());
+
+ log.debug("Attempting to process key agreemenent for algorithm: {}", processor.getAlgorithm());
+
+ KeyAgreementCredential cred = null;
+ try {
+ final Credential originatorCredential = resolveOriginatorCredential(agreementMethod, resolver);
+ final Credential recipientCredential = resolveRecipientCredential(agreementMethod, resolver);
+
+ final KeyAgreementParameters parameters = parametersParser.parse(agreementMethod);
+ parameters.add(new PrivateCredential(recipientCredential));
+
+ final String keyAlgorithm = resolveKeyAlgorithmAndSize(agreementMethod, parameters);
+
+ cred = processor.execute(originatorCredential, keyAlgorithm, parameters);
+
+ } catch (final KeyAgreementException e) {
+ log.error("Error processing AgreementMethod with algorithm: {}", processor.getAlgorithm(), e);
+ throw new SecurityException("Error processing AgreementMethod", e);
+ }
+
+ cred.getKeyNames().addAll(kiContext.getKeyNames());
+
+ final CredentialContext credContext = buildCredentialContext(kiContext);
+ if (credContext != null) {
+ cred.getCredentialContextSet().add(credContext);
+ }
+
+ log.debug("Credential successfully produced by AgreementMethod with algorithm: {}", cred.getAlgorithm());
+ final LazySet<Credential> credentialSet = new LazySet<>();
+ credentialSet.add(cred);
+ return credentialSet;
+ }
+
+ /**
+ * Resolve the encryption algorithm URI to be used with the derived key.
+ *
+ * <p>
+ * This comes from the AgreementMethod's grandparent's EncryptionMethod child element.
+ * </p>
+ *
+ * @param agreementMethod the AgreementMethod to process
+ * @param parameters the key agreement parameters
+ *
+ * @return the encryption algorithm URI
+ *
+ * @throws SecurityException if the algorithm URI can not be resolved
+ */
+ @Nonnull private String resolveKeyAlgorithmAndSize(@Nonnull final AgreementMethod agreementMethod,
+ @Nonnull final KeyAgreementParameters parameters) throws SecurityException {
+
+ // This was already validated in handles(...)
+ final EncryptedType encrytpedType = EncryptedType.class.cast(agreementMethod.getParent().getParent());
+
+ if (encrytpedType.getEncryptionMethod() == null || encrytpedType.getEncryptionMethod().getAlgorithm() == null) {
+ throw new SecurityException("EncryptedType contains no EncryptionMethod algorithm");
+ }
+
+ //TODO handle KeySize when new KeySize param is ready; add it to parameters
+
+ return encrytpedType.getEncryptionMethod().getAlgorithm();
+ }
+
+ /**
+ * Resolve the originator {@link Credential} from the {@link OriginatorKeyInfo} element.
+ *
+ * <p>
+ * This will be the public key credential from the encrypting party.
+ * </p>
+ *
+ * @param agreementMethod the AgreementMethod to process
+ * @param resolver the KeyInfoCredentialResolver to use
+ *
+ * @return the originator credential
+ *
+ * @throws SecurityException if the originator credential can not be resolved
+ */
+ @Nonnull private Credential resolveOriginatorCredential(@Nonnull final AgreementMethod agreementMethod,
+ @Nonnull final KeyInfoCredentialResolver resolver) throws SecurityException {
+
+ if (agreementMethod.getOriginatorKeyInfo() == null) {
+ throw new SecurityException("AgreementMethod OriginatorKeyInfo was null");
+ }
+
+ final CriteriaSet criteria = new CriteriaSet(
+ new KeyInfoCriterion(agreementMethod.getOriginatorKeyInfo()),
+ new KeyInfoCredentialResolutionMode(Mode.PUBLIC));
+ try {
+ final Credential cred = resolver.resolveSingle(criteria);
+ if (cred == null) {
+ throw new SecurityException("Failed to resolve Credential from OriginatorKeyInfo ");
+ }
+ return cred;
+ } catch (final ResolverException e) {
+ throw new SecurityException("Error resolving Credential from OriginatorKeyInfo", e);
+ }
+ }
+
+ /**
+ * Resolve the recipient {@link Credential} from the {@link RecipientKeyInfo} element.
+ *
+ * <p>
+ * This will be the private key credential from the decrypting party (this party).
+ * </p>
+ *
+ * @param agreementMethod the AgreementMethod to process
+ * @param resolver the KeyInfoCredentialResolver to use
+ *
+ * @return the recipient credential
+ *
+ * @throws SecurityException if the recipient credential can not be resolved or does not contain
+ * a private key
+ */
+ @Nonnull private Credential resolveRecipientCredential(@Nonnull final AgreementMethod agreementMethod,
+ @Nonnull final KeyInfoCredentialResolver resolver) throws SecurityException {
+
+ if (agreementMethod.getRecipientKeyInfo() == null) {
+ throw new SecurityException("AgreementMethod RecipientKeyInfo was null");
+ }
+
+ final CriteriaSet criteria = new CriteriaSet(new KeyInfoCriterion(agreementMethod.getRecipientKeyInfo()));
+ try {
+ final Credential cred = resolver.resolveSingle(criteria);
+ if (cred == null) {
+ throw new SecurityException("Failed to resolve Credential from RecipientKeyInfo ");
+ }
+ if (cred.getPrivateKey() == null) {
+ throw new SecurityException("Credential resolved from RecipientKeyInfo did not contain PrivateKey");
+ }
+ return cred;
+ } catch (final ResolverException e) {
+ throw new SecurityException("Error resolving Credential from RecipientKeyInfo", e);
+ }
+ }
+
+}
diff --git a/opensaml-xmlsec-impl/src/main/resources/META-INF/services/org.opensaml.xmlsec.agreement.KeyAgreementProcessor b/opensaml-xmlsec-impl/src/main/resources/META-INF/services/org.opensaml.xmlsec.agreement.KeyAgreementProcessor
new file mode 100644
index 000000000..b5d636f8b
--- /dev/null
+++ b/opensaml-xmlsec-impl/src/main/resources/META-INF/services/org.opensaml.xmlsec.agreement.KeyAgreementProcessor
@@ -0,0 +1 @@
+org.opensaml.xmlsec.agreement.impl.ECDHKeyAgreementProcessor
\ No newline at end of file
diff --git a/opensaml-xmlsec-impl/src/main/resources/META-INF/services/org.opensaml.xmlsec.agreement.impl.KeyAgreementParameterParser b/opensaml-xmlsec-impl/src/main/resources/META-INF/services/org.opensaml.xmlsec.agreement.impl.KeyAgreementParameterParser
new file mode 100644
index 000000000..a6a165b57
--- /dev/null
+++ b/opensaml-xmlsec-impl/src/main/resources/META-INF/services/org.opensaml.xmlsec.agreement.impl.KeyAgreementParameterParser
@@ -0,0 +1,4 @@
+org.opensaml.xmlsec.agreement.impl.DigestMethod$Parser
+org.opensaml.xmlsec.agreement.impl.KANonce$Parser
+org.opensaml.xmlsec.derivation.impl.ConcatKDF$Parser
+org.opensaml.xmlsec.derivation.impl.PBKDF2$Parser
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/DigestMethodTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/DigestMethodTest.java
index 172f53d45..93eefe50e 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/DigestMethodTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/DigestMethodTest.java
@@ -17,7 +17,7 @@
package org.opensaml.xmlsec.agreement.impl;
-import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.testing.XMLObjectBaseTestCase;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.xmlsec.signature.support.SignatureConstants;
import org.testng.Assert;
@@ -29,7 +29,7 @@ import net.shibboleth.utilities.java.support.component.UnmodifiableComponentExce
/**
*
*/
-public class DigestMethodTest extends OpenSAMLInitBaseTestCase {
+public class DigestMethodTest extends XMLObjectBaseTestCase {
@Test
public void basic() throws ComponentInitializationException {
@@ -57,6 +57,27 @@ public class DigestMethodTest extends OpenSAMLInitBaseTestCase {
}
+ @Test
+ public void fromXMLObject() throws Exception {
+ org.opensaml.xmlsec.signature.DigestMethod xmlObject = buildXMLObject(org.opensaml.xmlsec.signature.DigestMethod.DEFAULT_ELEMENT_NAME);
+ xmlObject.setAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA256);
+
+ DigestMethod parameter = DigestMethod.fromXMLObject(xmlObject);
+ Assert.assertNotNull(parameter);
+ Assert.assertTrue(parameter.isInitialized());
+ Assert.assertEquals(parameter.getAlgorithm(), SignatureConstants.ALGO_ID_DIGEST_SHA256);
+
+ xmlObject.setAlgorithm(null);
+
+
+ try {
+ DigestMethod.fromXMLObject(xmlObject);
+ Assert.fail("Should have failed invalid XMLObject");
+ } catch (ComponentInitializationException e) {
+ //expected
+ }
+ }
+
@Test(expectedExceptions = ComponentInitializationException.class)
public void missingValue() throws ComponentInitializationException {
DigestMethod digest = new DigestMethod();
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/KANonceTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/KANonceTest.java
index dfc023502..fb5989803 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/KANonceTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/KANonceTest.java
@@ -17,7 +17,7 @@
package org.opensaml.xmlsec.agreement.impl;
-import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.testing.XMLObjectBaseTestCase;
import org.opensaml.core.xml.XMLObject;
import org.testng.Assert;
import org.testng.annotations.Test;
@@ -28,7 +28,7 @@ import net.shibboleth.utilities.java.support.component.UnmodifiableComponentExce
/**
*
*/
-public class KANonceTest extends OpenSAMLInitBaseTestCase {
+public class KANonceTest extends XMLObjectBaseTestCase {
@Test
public void basic() throws ComponentInitializationException {
@@ -56,6 +56,27 @@ public class KANonceTest extends OpenSAMLInitBaseTestCase {
}
+ @Test
+ public void fromXMLObject() throws Exception {
+ org.opensaml.xmlsec.encryption.KANonce xmlObject = buildXMLObject(org.opensaml.xmlsec.encryption.KANonce.DEFAULT_ELEMENT_NAME);
+ xmlObject.setValue("someBase64==");
+
+ KANonce parameter = KANonce.fromXMLObject(xmlObject);
+ Assert.assertNotNull(parameter);
+ Assert.assertTrue(parameter.isInitialized());
+ Assert.assertEquals(parameter.getValue(), "someBase64==");
+
+ xmlObject.setValue(null);
+
+
+ try {
+ KANonce.fromXMLObject(xmlObject);
+ Assert.fail("Should have failed invalid XMLObject");
+ } catch (ComponentInitializationException e) {
+ //expected
+ }
+ }
+
@Test(expectedExceptions = ComponentInitializationException.class)
public void missingValue() throws ComponentInitializationException {
KANonce nonce = new KANonce();
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/KeyAgreementParametersParserTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/KeyAgreementParametersParserTest.java
new file mode 100644
index 000000000..7b80c710a
--- /dev/null
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/KeyAgreementParametersParserTest.java
@@ -0,0 +1,259 @@
+/*
+ * 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.agreement.impl;
+
+import org.opensaml.core.testing.XMLObjectBaseTestCase;
+import org.opensaml.xmlsec.agreement.KeyAgreementException;
+import org.opensaml.xmlsec.agreement.KeyAgreementParameters;
+import org.opensaml.xmlsec.derivation.impl.ConcatKDF;
+import org.opensaml.xmlsec.derivation.impl.PBKDF2;
+import org.opensaml.xmlsec.encryption.AgreementMethod;
+import org.opensaml.xmlsec.encryption.ConcatKDFParams;
+import org.opensaml.xmlsec.encryption.IterationCount;
+import org.opensaml.xmlsec.encryption.KeyDerivationMethod;
+import org.opensaml.xmlsec.encryption.KeyLength;
+import org.opensaml.xmlsec.encryption.PBKDF2Params;
+import org.opensaml.xmlsec.encryption.PRF;
+import org.opensaml.xmlsec.encryption.Salt;
+import org.opensaml.xmlsec.encryption.Specified;
+import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
+import org.opensaml.xmlsec.signature.support.SignatureConstants;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ *
+ */
+public class KeyAgreementParametersParserTest extends XMLObjectBaseTestCase {
+
+ @Test
+ public void ECDHWithConcatKDF() throws KeyAgreementException {
+ AgreementMethod agreementMethod = buildXMLObject(AgreementMethod.DEFAULT_ELEMENT_NAME);
+ agreementMethod.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+
+ KeyDerivationMethod kdm = buildXMLObject(KeyDerivationMethod.DEFAULT_ELEMENT_NAME);
+ kdm.setAlgorithm(EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF);
+
+ ConcatKDFParams xmlParams = buildXMLObject(ConcatKDFParams.DEFAULT_ELEMENT_NAME);
+ xmlParams.setAlgorithmID("00AA");
+ xmlParams.setPartyUInfo("00BB");
+ xmlParams.setPartyVInfo("00CC");
+ xmlParams.setSuppPubInfo("00DD");
+ xmlParams.setSuppPrivInfo("00EE");
+
+ org.opensaml.xmlsec.signature.DigestMethod digestMethod = buildXMLObject(org.opensaml.xmlsec.signature.DigestMethod.DEFAULT_ELEMENT_NAME);
+ digestMethod.setAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA512);
+ xmlParams.setDigestMethod(digestMethod);
+
+ kdm.getUnknownXMLObjects().add(xmlParams);
+
+ agreementMethod.getUnknownXMLObjects().add(kdm);
+
+ KeyAgreementParametersParser parser = new KeyAgreementParametersParser();
+
+ KeyAgreementParameters parameters = parser.parse(agreementMethod);
+ Assert.assertNotNull(parameters);
+ Assert.assertEquals(parameters.size(), 1);
+
+ Assert.assertTrue(parameters.contains(ConcatKDF.class));
+
+ ConcatKDF kdf = parameters.get(ConcatKDF.class);
+ Assert.assertTrue(kdf.isInitialized());
+ Assert.assertEquals(kdf.getDigestMethod(), SignatureConstants.ALGO_ID_DIGEST_SHA512);
+ Assert.assertEquals(kdf.getAlgorithmID(), "AA");
+ Assert.assertEquals(kdf.getPartyUInfo(), "BB");
+ Assert.assertEquals(kdf.getPartyVInfo(), "CC");
+ Assert.assertEquals(kdf.getSuppPubInfo(), "DD");
+ Assert.assertEquals(kdf.getSuppPrivInfo(), "EE");
+ }
+
+ @Test
+ public void ECDHWithPBKDF2() throws KeyAgreementException {
+ AgreementMethod agreementMethod = buildXMLObject(AgreementMethod.DEFAULT_ELEMENT_NAME);
+ agreementMethod.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+
+ KeyDerivationMethod kdm = buildXMLObject(KeyDerivationMethod.DEFAULT_ELEMENT_NAME);
+ kdm.setAlgorithm(EncryptionConstants.ALGO_ID_KEYDERIVATION_PBKDF2);
+
+ PBKDF2Params xmlParams = buildXMLObject(PBKDF2Params.DEFAULT_ELEMENT_NAME);
+
+ IterationCount iterationCount = buildXMLObject(IterationCount.DEFAULT_ELEMENT_NAME);
+ iterationCount.setValue(1500);
+ xmlParams.setIterationCount(iterationCount);
+
+ KeyLength keyLength = buildXMLObject(KeyLength.DEFAULT_ELEMENT_NAME);
+ keyLength.setValue(32);
+ xmlParams.setKeyLength(keyLength);
+
+ PRF prf = buildXMLObject(PRF.DEFAULT_ELEMENT_NAME);
+ prf.setAlgorithm(SignatureConstants.ALGO_ID_MAC_HMAC_SHA512);
+ xmlParams.setPRF(prf);
+
+ Salt salt = buildXMLObject(Salt.DEFAULT_ELEMENT_NAME);
+ Specified specified = buildXMLObject(Specified.DEFAULT_ELEMENT_NAME);
+ specified.setValue("ABCD");
+ salt.setSpecified(specified);
+ xmlParams.setSalt(salt);
+
+ kdm.getUnknownXMLObjects().add(xmlParams);
+
+ agreementMethod.getUnknownXMLObjects().add(kdm);
+
+ KeyAgreementParametersParser parser = new KeyAgreementParametersParser();
+
+ KeyAgreementParameters parameters = parser.parse(agreementMethod);
+ Assert.assertNotNull(parameters);
+ Assert.assertEquals(parameters.size(), 1);
+
+ Assert.assertTrue(parameters.contains(PBKDF2.class));
+
+ PBKDF2 kdf = parameters.get(PBKDF2.class);
+ Assert.assertTrue(kdf.isInitialized());
+ Assert.assertEquals(kdf.getIterationCount().intValue(), 1500);
+ Assert.assertEquals(kdf.getKeyLength().intValue(), 256);
+ Assert.assertEquals(kdf.getPRF(), SignatureConstants.ALGO_ID_MAC_HMAC_SHA512);
+ Assert.assertEquals(kdf.getSalt(), "ABCD");
+ }
+
+ @Test
+ public void DHWithConcatKDF() throws KeyAgreementException {
+ AgreementMethod agreementMethod = buildXMLObject(AgreementMethod.DEFAULT_ELEMENT_NAME);
+ agreementMethod.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH_EXPLICIT_KDF);
+
+ KeyDerivationMethod kdm = buildXMLObject(KeyDerivationMethod.DEFAULT_ELEMENT_NAME);
+ kdm.setAlgorithm(EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF);
+
+ ConcatKDFParams xmlParams = buildXMLObject(ConcatKDFParams.DEFAULT_ELEMENT_NAME);
+ xmlParams.setAlgorithmID("00AA");
+ xmlParams.setPartyUInfo("00BB");
+ xmlParams.setPartyVInfo("00CC");
+ xmlParams.setSuppPubInfo("00DD");
+ xmlParams.setSuppPrivInfo("00EE");
+
+ org.opensaml.xmlsec.signature.DigestMethod digestMethod = buildXMLObject(org.opensaml.xmlsec.signature.DigestMethod.DEFAULT_ELEMENT_NAME);
+ digestMethod.setAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA512);
+ xmlParams.setDigestMethod(digestMethod);
+
+ kdm.getUnknownXMLObjects().add(xmlParams);
+
+ agreementMethod.getUnknownXMLObjects().add(kdm);
+
+ KeyAgreementParametersParser parser = new KeyAgreementParametersParser();
+
+ KeyAgreementParameters parameters = parser.parse(agreementMethod);
+ Assert.assertNotNull(parameters);
+ Assert.assertEquals(parameters.size(), 1);
+
+ Assert.assertTrue(parameters.contains(ConcatKDF.class));
+
+ ConcatKDF kdf = parameters.get(ConcatKDF.class);
+ Assert.assertTrue(kdf.isInitialized());
+ Assert.assertEquals(kdf.getDigestMethod(), SignatureConstants.ALGO_ID_DIGEST_SHA512);
+ Assert.assertEquals(kdf.getAlgorithmID(), "AA");
+ Assert.assertEquals(kdf.getPartyUInfo(), "BB");
+ Assert.assertEquals(kdf.getPartyVInfo(), "CC");
+ Assert.assertEquals(kdf.getSuppPubInfo(), "DD");
+ Assert.assertEquals(kdf.getSuppPrivInfo(), "EE");
+ }
+
+ @Test
+ public void DHWithLegacyKDF() throws KeyAgreementException {
+ AgreementMethod agreementMethod = buildXMLObject(AgreementMethod.DEFAULT_ELEMENT_NAME);
+ agreementMethod.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH);
+
+ org.opensaml.xmlsec.encryption.KANonce xmlNonce = buildXMLObject(org.opensaml.xmlsec.encryption.KANonce.DEFAULT_ELEMENT_NAME);
+ xmlNonce.setValue("ABCD");
+ agreementMethod.getUnknownXMLObjects().add(xmlNonce);
+
+ org.opensaml.xmlsec.signature.DigestMethod xmlDigest = buildXMLObject(org.opensaml.xmlsec.signature.DigestMethod.DEFAULT_ELEMENT_NAME);
+ xmlDigest.setAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA512);
+ agreementMethod.getUnknownXMLObjects().add(xmlDigest);
+
+ KeyAgreementParametersParser parser = new KeyAgreementParametersParser();
+
+ KeyAgreementParameters parameters = parser.parse(agreementMethod);
+ Assert.assertNotNull(parameters);
+ Assert.assertEquals(parameters.size(), 2);
+
+ Assert.assertTrue(parameters.contains(KANonce.class));
+ KANonce nonce = parameters.get(KANonce.class);
+ Assert.assertTrue(nonce.isInitialized());
+ Assert.assertEquals(nonce.getValue(), "ABCD");
+ Assert.assertTrue(parameters.contains(KANonce.class));
+
+ Assert.assertTrue(parameters.contains(DigestMethod.class));
+ DigestMethod digestMethod = parameters.get(DigestMethod.class);
+ Assert.assertTrue(digestMethod.isInitialized());
+ Assert.assertEquals(digestMethod.getAlgorithm(), SignatureConstants.ALGO_ID_DIGEST_SHA512);
+ }
+
+ @Test(expectedExceptions = KeyAgreementException.class)
+ public void unknownChildType() throws KeyAgreementException {
+ AgreementMethod agreementMethod = buildXMLObject(AgreementMethod.DEFAULT_ELEMENT_NAME);
+ agreementMethod.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+
+ agreementMethod.getUnknownXMLObjects().add(buildXMLObject(simpleXMLObjectQName));
+
+ KeyAgreementParametersParser parser = new KeyAgreementParametersParser();
+
+ parser.parse(agreementMethod);
+ }
+
+ @Test(expectedExceptions = KeyAgreementException.class)
+ public void unknownKeyDerivationAlgorithm() throws KeyAgreementException {
+ AgreementMethod agreementMethod = buildXMLObject(AgreementMethod.DEFAULT_ELEMENT_NAME);
+ agreementMethod.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+
+ KeyDerivationMethod kdm = buildXMLObject(KeyDerivationMethod.DEFAULT_ELEMENT_NAME);
+ kdm.setAlgorithm("UNKNOWN");
+
+ agreementMethod.getUnknownXMLObjects().add(kdm);
+
+ KeyAgreementParametersParser parser = new KeyAgreementParametersParser();
+
+ parser.parse(agreementMethod);
+ }
+
+ @Test(expectedExceptions = KeyAgreementException.class)
+ public void invalidParamData() throws KeyAgreementException {
+ AgreementMethod agreementMethod = buildXMLObject(AgreementMethod.DEFAULT_ELEMENT_NAME);
+ agreementMethod.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+
+ KeyDerivationMethod kdm = buildXMLObject(KeyDerivationMethod.DEFAULT_ELEMENT_NAME);
+ kdm.setAlgorithm(EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF);
+
+ ConcatKDFParams xmlParams = buildXMLObject(ConcatKDFParams.DEFAULT_ELEMENT_NAME);
+ xmlParams.setAlgorithmID("01AA");
+ xmlParams.setPartyUInfo("02BB");
+ xmlParams.setPartyVInfo("03CC");
+ xmlParams.setSuppPubInfo("04DD");
+ xmlParams.setSuppPrivInfo("05EE");
+
+ org.opensaml.xmlsec.signature.DigestMethod digestMethod = buildXMLObject(org.opensaml.xmlsec.signature.DigestMethod.DEFAULT_ELEMENT_NAME);
+ digestMethod.setAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA512);
+ xmlParams.setDigestMethod(digestMethod);
+
+ kdm.getUnknownXMLObjects().add(xmlParams);
+
+ agreementMethod.getUnknownXMLObjects().add(kdm);
+
+ KeyAgreementParametersParser parser = new KeyAgreementParametersParser();
+
+ parser.parse(agreementMethod);
+ }
+}
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/config/impl/GlobalKeyAgreementProcessorRegistryTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/config/impl/GlobalKeyAgreementProcessorRegistryTest.java
new file mode 100644
index 000000000..337e649f1
--- /dev/null
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/config/impl/GlobalKeyAgreementProcessorRegistryTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.config.impl;
+
+import java.util.Set;
+
+import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
+import org.opensaml.xmlsec.agreement.KeyAgreementProcessorRegistry;
+import org.opensaml.xmlsec.agreement.KeyAgreementSupport;
+import org.opensaml.xmlsec.agreement.impl.ECDHKeyAgreementProcessor;
+import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ *
+ */
+public class GlobalKeyAgreementProcessorRegistryTest extends OpenSAMLInitBaseTestCase {
+
+ @Test
+ public void basic() {
+ KeyAgreementProcessorRegistry registry = KeyAgreementSupport.getGlobalProcessorRegistry();
+
+ Assert.assertNotNull(registry);
+ Assert.assertEquals(registry.getRegisteredAlgorithms().size(), 1);
+
+ Assert.assertEquals(registry.getRegisteredAlgorithms(), Set.of(EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES));
+ Assert.assertNotNull(registry.getProcessor(EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES));
+ Assert.assertTrue(ECDHKeyAgreementProcessor.class.isInstance(
+ registry.getProcessor(EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES)));
+ }
+
+}
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/ConcatKDFTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/ConcatKDFTest.java
index 87d8dc2f4..761a73f2f 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/ConcatKDFTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/ConcatKDFTest.java
@@ -22,11 +22,15 @@ import javax.crypto.SecretKey;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.testing.XMLObjectBaseTestCase;
import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.util.XMLObjectSupport;
+import org.opensaml.xmlsec.agreement.impl.KANonce;
import org.opensaml.xmlsec.derivation.KeyDerivationException;
import org.opensaml.xmlsec.encryption.ConcatKDFParams;
import org.opensaml.xmlsec.encryption.KeyDerivationMethod;
import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
+import org.opensaml.xmlsec.signature.DigestMethod;
import org.opensaml.xmlsec.signature.support.SignatureConstants;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
@@ -38,7 +42,7 @@ import net.shibboleth.utilities.java.support.component.ComponentInitializationEx
/**
*
*/
-public class ConcatKDFTest extends OpenSAMLInitBaseTestCase {
+public class ConcatKDFTest extends XMLObjectBaseTestCase {
@Test
public void defaultProperties() throws Exception {
@@ -155,6 +159,110 @@ public class ConcatKDFTest extends OpenSAMLInitBaseTestCase {
Assert.assertNotNull(kdmParams.getDigestMethod());
Assert.assertEquals(kdmParams.getDigestMethod().getAlgorithm(), SignatureConstants.ALGO_ID_DIGEST_SHA512);
}
+
+ @Test
+ public void fromXMLObject() throws Exception {
+ KeyDerivationMethod xmlKDM = buildXMLObject(KeyDerivationMethod.DEFAULT_ELEMENT_NAME);
+ xmlKDM.setAlgorithm(EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF);
+
+ ConcatKDFParams xmlParams= buildXMLObject(ConcatKDFParams.DEFAULT_ELEMENT_NAME);
+ xmlKDM.getUnknownXMLObjects().add(xmlParams);
+
+ DigestMethod xmlDigest = buildXMLObject(DigestMethod.DEFAULT_ELEMENT_NAME);
+ xmlDigest.setAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA256);
+ xmlParams.setDigestMethod(xmlDigest);
+
+ xmlParams.setAlgorithmID("00AA");
+ xmlParams.setPartyUInfo("00BB");
+ xmlParams.setPartyVInfo("00CC");
+ xmlParams.setSuppPubInfo("00DD");
+ xmlParams.setSuppPrivInfo("00EE");
+
+ ConcatKDF parameter = ConcatKDF.fromXMLObject(xmlKDM);
+ Assert.assertNotNull(parameter);
+ Assert.assertTrue(parameter.isInitialized());
+
+ KeyDerivationMethod xmlKDMBad = null;
+ ConcatKDFParams xmlParamsBad = null;
+
+ xmlKDMBad = XMLObjectSupport.cloneXMLObject(xmlKDM);
+ xmlKDMBad.setAlgorithm(EncryptionConstants.ALGO_ID_KEYDERIVATION_PBKDF2);
+ try {
+ ConcatKDF.fromXMLObject(xmlKDMBad);
+ Assert.fail("Should have failed invalid XMLObject");
+ } catch (ComponentInitializationException e) {
+ //expected
+ }
+
+ xmlKDMBad = XMLObjectSupport.cloneXMLObject(xmlKDM);
+ xmlKDMBad.getUnknownXMLObjects().add(buildXMLObject(simpleXMLObjectQName));
+ try {
+ ConcatKDF.fromXMLObject(xmlKDMBad);
+ Assert.fail("Should have failed invalid XMLObject");
+ } catch (ComponentInitializationException e) {
+ //expected
+ }
+
+ xmlKDMBad = XMLObjectSupport.cloneXMLObject(xmlKDM);
+ xmlParamsBad = (ConcatKDFParams) xmlKDMBad.getUnknownXMLObjects().get(0);
+ xmlParamsBad.setDigestMethod(null);
+ try {
+ ConcatKDF.fromXMLObject(xmlKDMBad);
+ Assert.fail("Should have failed invalid XMLObject");
+ } catch (ComponentInitializationException e) {
+ //expected
+ }
+
+ xmlKDMBad = XMLObjectSupport.cloneXMLObject(xmlKDM);
+ xmlParamsBad = (ConcatKDFParams) xmlKDMBad.getUnknownXMLObjects().get(0);
+ xmlParamsBad.setAlgorithmID("01AA");
+ try {
+ ConcatKDF.fromXMLObject(xmlKDMBad);
+ Assert.fail("Should have failed invalid XMLObject");
+ } catch (ComponentInitializationException e) {
+ //expected
+ }
+
+ xmlKDMBad = XMLObjectSupport.cloneXMLObject(xmlKDM);
+ xmlParamsBad = (ConcatKDFParams) xmlKDMBad.getUnknownXMLObjects().get(0);
+ xmlParamsBad.setPartyUInfo("01BB");
+ try {
+ ConcatKDF.fromXMLObject(xmlKDMBad);
+ Assert.fail("Should have failed invalid XMLObject");
+ } catch (ComponentInitializationException e) {
+ //expected
+ }
+
+ xmlKDMBad = XMLObjectSupport.cloneXMLObject(xmlKDM);
+ xmlParamsBad = (ConcatKDFParams) xmlKDMBad.getUnknownXMLObjects().get(0);
+ xmlParamsBad.setPartyVInfo("01CC");
+ try {
+ ConcatKDF.fromXMLObject(xmlKDMBad);
+ Assert.fail("Should have failed invalid XMLObject");
+ } catch (ComponentInitializationException e) {
+ //expected
+ }
+
+ xmlKDMBad = XMLObjectSupport.cloneXMLObject(xmlKDM);
+ xmlParamsBad = (ConcatKDFParams) xmlKDMBad.getUnknownXMLObjects().get(0);
+ xmlParamsBad.setSuppPubInfo("01DD");
+ try {
+ ConcatKDF.fromXMLObject(xmlKDMBad);
+ Assert.fail("Should have failed invalid XMLObject");
+ } catch (ComponentInitializationException e) {
+ //expected
+ }
+
+ xmlKDMBad = XMLObjectSupport.cloneXMLObject(xmlKDM);
+ xmlParamsBad = (ConcatKDFParams) xmlKDMBad.getUnknownXMLObjects().get(0);
+ xmlParamsBad.setSuppPrivInfo("01EE");
+ try {
+ ConcatKDF.fromXMLObject(xmlKDMBad);
+ Assert.fail("Should have failed invalid XMLObject");
+ } catch (ComponentInitializationException e) {
+ //expected
+ }
+ }
@Test
public void cloning() throws Exception {
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/PBKDF2Test.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/PBKDF2Test.java
index 438d95f1f..9ff95f482 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/PBKDF2Test.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/PBKDF2Test.java
@@ -23,14 +23,20 @@ import javax.crypto.SecretKey;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
-import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.testing.XMLObjectBaseTestCase;
import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.util.XMLObjectSupport;
import org.opensaml.xmlsec.algorithm.AlgorithmRegistry;
import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
import org.opensaml.xmlsec.algorithm.BlockEncryptionAlgorithm;
import org.opensaml.xmlsec.derivation.KeyDerivationException;
+import org.opensaml.xmlsec.encryption.IterationCount;
import org.opensaml.xmlsec.encryption.KeyDerivationMethod;
+import org.opensaml.xmlsec.encryption.KeyLength;
import org.opensaml.xmlsec.encryption.PBKDF2Params;
+import org.opensaml.xmlsec.encryption.PRF;
+import org.opensaml.xmlsec.encryption.Salt;
+import org.opensaml.xmlsec.encryption.Specified;
import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
import org.opensaml.xmlsec.signature.support.SignatureConstants;
import org.testng.Assert;
@@ -48,7 +54,7 @@ import net.shibboleth.utilities.java.support.component.ComponentInitializationEx
/**
*
*/
-public class PBKDF2Test extends OpenSAMLInitBaseTestCase {
+public class PBKDF2Test extends XMLObjectBaseTestCase {
@Test
public void defaultProperties() throws Exception {
@@ -75,7 +81,7 @@ public class PBKDF2Test extends OpenSAMLInitBaseTestCase {
kdf.setKeyLength(256);
kdf.setPRF(SignatureConstants.ALGO_ID_MAC_HMAC_SHA512);
kdf.setRandom(sr);
- kdf.setSalt("ABCDEFGH");
+ kdf.setSalt("ABCD");
kdf.initialize();
Assert.assertEquals(kdf.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYDERIVATION_PBKDF2);
@@ -85,7 +91,7 @@ public class PBKDF2Test extends OpenSAMLInitBaseTestCase {
Assert.assertEquals(kdf.getKeyLength().intValue(), 256);
Assert.assertEquals(kdf.getPRF(), SignatureConstants.ALGO_ID_MAC_HMAC_SHA512);
Assert.assertSame(kdf.getRandom(), sr);
- Assert.assertEquals(kdf.getSalt(), "ABCDEFGH");
+ Assert.assertEquals(kdf.getSalt(), "ABCD");
}
@Test(expectedExceptions = ComponentInitializationException.class)
@@ -115,7 +121,7 @@ public class PBKDF2Test extends OpenSAMLInitBaseTestCase {
kdf.setIterationCount(3000);
kdf.setKeyLength(256);
kdf.setPRF(SignatureConstants.ALGO_ID_MAC_HMAC_SHA512);
- kdf.setSalt("ABCDEFGH");
+ kdf.setSalt("ABCD");
kdf.initialize();
XMLObject xmlObject = kdf.buildXMLObject();
@@ -139,7 +145,104 @@ public class PBKDF2Test extends OpenSAMLInitBaseTestCase {
Assert.assertNotNull(kdmParams.getSalt());
Assert.assertNotNull(kdmParams.getSalt().getSpecified());
- Assert.assertEquals(kdmParams.getSalt().getSpecified().getValue(), "ABCDEFGH");
+ Assert.assertEquals(kdmParams.getSalt().getSpecified().getValue(), "ABCD");
+ }
+
+ @Test
+ public void fromXMLObject() throws Exception {
+ KeyDerivationMethod xmlKDM = buildXMLObject(KeyDerivationMethod.DEFAULT_ELEMENT_NAME);
+ xmlKDM.setAlgorithm(EncryptionConstants.ALGO_ID_KEYDERIVATION_PBKDF2);
+
+ PBKDF2Params xmlParams= buildXMLObject(PBKDF2Params.DEFAULT_ELEMENT_NAME);
+ xmlKDM.getUnknownXMLObjects().add(xmlParams);
+
+ IterationCount xmlIterationCount = buildXMLObject(IterationCount.DEFAULT_ELEMENT_NAME);
+ xmlIterationCount.setValue(3000);
+ xmlParams.setIterationCount(xmlIterationCount);
+
+ KeyLength xmlKeyLength = buildXMLObject(KeyLength.DEFAULT_ELEMENT_NAME);
+ xmlKeyLength.setValue(16);
+ xmlParams.setKeyLength(xmlKeyLength);
+
+ PRF xmlPRF = buildXMLObject(PRF.DEFAULT_ELEMENT_NAME);
+ xmlPRF.setAlgorithm(SignatureConstants.ALGO_ID_MAC_HMAC_SHA256);
+ xmlParams.setPRF(xmlPRF);
+
+ Salt xmlSalt = buildXMLObject(Salt.DEFAULT_ELEMENT_NAME);
+ Specified xmlSpecified = buildXMLObject(Specified.DEFAULT_ELEMENT_NAME);
+ xmlSpecified.setValue("ABCD");
+ xmlSalt.setSpecified(xmlSpecified);
+ xmlParams.setSalt(xmlSalt);
+
+ PBKDF2 parameter = PBKDF2.fromXMLObject(xmlKDM);
+ Assert.assertNotNull(parameter);
+ Assert.assertTrue(parameter.isInitialized());
+
+ Assert.assertEquals(parameter.getIterationCount().intValue(), 3000);
+ Assert.assertEquals(parameter.getKeyLength().intValue(), 128);
+ Assert.assertEquals(parameter.getPRF(), SignatureConstants.ALGO_ID_MAC_HMAC_SHA256);
+ Assert.assertEquals(parameter.getSalt(), "ABCD");
+
+ KeyDerivationMethod xmlKDMBad = null;
+ PBKDF2Params xmlParamsBad = null;
+
+ xmlKDMBad = XMLObjectSupport.cloneXMLObject(xmlKDM);
+ xmlKDMBad.setAlgorithm(EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF);
+ try {
+ PBKDF2.fromXMLObject(xmlKDMBad);
+ Assert.fail("Should have failed invalid XMLObject");
+ } catch (ComponentInitializationException e) {
+ //expected
+ }
+
+ xmlKDMBad = XMLObjectSupport.cloneXMLObject(xmlKDM);
+ xmlKDMBad.getUnknownXMLObjects().add(buildXMLObject(simpleXMLObjectQName));
+ try {
+ PBKDF2.fromXMLObject(xmlKDMBad);
+ Assert.fail("Should have failed invalid XMLObject");
+ } catch (ComponentInitializationException e) {
+ //expected
+ }
+
+ xmlKDMBad = XMLObjectSupport.cloneXMLObject(xmlKDM);
+ xmlParamsBad = (PBKDF2Params) xmlKDMBad.getUnknownXMLObjects(PBKDF2Params.DEFAULT_ELEMENT_NAME).get(0);
+ xmlParamsBad.setIterationCount(null);
+ try {
+ PBKDF2.fromXMLObject(xmlKDMBad);
+ Assert.fail("Should have failed invalid XMLObject");
+ } catch (ComponentInitializationException e) {
+ //expected
+ }
+
+ xmlKDMBad = XMLObjectSupport.cloneXMLObject(xmlKDM);
+ xmlParamsBad = (PBKDF2Params) xmlKDMBad.getUnknownXMLObjects(PBKDF2Params.DEFAULT_ELEMENT_NAME).get(0);
+ xmlParamsBad.setKeyLength(null);
+ try {
+ PBKDF2.fromXMLObject(xmlKDMBad);
+ Assert.fail("Should have failed invalid XMLObject");
+ } catch (ComponentInitializationException e) {
+ //expected
+ }
+
+ xmlKDMBad = XMLObjectSupport.cloneXMLObject(xmlKDM);
+ xmlParamsBad = (PBKDF2Params) xmlKDMBad.getUnknownXMLObjects(PBKDF2Params.DEFAULT_ELEMENT_NAME).get(0);
+ xmlParamsBad.setPRF(null);
+ try {
+ PBKDF2.fromXMLObject(xmlKDMBad);
+ Assert.fail("Should have failed invalid XMLObject");
+ } catch (ComponentInitializationException e) {
+ //expected
+ }
+
+ xmlKDMBad = XMLObjectSupport.cloneXMLObject(xmlKDM);
+ xmlParamsBad = (PBKDF2Params) xmlKDMBad.getUnknownXMLObjects(PBKDF2Params.DEFAULT_ELEMENT_NAME).get(0);
+ xmlParamsBad.setSalt(null);
+ try {
+ PBKDF2.fromXMLObject(xmlKDMBad);
+ Assert.fail("Should have failed invalid XMLObject");
+ } catch (ComponentInitializationException e) {
+ //expected
+ }
}
@Test
@@ -152,7 +255,7 @@ public class PBKDF2Test extends OpenSAMLInitBaseTestCase {
kdf.setKeyLength(256);
kdf.setPRF(SignatureConstants.ALGO_ID_MAC_HMAC_SHA512);
kdf.setRandom(sr);
- kdf.setSalt("ABCDEFGH");
+ kdf.setSalt("ABCD");
kdf.initialize();
PBKDF2 cloned = kdf.clone();
@@ -164,7 +267,7 @@ public class PBKDF2Test extends OpenSAMLInitBaseTestCase {
Assert.assertEquals(cloned.getKeyLength().intValue(), 256);
Assert.assertEquals(cloned.getPRF(), SignatureConstants.ALGO_ID_MAC_HMAC_SHA512);
Assert.assertSame(cloned.getRandom(), sr);
- Assert.assertEquals(cloned.getSalt(), "ABCDEFGH");
+ Assert.assertEquals(cloned.getSalt(), "ABCD");
}
@Test
@@ -195,7 +298,7 @@ public class PBKDF2Test extends OpenSAMLInitBaseTestCase {
kdf.setIterationCount(3000);
kdf.setKeyLength(256);
kdf.setPRF(SignatureConstants.ALGO_ID_MAC_HMAC_SHA512);
- kdf.setSalt("ABCDEFGH");
+ kdf.setSalt("ABCD");
kdf.initialize();
byte[] secret = Hex.decodeHex("DEADBEEF");
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/AgreementMethodTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/AgreementMethodTest.java
new file mode 100644
index 000000000..90239a793
--- /dev/null
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/AgreementMethodTest.java
@@ -0,0 +1,308 @@
+/*
+ * 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.keyinfo.impl;
+
+import java.security.KeyPair;
+import java.security.spec.ECGenParameterSpec;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.namespace.QName;
+
+import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.testing.XMLObjectBaseTestCase;
+import org.opensaml.security.SecurityException;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.CredentialResolver;
+import org.opensaml.security.credential.CredentialSupport;
+import org.opensaml.security.credential.impl.CollectionCredentialResolver;
+import org.opensaml.security.crypto.JCAConstants;
+import org.opensaml.security.crypto.KeySupport;
+import org.opensaml.xmlsec.agreement.KeyAgreementCredential;
+import org.opensaml.xmlsec.agreement.KeyAgreementParameters;
+import org.opensaml.xmlsec.agreement.impl.ECDHKeyAgreementProcessor;
+import org.opensaml.xmlsec.agreement.impl.PrivateCredential;
+import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
+import org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap;
+import org.opensaml.xmlsec.derivation.impl.ConcatKDF;
+import org.opensaml.xmlsec.encryption.EncryptedData;
+import org.opensaml.xmlsec.encryption.EncryptedType;
+import org.opensaml.xmlsec.encryption.EncryptionMethod;
+import org.opensaml.xmlsec.encryption.KeySize;
+import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
+import org.opensaml.xmlsec.keyinfo.KeyInfoCriterion;
+import org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorManager;
+import org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;
+import org.opensaml.xmlsec.keyinfo.impl.provider.AgreementMethodKeyInfoProvider;
+import org.opensaml.xmlsec.keyinfo.impl.provider.DEREncodedKeyValueProvider;
+import org.opensaml.xmlsec.keyinfo.impl.provider.DSAKeyValueProvider;
+import org.opensaml.xmlsec.keyinfo.impl.provider.InlineX509DataProvider;
+import org.opensaml.xmlsec.keyinfo.impl.provider.RSAKeyValueProvider;
+import org.opensaml.xmlsec.signature.KeyInfo;
+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;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+/**
+ *
+ */
+public class AgreementMethodTest extends XMLObjectBaseTestCase {
+
+ private LocalKeyInfoCredentialResolver resolver;
+
+ private Credential credRecipientPrivateEC, credRecipientPublicEC;
+ private KeyAgreementCredential credKeyAgreementOriginatorEC;
+
+ private CollectionCredentialResolver recipientLocalCredResolver;
+
+ private KeyAgreementKeyInfoGeneratorFactory keyInfoFactory;
+
+ private String expectedEncryptionAlgorithm = EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM;
+
+ @BeforeClass
+ public void beforeClass() throws Exception {
+ KeyPair kpRecipientEC = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_EC, new ECGenParameterSpec("secp256r1"), null);
+ credRecipientPrivateEC = CredentialSupport.getSimpleCredential(kpRecipientEC.getPublic(), kpRecipientEC.getPrivate());
+ credRecipientPublicEC = CredentialSupport.getSimpleCredential(kpRecipientEC.getPublic(), null);
+
+ recipientLocalCredResolver = new CollectionCredentialResolver();
+
+ List<KeyInfoProvider> providers = new ArrayList<>();
+ providers.add( new RSAKeyValueProvider() );
+ providers.add( new DSAKeyValueProvider() );
+ providers.add( new DEREncodedKeyValueProvider());
+ providers.add( new InlineX509DataProvider() );
+ providers.add( new AgreementMethodKeyInfoProvider() );
+ resolver = new LocalKeyInfoCredentialResolver(providers, recipientLocalCredResolver);
+
+ keyInfoFactory = new KeyAgreementKeyInfoGeneratorFactory();
+ }
+
+ @BeforeMethod
+ public void beforeMethod() throws Exception {
+ recipientLocalCredResolver.getCollection().clear();
+ recipientLocalCredResolver.getCollection().add(credRecipientPrivateEC);
+
+ ConcatKDF kdf = new ConcatKDF();
+ kdf.setDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA512);
+ kdf.setAlgorithmID("AA");
+ kdf.setPartyUInfo("BB");
+ kdf.setPartyVInfo("CC");
+ kdf.setSuppPubInfo("DD");
+ kdf.setSuppPrivInfo("EE");
+ kdf.initialize();
+
+ KeyAgreementParameters params = new KeyAgreementParameters();
+ params.add(kdf);
+
+ ECDHKeyAgreementProcessor processor = new ECDHKeyAgreementProcessor();
+
+ credKeyAgreementOriginatorEC = processor.execute(credRecipientPublicEC, expectedEncryptionAlgorithm, params);
+ }
+
+ @Test
+ public void ECDHWithConcatKDF_Success() throws Exception {
+ KeyInfo keyInfo = prepareAndValidateKeyInfo(credKeyAgreementOriginatorEC);
+
+ Iterable<Credential> creds = resolver.resolve(new CriteriaSet(new KeyInfoCriterion(keyInfo)));
+
+ Assert.assertNotNull(creds);
+ Assert.assertEquals(Iterables.size(creds), 1);
+
+ Credential cred = creds.iterator().next();
+
+ Assert.assertTrue(KeyAgreementCredential.class.isInstance(cred));
+
+
+ KeyAgreementCredential keyAgreementCred = KeyAgreementCredential.class.cast(cred);
+ Assert.assertEquals(keyAgreementCred.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+ validateDerivedKey(keyAgreementCred, expectedEncryptionAlgorithm);
+
+ // Originator credential
+ Assert.assertNotNull(keyAgreementCred.getOriginatorCredential());
+ Assert.assertNotNull(keyAgreementCred.getOriginatorCredential().getPublicKey());
+ Assert.assertNull(keyAgreementCred.getOriginatorCredential().getPrivateKey());
+ Assert.assertEquals(keyAgreementCred.getOriginatorCredential().getPublicKey(), credKeyAgreementOriginatorEC.getOriginatorCredential().getPublicKey());
+
+ // Recipient credential
+ Assert.assertNotNull(keyAgreementCred.getRecipientCredential());
+ Assert.assertNotNull(keyAgreementCred.getRecipientCredential().getPublicKey());
+ Assert.assertNotNull(keyAgreementCred.getRecipientCredential().getPrivateKey());
+ Assert.assertEquals(keyAgreementCred.getRecipientCredential().getPublicKey(), credRecipientPrivateEC.getPublicKey());
+ Assert.assertEquals(keyAgreementCred.getRecipientCredential().getPrivateKey(), credRecipientPrivateEC.getPrivateKey());
+
+ // Parameters
+ Assert.assertTrue(keyAgreementCred.getParameters().contains(ConcatKDF.class));
+ ConcatKDF kdf = keyAgreementCred.getParameters().get(ConcatKDF.class);
+ Assert.assertEquals(kdf.getDigestMethod(), SignatureConstants.ALGO_ID_DIGEST_SHA512);
+ Assert.assertEquals(kdf.getAlgorithmID(), "AA");
+ Assert.assertEquals(kdf.getPartyUInfo(), "BB");
+ Assert.assertEquals(kdf.getPartyVInfo(), "CC");
+ Assert.assertEquals(kdf.getSuppPubInfo(), "DD");
+ Assert.assertEquals(kdf.getSuppPrivInfo(), "EE");
+
+ Assert.assertTrue(keyAgreementCred.getCredentialContextSet().contains(KeyInfoCredentialContext.class));
+ Assert.assertSame(keyAgreementCred.getCredentialContextSet().get(KeyInfoCredentialContext.class).getKeyInfo(), keyInfo);
+ }
+
+ @Test
+ public void agreementAlgorithmNotRegistered() throws Exception {
+ KeyInfo keyInfo = prepareAndValidateKeyInfo(credKeyAgreementOriginatorEC);
+
+ keyInfo.getAgreementMethods().get(0).setAlgorithm("INVALID");
+
+ Iterable<Credential> creds = resolver.resolve(new CriteriaSet(new KeyInfoCriterion(keyInfo)));
+
+ Assert.assertNotNull(creds);
+ Assert.assertEquals(Iterables.size(creds), 0);
+ }
+
+ @Test
+ public void agreementMethodNotGranndchildOfEncryptedType() throws Exception {
+ KeyInfo keyInfo = prepareAndValidateKeyInfo(credKeyAgreementOriginatorEC);
+
+ keyInfo.setParent(null);
+
+ Iterable<Credential> creds = resolver.resolve(new CriteriaSet(new KeyInfoCriterion(keyInfo)));
+
+ Assert.assertNotNull(creds);
+ Assert.assertEquals(Iterables.size(creds), 0);
+ }
+
+ @Test(expectedExceptions = ResolverException.class)
+ public void missingEncryptionAlgorithm() throws Exception {
+ KeyInfo keyInfo = prepareAndValidateKeyInfo(credKeyAgreementOriginatorEC);
+
+ EncryptedType.class.cast(keyInfo.getParent()).getEncryptionMethod().setAlgorithm(null);
+
+ resolver.resolve(new CriteriaSet(new KeyInfoCriterion(keyInfo)));
+ }
+
+ @Test(expectedExceptions = ResolverException.class)
+ public void unknownEncryptionAlgorithm() throws Exception {
+ KeyInfo keyInfo = prepareAndValidateKeyInfo(credKeyAgreementOriginatorEC);
+
+ EncryptedType.class.cast(keyInfo.getParent()).getEncryptionMethod().setAlgorithm("INVALID");
+
+ resolver.resolve(new CriteriaSet(new KeyInfoCriterion(keyInfo)));
+ }
+
+ @Test(expectedExceptions = ResolverException.class)
+ public void missingOriginatorKeyInfo() throws Exception {
+ KeyInfo keyInfo = prepareAndValidateKeyInfo(credKeyAgreementOriginatorEC);
+
+ keyInfo.getAgreementMethods().get(0).setOriginatorKeyInfo(null);
+
+ resolver.resolve(new CriteriaSet(new KeyInfoCriterion(keyInfo)));
+ }
+
+ @Test(expectedExceptions = ResolverException.class)
+ public void originatorCredResolutionFailedMissingKeyInfoData() throws Exception {
+ KeyInfo keyInfo = prepareAndValidateKeyInfo(credKeyAgreementOriginatorEC);
+
+ keyInfo.getAgreementMethods().get(0).getOriginatorKeyInfo().getDEREncodedKeyValues().clear();
+ keyInfo.getAgreementMethods().get(0).getOriginatorKeyInfo().getKeyValues().clear();
+
+ resolver.resolve(new CriteriaSet(new KeyInfoCriterion(keyInfo)));
+ }
+
+ @Test(expectedExceptions = ResolverException.class)
+ public void missingRecipientKeyInfo() throws Exception {
+ KeyInfo keyInfo = prepareAndValidateKeyInfo(credKeyAgreementOriginatorEC);
+
+ keyInfo.getAgreementMethods().get(0).setRecipientKeyInfo(null);
+
+ resolver.resolve(new CriteriaSet(new KeyInfoCriterion(keyInfo)));
+ }
+
+ @Test(expectedExceptions = ResolverException.class)
+ public void recipientCredResolutionFailedMissingKeyInfoData() throws Exception {
+ KeyInfo keyInfo = prepareAndValidateKeyInfo(credKeyAgreementOriginatorEC);
+
+ keyInfo.getAgreementMethods().get(0).getRecipientKeyInfo().getDEREncodedKeyValues().clear();
+ keyInfo.getAgreementMethods().get(0).getRecipientKeyInfo().getKeyValues().clear();
+
+ resolver.resolve(new CriteriaSet(new KeyInfoCriterion(keyInfo)));
+ }
+
+
+ @Test(expectedExceptions = ResolverException.class)
+ public void recipientCredResolutionFailedAtCredentialResolver() throws Exception {
+ KeyInfo keyInfo = prepareAndValidateKeyInfo(credKeyAgreementOriginatorEC);
+
+ recipientLocalCredResolver.getCollection().clear();
+
+ resolver.resolve(new CriteriaSet(new KeyInfoCriterion(keyInfo)));
+ }
+
+ @Test(expectedExceptions = ResolverException.class)
+ public void recipientCredMissingPrivateKey() throws Exception {
+ KeyInfo keyInfo = prepareAndValidateKeyInfo(credKeyAgreementOriginatorEC);
+
+ recipientLocalCredResolver.getCollection().clear();
+ recipientLocalCredResolver.getCollection().add(credRecipientPublicEC);
+
+ resolver.resolve(new CriteriaSet(new KeyInfoCriterion(keyInfo)));
+ }
+
+
+
+ //
+ // Helpers
+ //
+
+ private KeyInfo prepareAndValidateKeyInfo(KeyAgreementCredential cred) throws SecurityException {
+ KeyInfo keyInfo = keyInfoFactory.newInstance().generate(credKeyAgreementOriginatorEC);
+ Assert.assertNotNull(keyInfo);
+ Assert.assertEquals(keyInfo.getOrderedChildren().size(), 1);
+ Assert.assertEquals(keyInfo.getAgreementMethods().size(), 1);
+ makeEncryptionMethodChild(keyInfo, expectedEncryptionAlgorithm, null, EncryptedData.DEFAULT_ELEMENT_NAME);
+ return keyInfo;
+ }
+
+ private EncryptedType makeEncryptionMethodChild(KeyInfo keyinfo, String algorithm, Integer keySize, QName elementType) {
+ EncryptedType encryptedType = buildXMLObject(elementType);
+ encryptedType.setKeyInfo(keyinfo);
+
+ EncryptionMethod encryptionMethod = buildXMLObject(EncryptionMethod.DEFAULT_ELEMENT_NAME);
+ encryptionMethod.setAlgorithm(algorithm);
+ if (keySize != null) {
+ KeySize keySizeElement = buildXMLObject(KeySize.DEFAULT_ELEMENT_NAME);
+ keySizeElement.setValue(keySize);
+ encryptionMethod.setKeySize(keySizeElement);
+ }
+ encryptedType.setEncryptionMethod(encryptionMethod);;
+ return encryptedType;
+ }
+
+ private void validateDerivedKey(Credential credential, String algorithmURI) {
+ Assert.assertNotNull(credential);
+ Assert.assertNotNull(credential.getSecretKey());
+ Assert.assertEquals(credential.getSecretKey().getAlgorithm(), AlgorithmSupport.getKeyAlgorithm(algorithmURI));
+ Assert.assertEquals(KeySupport.getKeyLength(credential.getSecretKey()), AlgorithmSupport.getKeyLength(algorithmURI));
+ }
+
+}
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/BasicKeyInfoGeneratorTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/BasicKeyInfoGeneratorTest.java
index 68ec2d7c9..90039883c 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/BasicKeyInfoGeneratorTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/BasicKeyInfoGeneratorTest.java
@@ -19,6 +19,7 @@ package org.opensaml.xmlsec.keyinfo.impl;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
import org.testng.Assert;
import java.security.KeyException;
import java.security.PublicKey;
@@ -28,6 +29,8 @@ import org.opensaml.core.testing.XMLObjectBaseTestCase;
import org.opensaml.security.SecurityException;
import org.opensaml.security.credential.BasicCredential;
import org.opensaml.security.crypto.KeySupport;
+import org.opensaml.xmlsec.encryption.OriginatorKeyInfo;
+import org.opensaml.xmlsec.encryption.RecipientKeyInfo;
import org.opensaml.xmlsec.keyinfo.KeyInfoGenerator;
import org.opensaml.xmlsec.keyinfo.KeyInfoSupport;
import org.opensaml.xmlsec.signature.KeyInfo;
@@ -107,7 +110,7 @@ public class BasicKeyInfoGeneratorTest extends XMLObjectBaseTestCase {
PublicKey generatedKey = KeyInfoSupport.getKey(keyInfo.getKeyValues().get(0));
Assert.assertEquals(generatedKey, pubKey, "Unexpected key value");
PublicKey generatedKey2 = KeyInfoSupport.getKey(keyInfo.getDEREncodedKeyValues().get(0));
- Assert.assertEquals(pubKey, generatedKey2, "Unexpected key value");
+ Assert.assertEquals(generatedKey2, pubKey, "Unexpected key value");
}
/**
@@ -181,5 +184,43 @@ public class BasicKeyInfoGeneratorTest extends XMLObjectBaseTestCase {
Assert.assertNotNull(keyInfo.getOrderedChildren(), "Generated KeyInfo children list was null");
Assert.assertEquals(keyInfo.getOrderedChildren().size(), 5, "Unexpected # of KeyInfo children found");
}
+
+ @DataProvider
+ public Object[][] keyInfoTypes() {
+ return new Object[][] {
+ new Object[] { KeyInfo.class },
+ new Object[] { OriginatorKeyInfo.class },
+ new Object[] { RecipientKeyInfo.class },
+ };
+ }
+
+ /**
+ * Test emit of sub-type of KeyInfo
+ *
+ * @throws SecurityException ...
+ * @throws KeyException ...
+ */
+ @Test(dataProvider = "keyInfoTypes")
+ public void testKeyInfoElmementType(Class<? extends KeyInfo> type) throws SecurityException, KeyException {
+ factory.setEmitPublicKeyValue(true);
+ factory.setEmitPublicDEREncodedKeyValue(true);
+
+ generator = factory.newInstance(type);
+ KeyInfo keyInfo = generator.generate(credential);
+
+ Assert.assertTrue(type.isInstance(keyInfo));
+
+ Assert.assertNotNull(keyInfo, "Generated KeyInfo was null");
+ Assert.assertNotNull(keyInfo.getOrderedChildren(), "Generated KeyInfo children list was null");
+
+ Assert.assertEquals(keyInfo.getOrderedChildren().size(), 2, "Unexpected number of KeyInfo children");
+ Assert.assertEquals(keyInfo.getKeyValues().size(), 1, "Unexpected number of KeyValue elements");
+ Assert.assertEquals(keyInfo.getDEREncodedKeyValues().size(), 1,
+ "Unexpected number of DEREncodedKeyValue elements");
+ PublicKey generatedKey = KeyInfoSupport.getKey(keyInfo.getKeyValues().get(0));
+ Assert.assertEquals(generatedKey, pubKey, "Unexpected key value");
+ PublicKey generatedKey2 = KeyInfoSupport.getKey(keyInfo.getDEREncodedKeyValues().get(0));
+ Assert.assertEquals(generatedKey2, pubKey, "Unexpected key value");
+ }
}
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/KeyAgreementKeyInfoGeneratorTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/KeyAgreementKeyInfoGeneratorTest.java
new file mode 100644
index 000000000..fa93ed144
--- /dev/null
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/KeyAgreementKeyInfoGeneratorTest.java
@@ -0,0 +1,272 @@
+/*
+ * 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.keyinfo.impl;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.spec.ECGenParameterSpec;
+
+import javax.crypto.SecretKey;
+
+import org.opensaml.core.testing.XMLObjectBaseTestCase;
+import org.opensaml.security.credential.BasicCredential;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.crypto.JCAConstants;
+import org.opensaml.security.crypto.KeySupport;
+import org.opensaml.xmlsec.agreement.KeyAgreementCredential;
+import org.opensaml.xmlsec.agreement.impl.BasicKeyAgreementCredential;
+import org.opensaml.xmlsec.derivation.impl.ConcatKDF;
+import org.opensaml.xmlsec.derivation.impl.PBKDF2;
+import org.opensaml.xmlsec.encryption.AgreementMethod;
+import org.opensaml.xmlsec.encryption.ConcatKDFParams;
+import org.opensaml.xmlsec.encryption.KeyDerivationMethod;
+import org.opensaml.xmlsec.encryption.OriginatorKeyInfo;
+import org.opensaml.xmlsec.encryption.PBKDF2Params;
+import org.opensaml.xmlsec.encryption.RecipientKeyInfo;
+import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
+import org.opensaml.xmlsec.keyinfo.KeyInfoGenerator;
+import org.opensaml.xmlsec.keyinfo.KeyInfoSupport;
+import org.opensaml.xmlsec.signature.KeyInfo;
+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;
+
+/**
+ *
+ */
+public class KeyAgreementKeyInfoGeneratorTest extends XMLObjectBaseTestCase {
+
+ private KeyPair keyPairOriginatorECDH, keyPairRecipientECDH;
+
+ private Credential credOriginatorECDH, credRecipientECDH;
+
+ private SecretKey derivedKey;
+
+ private KeyAgreementCredential credECDH;
+
+ private KeyAgreementKeyInfoGeneratorFactory factory;
+
+ @BeforeClass
+ public void beforeClass() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
+ keyPairOriginatorECDH = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_EC, new ECGenParameterSpec("secp256r1"), null);
+ credOriginatorECDH = new BasicCredential(keyPairOriginatorECDH.getPublic(), keyPairOriginatorECDH.getPrivate());
+
+ keyPairRecipientECDH = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_EC, new ECGenParameterSpec("secp256r1"), null);
+ credRecipientECDH = new BasicCredential(keyPairRecipientECDH.getPublic());
+
+ derivedKey = KeySupport.generateKey(JCAConstants.KEY_ALGO_AES, 256, null);
+ }
+
+ @BeforeMethod
+ public void beforeMethod() {
+ factory = new KeyAgreementKeyInfoGeneratorFactory();
+
+ credECDH = new BasicKeyAgreementCredential(derivedKey, EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES, credOriginatorECDH, credRecipientECDH);
+ }
+
+
+ @Test
+ void ECDHWithConcatKDFWithDefaults() throws Exception {
+ ConcatKDF kdf = new ConcatKDF();
+ kdf.setDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA512);
+ kdf.setAlgorithmID("AA");
+ kdf.setPartyUInfo("BB");
+ kdf.setPartyVInfo("CC");
+ kdf.setSuppPubInfo("DD");
+ kdf.setSuppPrivInfo("EE");
+ kdf.initialize();
+
+ credECDH.getParameters().add(kdf);
+
+ KeyInfoGenerator generator = factory.newInstance();
+ KeyInfo keyInfo = generator.generate(credECDH);
+
+ Assert.assertNotNull(keyInfo);
+ Assert.assertNotNull(keyInfo.getOrderedChildren());
+ Assert.assertEquals(keyInfo.getOrderedChildren().size(), 1);
+ Assert.assertEquals(keyInfo.getAgreementMethods().size(), 1);
+
+ AgreementMethod agreementMethod = keyInfo.getAgreementMethods().get(0);
+ Assert.assertEquals(agreementMethod.getOrderedChildren().size(), 3);
+
+ //Originator
+ Assert.assertNotNull(agreementMethod.getOriginatorKeyInfo());
+ OriginatorKeyInfo originatorKeyInfo = agreementMethod.getOriginatorKeyInfo();
+ Assert.assertEquals(originatorKeyInfo.getOrderedChildren().size(), 1);
+ Assert.assertEquals(originatorKeyInfo.getDEREncodedKeyValues().size(), 1);
+ Assert.assertEquals(KeyInfoSupport.getKey(originatorKeyInfo.getDEREncodedKeyValues().get(0)), keyPairOriginatorECDH.getPublic());
+ //TODO Can't do these until have support for ECKeyValue <-> PublicKey. Change KeyInfo children size above also 1 -> 2.
+ //Assert.assertEquals(originatorKeyInfo.getKeyValues().size(), 1);
+ //Assert.assertEquals(KeyInfoSupport.getKey(originatorKeyInfo.getKeyValues().get(0)), keyPairOriginatorECDH.getPublic());
+
+ //Recipient
+ Assert.assertNotNull(agreementMethod.getRecipientKeyInfo());
+ RecipientKeyInfo recipientKeyInfo = agreementMethod.getRecipientKeyInfo();
+ Assert.assertEquals(recipientKeyInfo.getOrderedChildren().size(), 1);
+ Assert.assertEquals(recipientKeyInfo.getDEREncodedKeyValues().size(), 1);
+ Assert.assertEquals(KeyInfoSupport.getKey(recipientKeyInfo.getDEREncodedKeyValues().get(0)), keyPairRecipientECDH.getPublic());
+ //TODO Can't do these until have support for ECKeyValue <-> PublicKey. Change KeyInfo children size above also 1 -> 2.
+ //Assert.assertEquals(recipientKeyInfo.getKeyValues().size(), 1);
+ //Assert.assertEquals(KeyInfoSupport.getKey(recipientKeyInfo.getKeyValues().get(0)), keyPairRecipientECDH.getPublic());
+
+ //Params
+ Assert.assertEquals(agreementMethod.getUnknownXMLObjects(KeyDerivationMethod.DEFAULT_ELEMENT_NAME).size(), 1);
+ KeyDerivationMethod kdm = (KeyDerivationMethod) agreementMethod.getUnknownXMLObjects(KeyDerivationMethod.DEFAULT_ELEMENT_NAME).get(0);
+ Assert.assertEquals(kdm.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF);
+ Assert.assertEquals(kdm.getUnknownXMLObjects().size(), 1);
+ Assert.assertEquals(kdm.getUnknownXMLObjects(ConcatKDFParams.DEFAULT_ELEMENT_NAME).size(), 1);
+ ConcatKDFParams kdfParams = (ConcatKDFParams) kdm.getUnknownXMLObjects(ConcatKDFParams.DEFAULT_ELEMENT_NAME).get(0);
+ Assert.assertNotNull(kdfParams.getDigestMethod());
+ Assert.assertEquals(kdfParams.getDigestMethod().getAlgorithm(), SignatureConstants.ALGO_ID_DIGEST_SHA512);
+ Assert.assertEquals(kdfParams.getAlgorithmID(), "00AA");
+ Assert.assertEquals(kdfParams.getPartyUInfo(), "00BB");
+ Assert.assertEquals(kdfParams.getPartyVInfo(), "00CC");
+ Assert.assertEquals(kdfParams.getSuppPubInfo(), "00DD");
+ Assert.assertEquals(kdfParams.getSuppPrivInfo(), "00EE");
+ }
+
+ @Test
+ void ECDHWithPBKDF2WithDefaults() throws Exception {
+ PBKDF2 kdf = new PBKDF2();
+ kdf.setIterationCount(1500);
+ kdf.setKeyLength(256);
+ kdf.setPRF(SignatureConstants.ALGO_ID_MAC_HMAC_SHA512);
+ kdf.setSalt("ABCD");
+ kdf.initialize();
+
+ credECDH.getParameters().add(kdf);
+
+ KeyInfoGenerator generator = factory.newInstance();
+ KeyInfo keyInfo = generator.generate(credECDH);
+
+ Assert.assertNotNull(keyInfo);
+ Assert.assertNotNull(keyInfo.getOrderedChildren());
+ Assert.assertEquals(keyInfo.getOrderedChildren().size(), 1);
+ Assert.assertEquals(keyInfo.getAgreementMethods().size(), 1);
+
+ AgreementMethod agreementMethod = keyInfo.getAgreementMethods().get(0);
+ Assert.assertEquals(agreementMethod.getOrderedChildren().size(), 3);
+
+ //Originator
+ Assert.assertNotNull(agreementMethod.getOriginatorKeyInfo());
+ OriginatorKeyInfo originatorKeyInfo = agreementMethod.getOriginatorKeyInfo();
+ Assert.assertEquals(originatorKeyInfo.getOrderedChildren().size(), 1);
+ Assert.assertEquals(originatorKeyInfo.getDEREncodedKeyValues().size(), 1);
+ Assert.assertEquals(KeyInfoSupport.getKey(originatorKeyInfo.getDEREncodedKeyValues().get(0)), keyPairOriginatorECDH.getPublic());
+ //TODO Can't do these until have support for ECKeyValue <-> PublicKey. Change KeyInfo children size above also 1 -> 2.
+ //Assert.assertEquals(originatorKeyInfo.getKeyValues().size(), 1);
+ //Assert.assertEquals(KeyInfoSupport.getKey(originatorKeyInfo.getKeyValues().get(0)), keyPairOriginatorECDH.getPublic());
+
+ //Recipient
+ Assert.assertNotNull(agreementMethod.getRecipientKeyInfo());
+ RecipientKeyInfo recipientKeyInfo = agreementMethod.getRecipientKeyInfo();
+ Assert.assertEquals(recipientKeyInfo.getOrderedChildren().size(), 1);
+ Assert.assertEquals(recipientKeyInfo.getDEREncodedKeyValues().size(), 1);
+ Assert.assertEquals(KeyInfoSupport.getKey(recipientKeyInfo.getDEREncodedKeyValues().get(0)), keyPairRecipientECDH.getPublic());
+ //TODO Can't do these until have support for ECKeyValue <-> PublicKey. Change KeyInfo children size above also 1 -> 2.
+ //Assert.assertEquals(recipientKeyInfo.getKeyValues().size(), 1);
+ //Assert.assertEquals(KeyInfoSupport.getKey(recipientKeyInfo.getKeyValues().get(0)), keyPairRecipientECDH.getPublic());
+
+ //Params
+ Assert.assertEquals(agreementMethod.getUnknownXMLObjects(KeyDerivationMethod.DEFAULT_ELEMENT_NAME).size(), 1);
+ KeyDerivationMethod kdm = (KeyDerivationMethod) agreementMethod.getUnknownXMLObjects(KeyDerivationMethod.DEFAULT_ELEMENT_NAME).get(0);
+ Assert.assertEquals(kdm.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYDERIVATION_PBKDF2);
+ Assert.assertEquals(kdm.getUnknownXMLObjects().size(), 1);
+ Assert.assertEquals(kdm.getUnknownXMLObjects(PBKDF2Params.DEFAULT_ELEMENT_NAME).size(), 1);
+ PBKDF2Params kdfParams = (PBKDF2Params) kdm.getUnknownXMLObjects(PBKDF2Params.DEFAULT_ELEMENT_NAME).get(0);
+ Assert.assertNotNull(kdfParams.getIterationCount());
+ Assert.assertEquals(kdfParams.getIterationCount().getValue().intValue(), 1500);
+ Assert.assertNotNull(kdfParams.getKeyLength());
+ Assert.assertEquals(kdfParams.getKeyLength().getValue().intValue(), 256/8); // bytes
+ Assert.assertNotNull(kdfParams.getPRF());
+ Assert.assertEquals(kdfParams.getPRF().getAlgorithm(), SignatureConstants.ALGO_ID_MAC_HMAC_SHA512);
+ Assert.assertNotNull(kdfParams.getSalt());
+ Assert.assertNotNull(kdfParams.getSalt().getSpecified());
+ Assert.assertEquals(kdfParams.getSalt().getSpecified().getValue(), "ABCD");
+ }
+
+ @Test
+ public void noEmitKeyinfos() throws Exception {
+ factory.setEmitOriginatorKeyInfo(false);
+ factory.setEmitRecipientKeyInfo(false);
+
+ ConcatKDF kdf = new ConcatKDF();
+ kdf.setDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA512);
+ kdf.setAlgorithmID("AA");
+ kdf.setPartyUInfo("BB");
+ kdf.setPartyVInfo("CC");
+ kdf.setSuppPubInfo("DD");
+ kdf.setSuppPrivInfo("EE");
+ kdf.initialize();
+
+ credECDH.getParameters().add(kdf);
+
+ KeyInfoGenerator generator = factory.newInstance();
+ KeyInfo keyInfo = generator.generate(credECDH);
+
+ Assert.assertNotNull(keyInfo);
+ Assert.assertNotNull(keyInfo.getOrderedChildren());
+ Assert.assertEquals(keyInfo.getOrderedChildren().size(), 1);
+ Assert.assertEquals(keyInfo.getAgreementMethods().size(), 1);
+
+ AgreementMethod agreementMethod = keyInfo.getAgreementMethods().get(0);
+ Assert.assertEquals(agreementMethod.getOrderedChildren().size(), 1);
+
+ Assert.assertEquals(agreementMethod.getUnknownXMLObjects(KeyDerivationMethod.DEFAULT_ELEMENT_NAME).size(), 1);
+ Assert.assertNull(agreementMethod.getOriginatorKeyInfo());
+ Assert.assertNull(agreementMethod.getRecipientKeyInfo());
+ }
+
+ @Test
+ public void noKeyInfoManagers() throws Exception {
+ factory.setOriginatorKeyInfoGeneratorManager(null);
+ factory.setRecipientKeyInfoGeneratorManager(null);
+
+ ConcatKDF kdf = new ConcatKDF();
+ kdf.setDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA512);
+ kdf.setAlgorithmID("AA");
+ kdf.setPartyUInfo("BB");
+ kdf.setPartyVInfo("CC");
+ kdf.setSuppPubInfo("DD");
+ kdf.setSuppPrivInfo("EE");
+ kdf.initialize();
+
+ credECDH.getParameters().add(kdf);
+
+ KeyInfoGenerator generator = factory.newInstance();
+ KeyInfo keyInfo = generator.generate(credECDH);
+
+ Assert.assertNotNull(keyInfo);
+ Assert.assertNotNull(keyInfo.getOrderedChildren());
+ Assert.assertEquals(keyInfo.getOrderedChildren().size(), 1);
+ Assert.assertEquals(keyInfo.getAgreementMethods().size(), 1);
+
+ AgreementMethod agreementMethod = keyInfo.getAgreementMethods().get(0);
+ Assert.assertEquals(agreementMethod.getOrderedChildren().size(), 1);
+
+ Assert.assertEquals(agreementMethod.getUnknownXMLObjects(KeyDerivationMethod.DEFAULT_ELEMENT_NAME).size(), 1);
+ Assert.assertNull(agreementMethod.getOriginatorKeyInfo());
+ Assert.assertNull(agreementMethod.getRecipientKeyInfo());
+ }
+
+
+}
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/LocalKeyInfoCredentialResolverTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/LocalKeyInfoCredentialResolverTest.java
index b8ec78aae..276679117 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/LocalKeyInfoCredentialResolverTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/LocalKeyInfoCredentialResolverTest.java
@@ -34,6 +34,8 @@ import org.opensaml.security.credential.BasicCredential;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.credential.impl.CollectionCredentialResolver;
import org.opensaml.security.crypto.KeySupport;
+import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolutionMode;
+import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolutionMode.Mode;
import org.opensaml.xmlsec.keyinfo.KeyInfoCriterion;
import org.opensaml.xmlsec.keyinfo.KeyInfoSupport;
import org.opensaml.xmlsec.keyinfo.impl.provider.RSAKeyValueProvider;
@@ -104,4 +106,57 @@ public class LocalKeyInfoCredentialResolverTest extends XMLObjectBaseTestCase {
Assert.assertNull(resolvedCred, "Expected no credential to be resolved");
}
+ @Test
+ public void testLocalMode() throws ResolverException, EncodingException {
+ // Nominally the same as "testKeyInfoWithKnownPublicKey", just testing an explicit mode which is the default
+ KeyInfoSupport.addPublicKey(keyInfo, keyPair.getPublic());
+
+ CriteriaSet criteriaSet = new CriteriaSet( new KeyInfoCriterion(keyInfo), new KeyInfoCredentialResolutionMode(Mode.LOCAL) );
+ Credential resolvedCred = keyInfoResolver.resolveSingle(criteriaSet);
+
+ Assert.assertEquals(resolvedCred, localCred, "Unexpected local credential resolved");
+ }
+
+ @Test
+ public void testPublicMode() throws ResolverException, EncodingException {
+ localCredResolver.getCollection().clear();
+
+ KeyInfoSupport.addPublicKey(keyInfo, keyPair.getPublic());
+
+ CriteriaSet criteriaSet = new CriteriaSet( new KeyInfoCriterion(keyInfo), new KeyInfoCredentialResolutionMode(Mode.PUBLIC) );
+ Credential resolvedCred = keyInfoResolver.resolveSingle(criteriaSet);
+
+ Assert.assertNotNull(resolvedCred);
+ Assert.assertNotSame(resolvedCred, localCred);
+ Assert.assertNull(resolvedCred.getPrivateKey());
+ Assert.assertNotNull(resolvedCred.getPublicKey());
+ Assert.assertEquals(resolvedCred.getPublicKey(), keyPair.getPublic());
+ }
+
+ @Test
+ public void testBothModeWithLocalNotPresent() throws ResolverException, EncodingException {
+ localCredResolver.getCollection().clear();
+
+ KeyInfoSupport.addPublicKey(keyInfo, keyPair.getPublic());
+
+ CriteriaSet criteriaSet = new CriteriaSet( new KeyInfoCriterion(keyInfo), new KeyInfoCredentialResolutionMode(Mode.BOTH) );
+ Credential resolvedCred = keyInfoResolver.resolveSingle(criteriaSet);
+
+ Assert.assertNotNull(resolvedCred);
+ Assert.assertNotSame(resolvedCred, localCred);
+ Assert.assertNull(resolvedCred.getPrivateKey());
+ Assert.assertNotNull(resolvedCred.getPublicKey());
+ Assert.assertEquals(resolvedCred.getPublicKey(), keyPair.getPublic());
+ }
+
+ @Test
+ public void testBothModeWithLocalPresent() throws ResolverException, EncodingException {
+ KeyInfoSupport.addPublicKey(keyInfo, keyPair.getPublic());
+
+ CriteriaSet criteriaSet = new CriteriaSet( new KeyInfoCriterion(keyInfo), new KeyInfoCredentialResolutionMode(Mode.BOTH) );
+ Credential resolvedCred = keyInfoResolver.resolveSingle(criteriaSet);
+
+ Assert.assertEquals(resolvedCred, localCred, "Unexpected local credential resolved");
+ }
+
}
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/X509KeyInfoGeneratorTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/X509KeyInfoGeneratorTest.java
index 602d68b51..b9bf74286 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/X509KeyInfoGeneratorTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/impl/X509KeyInfoGeneratorTest.java
@@ -19,6 +19,7 @@ package org.opensaml.xmlsec.keyinfo.impl;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
import org.testng.Assert;
import java.math.BigInteger;
import java.security.KeyException;
@@ -42,6 +43,8 @@ import org.opensaml.core.testing.XMLObjectBaseTestCase;
import org.opensaml.security.SecurityException;
import org.opensaml.security.x509.BasicX509Credential;
import org.opensaml.security.x509.X509Support;
+import org.opensaml.xmlsec.encryption.OriginatorKeyInfo;
+import org.opensaml.xmlsec.encryption.RecipientKeyInfo;
import org.opensaml.xmlsec.keyinfo.KeyInfoGenerator;
import org.opensaml.xmlsec.keyinfo.KeyInfoSupport;
import org.opensaml.xmlsec.signature.KeyInfo;
@@ -608,5 +611,40 @@ public class X509KeyInfoGeneratorTest extends XMLObjectBaseTestCase {
Assert.assertNotNull(keyInfo.getOrderedChildren(), "Generated KeyInfo children list was null");
Assert.assertEquals(keyInfo.getOrderedChildren().size(), 4, "Unexpected # of KeyInfo children found");
}
+
+ @DataProvider
+ public Object[][] keyInfoTypes() {
+ return new Object[][] {
+ new Object[] { KeyInfo.class },
+ new Object[] { OriginatorKeyInfo.class },
+ new Object[] { RecipientKeyInfo.class },
+ };
+ }
+
+ /**
+ * Test emit of sub-type of KeyInfo.
+ *
+ * @throws SecurityException ...
+ * @throws CertificateException ...
+ */
+ @Test(dataProvider = "keyInfoTypes")
+ public void testKeyInfoElementType(Class<? extends KeyInfo> type) throws SecurityException, CertificateException {
+ factory.setEmitEntityCertificate(true);
+
+ generator = factory.newInstance(type);
+ KeyInfo keyInfo = generator.generate(credential);
+
+ Assert.assertNotNull(keyInfo, "Generated KeyInfo was null");
+ Assert.assertNotNull(keyInfo.getOrderedChildren(), "Generated KeyInfo children list was null");
+
+ Assert.assertTrue(type.isInstance(keyInfo));
+
+ Assert.assertEquals(keyInfo.getX509Datas().size(), 1, "Unexpected number of X509Data elements");
+ X509Data x509Data = keyInfo.getX509Datas().get(0);
+ Assert.assertEquals(x509Data.getX509Certificates().size(), 1, "Unexpected number of X509Certificate elements");
+ List<X509Certificate> certs = KeyInfoSupport.getCertificates(x509Data);
+ Assert.assertEquals(certs.get(0), entityCert, "Unexpected certificate value found");
+ }
+
}
diff --git a/opensaml-xmlsec-impl/src/test/resources/logback-test.xml b/opensaml-xmlsec-impl/src/test/resources/logback-test.xml
index 3de039be6..b031592ce 100644
--- a/opensaml-xmlsec-impl/src/test/resources/logback-test.xml
+++ b/opensaml-xmlsec-impl/src/test/resources/logback-test.xml
@@ -23,6 +23,10 @@
<level value="TRACE"/>
</logger>
+ <logger name="org.opensaml.xmlsec.keyinfo">
+ <level value="WARN"/>
+ </logger>
+
<logger name="org.opensaml.xmlsec.impl">
<level value="TRACE"/>
</logger>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list