[java-opensaml] 12/15: KeyInfo support for KeyAgreementCredential.

Brent Putman putmanb at georgetown.edu
Thu Jan 21 21:33:32 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=302408c8b0222374f74263182545d1006b6804f6

commit 302408c8b0222374f74263182545d1006b6804f6
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    | 133 +++++++-
 .../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, 3012 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..2b9e5a293 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,106 @@ 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());
+        param.setKeyLength(xmlParams.getKeyLength().getValue());
+        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..aa13f143b
--- /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(256);
+        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..70bdf4f33 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(128);
+        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