[java-opensaml] branch main updated: OSJ-333: Support encryption via classic Diffie-Hellman key agreement
Brent Putman
putmanb at georgetown.edu
Wed Mar 10 01:37:53 UTC 2021
This is an automated email from the git hooks/post-receive script.
putmanb pushed a commit to branch main
in repository java-opensaml.
View the commit online:
http://git.shibboleth.net/view/?p=java-opensaml.git;a=commit;h=cd392dd71a292a156d739c78e5f27ecde3dc1127
The following commit(s) were added to refs/heads/main by this push:
new cd392dd71 OSJ-333: Support encryption via classic Diffie-Hellman key agreement
cd392dd71 is described below
commit cd392dd71a292a156d739c78e5f27ecde3dc1127
Author: Brent Putman <putmanb at georgetown.edu>
AuthorDate: Wed Mar 3 18:03:51 2021 -0500
OSJ-333: Support encryption via classic Diffie-Hellman key agreement
This includes key agreement, encryption, KeyInfo support and related
components.
---
.../saml/common/testing/SAMLTestSupport.java | 2 +
.../org/opensaml/security/crypto/JCAConstants.java | 8 +
.../org/opensaml/security/crypto/KeySupport.java | 31 ++++
.../org/opensaml/security/crypto/dh/DHSupport.java | 135 +++++++++++++++
.../opensaml/security/crypto/dh/package-info.java | 19 +++
.../opensaml/security/crypto/dh/BaseDHTest.java | 37 ++++
.../opensaml/security/crypto/dh/DHSupportTest.java | 108 ++++++++++++
.../xmlsec/agreement/KeyAgreementSupport.java | 2 +-
.../opensaml/xmlsec/keyinfo/KeyInfoSupport.java | 150 +++++++++++++++--
.../org/opensaml/xmlsec/signature/KeyValue.java | 21 +++
.../DHWithExplicitKDFKeyAgreementProcessor.java | 102 +++++++++++
.../impl/DHWithLegacyKDFKeyAgreementProcessor.java | 146 ++++++++++++++++
.../opensaml/xmlsec/agreement/impl/KANonce.java | 111 +++++++++++-
.../DefaultSecurityConfigurationBootstrap.java | 14 ++
.../xmlsec/derivation/impl/DHLegacyKDF.java | 186 +++++++++++++++++++++
.../xmlsec/signature/impl/KeyValueImpl.java | 17 ++
.../signature/impl/KeyValueUnmarshaller.java | 3 +
...opensaml.xmlsec.agreement.KeyAgreementProcessor | 4 +-
...HWithExplicitKDFKeyAgreementProcessorTest.java} | 29 ++--
... DHWithLegacyKDFKeyAgreementProcessorTest.java} | 107 ++++++------
.../impl/ECDHKeyAgreementProcessorTest.java | 6 +-
.../xmlsec/agreement/impl/KANonceTest.java | 49 ++++--
.../GlobalKeyAgreementProcessorRegistryTest.java | 16 +-
.../xmlsec/derivation/impl/ConcatKDFTest.java | 2 -
.../xmlsec/derivation/impl/DHLegacyKDFTest.java | 157 +++++++++++++++++
.../{ECDHTest.java => DHWithExplicitKDFTest.java} | 46 +++--
.../{ECDHTest.java => DHWithLegacyKDFTest.java} | 86 ++++++----
.../xmlsec/encryption/support/tests/ECDHTest.java | 21 ++-
.../impl/KeyAgreementKeyInfoGeneratorTest.java | 138 ++++++++++++++-
.../xmlsec/keyinfo/tests/KeyInfoSupportTest.java | 114 ++++++++++++-
.../xmlsec/testing/XMLSecurityTestingSupport.java | 2 +
.../src/test/resources/logback-test.xml | 4 +
32 files changed, 1708 insertions(+), 165 deletions(-)
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/common/testing/SAMLTestSupport.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/common/testing/SAMLTestSupport.java
index 64fe0e684..7814c9202 100644
--- a/opensaml-saml-impl/src/test/java/org/opensaml/saml/common/testing/SAMLTestSupport.java
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/common/testing/SAMLTestSupport.java
@@ -23,6 +23,7 @@ import java.util.List;
import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
import org.opensaml.xmlsec.keyinfo.impl.BasicProviderKeyInfoCredentialResolver;
import org.opensaml.xmlsec.keyinfo.impl.KeyInfoProvider;
+import org.opensaml.xmlsec.keyinfo.impl.provider.DEREncodedKeyValueProvider;
import org.opensaml.xmlsec.keyinfo.impl.provider.DSAKeyValueProvider;
import org.opensaml.xmlsec.keyinfo.impl.provider.ECKeyValueProvider;
import org.opensaml.xmlsec.keyinfo.impl.provider.InlineX509DataProvider;
@@ -51,6 +52,7 @@ public final class SAMLTestSupport {
providers.add( new RSAKeyValueProvider() );
providers.add( new DSAKeyValueProvider() );
providers.add( new ECKeyValueProvider() );
+ providers.add( new DEREncodedKeyValueProvider() );
providers.add( new InlineX509DataProvider() );
return providers;
}
diff --git a/opensaml-security-api/src/main/java/org/opensaml/security/crypto/JCAConstants.java b/opensaml-security-api/src/main/java/org/opensaml/security/crypto/JCAConstants.java
index 329e220c7..13e14aae7 100644
--- a/opensaml-security-api/src/main/java/org/opensaml/security/crypto/JCAConstants.java
+++ b/opensaml-security-api/src/main/java/org/opensaml/security/crypto/JCAConstants.java
@@ -17,6 +17,8 @@
package org.opensaml.security.crypto;
+import java.security.Key;
+
/**
* Various useful constants defined in and/or used with the Java Cryptography Architecture (JCA) specification.
*/
@@ -42,6 +44,12 @@ public final class JCAConstants {
/** Key algorithm: "DESede". */
public static final String KEY_ALGO_DESEDE = "DESede";
+ /** Key algorithm: "DH" (returned by {@link Key#getAlgorithm()}). */
+ public static final String KEY_ALGO_DH = "DH";
+
+ /** Key algorithm: "DiffieHellman" (used with key and key pair factories, generators, etc). */
+ public static final String KEY_ALGO_DIFFIE_HELLMAN = "DiffieHellman";
+
// Key formats
diff --git a/opensaml-security-api/src/main/java/org/opensaml/security/crypto/KeySupport.java b/opensaml-security-api/src/main/java/org/opensaml/security/crypto/KeySupport.java
index 43044c97a..8b50f5b9a 100644
--- a/opensaml-security-api/src/main/java/org/opensaml/security/crypto/KeySupport.java
+++ b/opensaml-security-api/src/main/java/org/opensaml/security/crypto/KeySupport.java
@@ -57,6 +57,8 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.SecretKeySpec;
import net.shibboleth.utilities.java.support.codec.Base64Support;
@@ -322,6 +324,19 @@ public final class KeySupport {
final X509EncodedKeySpec keySpec = new X509EncodedKeySpec(base64DecodeOrThrow(base64EncodedKey));
return (DSAPublicKey) buildKey(keySpec, JCAConstants.KEY_ALGO_DSA);
}
+
+ /**
+ * Build Java DH public key from base64 encoding.
+ *
+ * @param base64EncodedKey base64-encoded DH public key
+ * @return a native Java DHPublicKey
+ * @throws KeyException thrown if there is an error constructing key
+ */
+ @Nonnull public static DHPublicKey buildJavaDHPublicKey(@Nonnull final String base64EncodedKey)
+ throws KeyException {
+ final X509EncodedKeySpec keySpec = new X509EncodedKeySpec(base64DecodeOrThrow(base64EncodedKey));
+ return (DHPublicKey) buildKey(keySpec, JCAConstants.KEY_ALGO_DIFFIE_HELLMAN);
+ }
/**
* Build Java RSA public key from base64 encoding.
@@ -397,6 +412,22 @@ public final class KeySupport {
return (DSAPrivateKey) key;
}
+ /**
+ * Build Java DH private key from base64 encoding.
+ *
+ * @param base64EncodedKey base64-encoded DH private key
+ * @return a native Java DHPrivateKey
+ * @throws KeyException thrown if there is an error constructing key
+ */
+ @Nonnull public static DHPrivateKey buildJavaDHPrivateKey(@Nonnull final String base64EncodedKey)
+ throws KeyException {
+ final PrivateKey key = buildJavaPrivateKey(base64EncodedKey);
+ if (!(key instanceof DHPrivateKey)) {
+ throw new KeyException("Generated key was not a DHPrivateKey instance");
+ }
+ return (DHPrivateKey) key;
+ }
+
/**
* Build Java EC private key from base64 encoding.
*
diff --git a/opensaml-security-api/src/main/java/org/opensaml/security/crypto/dh/DHSupport.java b/opensaml-security-api/src/main/java/org/opensaml/security/crypto/dh/DHSupport.java
new file mode 100644
index 000000000..52b9fd216
--- /dev/null
+++ b/opensaml-security-api/src/main/java/org/opensaml/security/crypto/dh/DHSupport.java
@@ -0,0 +1,135 @@
+/*
+ * 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.security.crypto.dh;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.crypto.KeyAgreement;
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.DomainParameters;
+import org.opensaml.security.crypto.JCAConstants;
+import org.opensaml.security.crypto.KeySupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Cryptography support related to Elliptic Curve.
+ */
+public final class DHSupport {
+
+ /** Logger. */
+ private static final Logger LOG = LoggerFactory.getLogger(DHSupport.class);
+
+ /** Constructor. */
+ private DHSupport() { }
+
+ /**
+ * Perform DH key agreement between the given public and private keys.
+ *
+ * @param publicKey the public key
+ * @param privateKey the private key
+ * @param provider the optional security provider to use
+ *
+ * @return the secret produced by key agreement
+ *
+ * @throws NoSuchAlgorithmException if algorithm is unknown
+ * @throws NoSuchProviderException if provider is unknown
+ * @throws InvalidKeyException if supplied key is invalid
+ */
+ public static byte[] performKeyAgreement(@Nonnull final DHPublicKey publicKey,
+ @Nonnull final DHPrivateKey privateKey, @Nullable final String provider)
+ throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException {
+ Constraint.isNotNull(publicKey, "DHPublicKey was null");
+ Constraint.isNotNull(privateKey, "DHPrivateKey was null");
+
+ KeyAgreement keyAgreement = null;
+ if (provider != null) {
+ keyAgreement = KeyAgreement.getInstance(JCAConstants.KEY_AGREEMENT_DH, provider);
+ } else {
+ keyAgreement = KeyAgreement.getInstance(JCAConstants.KEY_AGREEMENT_DH);
+ }
+
+ keyAgreement.init(privateKey);
+ keyAgreement.doPhase(publicKey, true);
+ return keyAgreement.generateSecret();
+ }
+
+ /**
+ * Generate a key pair whose parameters are compatible with those of the specified DH public key.
+ *
+ * @param publicKey the public key
+ * @param provider the optional security provider to use
+ *
+ * @return the generated key pair
+ *
+ * @throws NoSuchAlgorithmException if algorithm is unknown
+ * @throws NoSuchProviderException if provider is unknown
+ * @throws InvalidAlgorithmParameterException if the public key's {@link DHParameterSpec} is not supported
+ */
+ public static KeyPair generateCompatibleKeyPair(@Nonnull final DHPublicKey publicKey,
+ @Nullable final String provider)
+ throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
+ Constraint.isNotNull(publicKey, "DHPublicKey was null");
+
+ return KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN, publicKey.getParams(), provider);
+ }
+
+ /**
+ * Obtain the prime Q domain parameter from the specified DH public key.
+ *
+ * <p>
+ * Java's interface for DH domain parameters {@link DHParameterSpec} doesn't expose
+ * the prime Q parameter, but in some contexts it is required, e.g XML Encryption <code>DHKeyValue</code>
+ * element. The approach here is to parse the ASN.1 encoding of the key directly.
+ * </p>
+ *
+ * @param publicKey the public key
+ *
+ * @return the prime Q domain parameter, or null if could not be processed
+ */
+ public static BigInteger getPrimeQDomainParameter(@Nonnull final DHPublicKey publicKey) {
+ Constraint.isNotNull(publicKey, "DHPublicKey was null");
+ try (ASN1InputStream input = new ASN1InputStream(publicKey.getEncoded())) {
+ final SubjectPublicKeyInfo spki = SubjectPublicKeyInfo.getInstance(input.readObject());
+ if (spki.getAlgorithm().getParameters() != null) {
+ final DomainParameters dp = DomainParameters.getInstance(spki.getAlgorithm().getParameters());
+ return dp.getQ();
+ }
+ return null;
+ } catch (final Exception e) {
+ LOG.warn("Error processing DHPublicKey for prime Q parameter", e);
+ return null;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/opensaml-security-api/src/main/java/org/opensaml/security/crypto/dh/package-info.java b/opensaml-security-api/src/main/java/org/opensaml/security/crypto/dh/package-info.java
new file mode 100644
index 000000000..f6f3750a6
--- /dev/null
+++ b/opensaml-security-api/src/main/java/org/opensaml/security/crypto/dh/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+/** Support for Diffie-Hellman cryptography. */
+package org.opensaml.security.crypto.dh;
\ No newline at end of file
diff --git a/opensaml-security-api/src/test/java/org/opensaml/security/crypto/dh/BaseDHTest.java b/opensaml-security-api/src/test/java/org/opensaml/security/crypto/dh/BaseDHTest.java
new file mode 100644
index 000000000..d6bf6b44a
--- /dev/null
+++ b/opensaml-security-api/src/test/java/org/opensaml/security/crypto/dh/BaseDHTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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.security.crypto.dh;
+
+import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
+import org.testng.annotations.DataProvider;
+
+/**
+ * This mostly exists as a single place to define the set of DH key sizes we want to test in various tests.
+ */
+public class BaseDHTest extends OpenSAMLInitBaseTestCase {
+
+ @DataProvider
+ public Object[][] dhKeySizes() {
+ return new Object[][] {
+ new Object[] {1024},
+ new Object[] {2048},
+ new Object[] {4096},
+ };
+ }
+
+}
diff --git a/opensaml-security-api/src/test/java/org/opensaml/security/crypto/dh/DHSupportTest.java b/opensaml-security-api/src/test/java/org/opensaml/security/crypto/dh/DHSupportTest.java
new file mode 100644
index 000000000..6edc2dc0a
--- /dev/null
+++ b/opensaml-security-api/src/test/java/org/opensaml/security/crypto/dh/DHSupportTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.security.crypto.dh;
+
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHPublicKeySpec;
+
+import org.opensaml.security.crypto.JCAConstants;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ *
+ */
+public class DHSupportTest extends BaseDHTest {
+
+ @Test(dataProvider="dhKeySizes")
+ public void generateCompatibleKeyPair(int keySize) throws Exception {
+ final KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN);
+ kpGenerator.initialize(keySize);
+ final KeyPair origKeyPair = kpGenerator.generateKeyPair();
+ Assert.assertNotNull(origKeyPair);
+ Assert.assertTrue(DHPublicKey.class.isInstance(origKeyPair.getPublic()));
+ DHPublicKey origPublicKey = DHPublicKey.class.cast(origKeyPair.getPublic());
+
+ final KeyPair generatedKeyPair = DHSupport.generateCompatibleKeyPair(origPublicKey, null);
+
+ Assert.assertNotNull(generatedKeyPair);
+ Assert.assertTrue(DHPublicKey.class.isInstance(generatedKeyPair.getPublic()));
+ Assert.assertTrue(DHPrivateKey.class.isInstance(generatedKeyPair.getPrivate()));
+ }
+
+ @Test(dataProvider="dhKeySizes")
+ public void performKeyAgreement(int keySize) throws Exception {
+ final KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN);
+ kpGenerator.initialize(keySize);
+ final KeyPair publicKeyPair = kpGenerator.generateKeyPair();
+ DHPublicKey publicKey = DHPublicKey.class.cast(publicKeyPair.getPublic());
+
+ final KeyPair privateKeyPair = DHSupport.generateCompatibleKeyPair(publicKey, null);
+
+ final DHPrivateKey privateKey = DHPrivateKey.class.cast(privateKeyPair.getPrivate());
+
+ byte[] secret = DHSupport.performKeyAgreement(publicKey, privateKey, null);
+ Assert.assertNotNull(secret);
+ }
+
+ @Test(dataProvider="dhKeySizes")
+ public void getPrimeQDomainParameter(int keySize) throws Exception {
+ final KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN);
+ kpGenerator.initialize(keySize);
+ final KeyPair publicKeyPair = kpGenerator.generateKeyPair();
+ DHPublicKey publicKey1 = DHPublicKey.class.cast(publicKeyPair.getPublic());
+
+ BigInteger q1 = DHSupport.getPrimeQDomainParameter(publicKey1);
+ Assert.assertNotNull(q1);
+
+ // Check that a DHPublicKey constructed via a KeyFactory from G and P alone still has Q component
+
+ final DHPublicKeySpec dhPubSpec = new DHPublicKeySpec(publicKey1.getY(), publicKey1.getParams().getP(), publicKey1.getParams().getG());
+ final KeyFactory keyFactory = KeyFactory.getInstance(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN);
+ final DHPublicKey publicKey2 = DHPublicKey.class.cast(keyFactory.generatePublic(dhPubSpec));
+ Assert.assertEquals(publicKey2, publicKey2);
+
+ /* Turns out this doesn't actually hold. BC throws IllegalArgumentException on new instance of DomainParameters
+ * b/c ASN1Sequence has only 2 elements (P and G), not 3 as it should.
+ * Seems like a bug in Java, b/c Q is required in the ASN.1 encoding.
+ * See: https://tools.ietf.org/html/rfc3279, section 2.3.3.
+ BigInteger q2 = DHSupport.getPrimeQDomainParameter(publicKey2);
+ Assert.assertNotNull(q2);
+ Assert.assertEquals(q2, q1);
+ */
+
+ // Check that a new DHPublicKey generated from the params spec of public key created with P and G alone,
+ // still has a Q component. This does work, despite the issue with the input key itself.
+ // More reason to think the above is a bug, e.g. in KeyFactory.
+
+ final KeyPairGenerator kpGenerator2 = KeyPairGenerator.getInstance(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN);
+ kpGenerator2.initialize(publicKey2.getParams());
+ final KeyPair compatKeyPair = kpGenerator.generateKeyPair();
+ final DHPublicKey publicKey3 = DHPublicKey.class.cast(compatKeyPair.getPublic());
+
+ BigInteger q3 = DHSupport.getPrimeQDomainParameter(publicKey3);
+ Assert.assertNotNull(q3);
+ Assert.assertEquals(q3, q1);
+ }
+
+}
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementSupport.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementSupport.java
index f42925508..588823570 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementSupport.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementSupport.java
@@ -37,7 +37,7 @@ import org.opensaml.xmlsec.encryption.KeySize;
public final class KeyAgreementSupport {
/** JCA key algorithms that support key agreement. */
- public static final Set<String> KEY_ALGORITHMS = Set.of(JCAConstants.KEY_ALGO_EC);
+ public static final Set<String> KEY_ALGORITHMS = Set.of(JCAConstants.KEY_ALGO_EC, JCAConstants.KEY_ALGO_DH);
/** Constructor. */
private KeyAgreementSupport() {}
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/keyinfo/KeyInfoSupport.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/keyinfo/KeyInfoSupport.java
index cd39cd5da..f7624d1a9 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/keyinfo/KeyInfoSupport.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/keyinfo/KeyInfoSupport.java
@@ -47,11 +47,8 @@ import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-
-import net.shibboleth.utilities.java.support.codec.Base64Support;
-import net.shibboleth.utilities.java.support.codec.DecodingException;
-import net.shibboleth.utilities.java.support.codec.EncodingException;
-import net.shibboleth.utilities.java.support.logic.Constraint;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHPublicKeySpec;
import org.apache.xml.security.utils.XMLUtils;
import org.opensaml.core.xml.XMLObjectBuilder;
@@ -60,9 +57,13 @@ import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
import org.opensaml.security.SecurityException;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.crypto.JCAConstants;
+import org.opensaml.security.crypto.dh.DHSupport;
import org.opensaml.security.crypto.ec.ECSupport;
import org.opensaml.security.x509.X509Support;
import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
+import org.opensaml.xmlsec.encryption.DHKeyValue;
+import org.opensaml.xmlsec.encryption.Generator;
+import org.opensaml.xmlsec.encryption.Public;
import org.opensaml.xmlsec.signature.DEREncodedKeyValue;
import org.opensaml.xmlsec.signature.DSAKeyValue;
import org.opensaml.xmlsec.signature.ECKeyValue;
@@ -89,6 +90,11 @@ import org.slf4j.LoggerFactory;
import com.google.common.base.Strings;
+import net.shibboleth.utilities.java.support.codec.Base64Support;
+import net.shibboleth.utilities.java.support.codec.DecodingException;
+import net.shibboleth.utilities.java.support.codec.EncodingException;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
/**
* Utility class for working with data inside a KeyInfo object.
*
@@ -515,11 +521,18 @@ public class KeyInfoSupport {
}
/**
- * Converts a Java DSA or RSA public key into the corresponding XMLObject and stores it in a {@link KeyInfo} in a
- * new {@link KeyValue} element.
+ * Converts a Java RSA, EC, DSA or DH public key into the corresponding XMLObject and stores it in a
+ * {@link KeyInfo} in a new {@link KeyValue} element.
*
- * As input, only supports {@link PublicKey}s which are instances of either
- * {@link java.security.interfaces.DSAPublicKey} or {@link java.security.interfaces.RSAPublicKey}
+ * <p>
+ * As input, only supports {@link PublicKey} instances which are:
+ * </p>
+ * <ul>
+ * <li>{@link java.security.interfaces.RSAPublicKey}</li>
+ * <li>{@link java.security.interfaces.ECPublicKey}</li>
+ * <li>{@link java.security.interfaces.DSAPublicKey}</li>
+ * <li>{@link javax.crypto.interfaces.DHPublicKey}</li>
+ * </ul>
*
* @param keyInfo the {@link KeyInfo} element to which to add the key
* @param pk the native Java {@link PublicKey} to add
@@ -539,12 +552,68 @@ public class KeyInfoSupport {
keyValue.setECKeyValue(buildECKeyValue((ECPublicKey) pk));
} else if (pk instanceof DSAPublicKey) {
keyValue.setDSAKeyValue(buildDSAKeyValue((DSAPublicKey) pk));
+ } else if (pk instanceof DHPublicKey) {
+ keyValue.setDHKeyValue(buildDHKeyValue((DHPublicKey) pk));
} else {
- throw new IllegalArgumentException("Only RSAPublicKey and DSAPublicKey are supported");
+ throw new IllegalArgumentException("Saw unsupported public key type: " + pk.getClass().getName());
}
keyInfo.getKeyValues().add(keyValue);
}
+
+ /**
+ * Builds a {@link DHKeyValue} XMLObject from the Java security DH public key type.
+ *
+ * @param dhPubKey a native Java {@link DHPublicKey}
+ * @return an {@link DHKeyValue} XMLObject
+ * @throws EncodingException if the DH public key parameters can not be base64 encoded
+ */
+ @Nonnull public static DHKeyValue buildDHKeyValue(@Nonnull final DHPublicKey dhPubKey)
+ throws EncodingException {
+ Constraint.isNotNull(dhPubKey, "DH public key cannot be null");
+
+ final XMLObjectBuilderFactory builderFactory = XMLObjectProviderRegistrySupport.getBuilderFactory();
+
+ final XMLObjectBuilder<DHKeyValue> dhKeyValueBuilder =
+ builderFactory.getBuilderOrThrow(DHKeyValue.DEFAULT_ELEMENT_NAME);
+ final DHKeyValue dhKeyValue = dhKeyValueBuilder.buildObject(DHKeyValue.DEFAULT_ELEMENT_NAME);
+
+ final XMLObjectBuilder<Generator> generatorBuilder =
+ builderFactory.getBuilderOrThrow(Generator.DEFAULT_ELEMENT_NAME);
+ final XMLObjectBuilder<Public> publicBuilder = builderFactory.getBuilderOrThrow(Public.DEFAULT_ELEMENT_NAME);
+ final XMLObjectBuilder<org.opensaml.xmlsec.encryption.P> pBuilder =
+ builderFactory.getBuilderOrThrow(org.opensaml.xmlsec.encryption.P.DEFAULT_ELEMENT_NAME);
+ final XMLObjectBuilder<org.opensaml.xmlsec.encryption.Q> qBuilder =
+ builderFactory.getBuilderOrThrow(org.opensaml.xmlsec.encryption.Q.DEFAULT_ELEMENT_NAME);
+
+ final Public pub = publicBuilder.buildObject(Public.DEFAULT_ELEMENT_NAME);
+ final Generator gen = generatorBuilder.buildObject(Generator.DEFAULT_ELEMENT_NAME);
+ final org.opensaml.xmlsec.encryption.P p =
+ pBuilder.buildObject(org.opensaml.xmlsec.encryption.P.DEFAULT_ELEMENT_NAME);
+
+ pub.setValueBigInt(dhPubKey.getY());
+ dhKeyValue.setPublic(pub);
+
+ gen.setValueBigInt(dhPubKey.getParams().getG());
+ dhKeyValue.setGenerator(gen);
+
+ p.setValueBigInt(dhPubKey.getParams().getP());
+ dhKeyValue.setP(p);
+
+ // DHParameterSpec doesn't expose the Q param. Also, it seems sometimes the ASN.1 encoded keys do not have
+ // a Q param, which is a violation of RFC 3279, section 2.3.3. So we have to deal with the null case.
+ // If it doesn't have Q, just emit without, even thought it violates the XML Encryption schema,
+ // since we don't actually need it to construct a DHPublicKey key anyway.
+ final BigInteger qValue = DHSupport.getPrimeQDomainParameter(dhPubKey);
+ if (qValue != null) {
+ final org.opensaml.xmlsec.encryption.Q q =
+ qBuilder.buildObject(org.opensaml.xmlsec.encryption.Q.DEFAULT_ELEMENT_NAME);
+ q.setValueBigInt(qValue);
+ dhKeyValue.setQ(q);
+ }
+
+ return dhKeyValue;
+ }
/**
* Builds an {@link ECKeyValue} XMLObject from the Java security EC public key type.
@@ -751,6 +820,8 @@ public class KeyInfoSupport {
return getRSAKey(keyValue.getRSAKeyValue());
} else if (keyValue.getECKeyValue() != null) {
return getECKey(keyValue.getECKeyValue());
+ } else if (keyValue.getDHKeyValue() != null) {
+ return getDHKey(keyValue.getDHKeyValue());
} else {
return null;
}
@@ -792,6 +863,52 @@ public class KeyInfoSupport {
}
}
+
+ /**
+ * Builds a DH key from a {@link DHKeyValue} element. The element must contain values for all required DH public
+ * key parameters, including values for shared key family values P, Q and G (aka Generator).
+ *
+ * @param keyDescriptor the {@link DHKeyValue} key descriptor
+ *
+ * @return a new {@link DHPublicKey} instance of {@link PublicKey}
+ *
+ * @throws KeyException thrown if the key algorithm is not supported by the JCE or the key spec does not contain
+ * valid information
+ */
+ @Nonnull public static PublicKey getDHKey(@Nonnull final DHKeyValue keyDescriptor) throws KeyException {
+ if (!hasCompleteDHParams(keyDescriptor)) {
+ throw new KeyException("DHKeyValue element did not contain at least one of DH parameters P, Q or G");
+ }
+
+ final BigInteger gComponent = keyDescriptor.getGenerator().getValueBigInt();
+ final BigInteger pComponent = keyDescriptor.getP().getValueBigInt();
+ // Note: Java doesn't need or even accept the prime Q component, so don't bother to parse it
+
+ final BigInteger publicComponent = keyDescriptor.getPublic().getValueBigInt();
+
+ final DHPublicKeySpec keySpec = new DHPublicKeySpec(publicComponent, pComponent, gComponent);
+ return buildKey(keySpec, JCAConstants.KEY_ALGO_DIFFIE_HELLMAN);
+ }
+
+ /**
+ * Check whether the specified {@link DHKeyValue} element has the all optional DH values which can be shared
+ * amongst many keys in a DH "key family", and are presumed to be known from context.
+ *
+ * @param keyDescriptor the {@link DHKeyValue} element to check
+ * @return true if all parameters are present and non-empty, false otherwise
+ */
+ public static boolean hasCompleteDHParams(@Nullable final DHKeyValue keyDescriptor) {
+ if (keyDescriptor == null
+ || keyDescriptor.getGenerator() == null
+ || Strings.isNullOrEmpty(keyDescriptor.getGenerator().getValue())
+ || keyDescriptor.getP() == null || Strings.isNullOrEmpty(keyDescriptor.getP().getValue())
+ // Note: Java doesn't need or even accept the prime Q component. So even though it's
+ // required per the schema, relax the check here and don't require.
+ ) {
+ return false;
+ }
+ return true;
+ }
/**
* Builds an DSA key from a {@link DSAKeyValue} element. The element must contain values for all required DSA public
@@ -943,10 +1060,15 @@ public class KeyInfoSupport {
* @throws KeyException thrown if the given key data can not be converted into {@link PublicKey}
*/
@Nonnull public static PublicKey getKey(@Nonnull final DEREncodedKeyValue keyValue) throws KeyException{
+ // Note: Testing shows DH must come before DSA. If you attempt to decode a DH key as DSA,
+ // it "works", b/c they have similar structures, but it's probably not correct. DSA does not decode as DH,
+ // so this ordering is what works at present. If this methodology has a problem in the future, we'll likely
+ // need to switch to explicit ASN.1 parsing of the key's type, instead of "try and return what doesn't fail".
final String[] supportedKeyTypes = {
JCAConstants.KEY_ALGO_RSA,
- JCAConstants.KEY_ALGO_DSA,
- JCAConstants.KEY_ALGO_EC};
+ JCAConstants.KEY_ALGO_EC,
+ JCAConstants.KEY_ALGO_DIFFIE_HELLMAN,
+ JCAConstants.KEY_ALGO_DSA};
Constraint.isNotNull(keyValue, "DEREncodedKeyValue cannot be null");
if (keyValue.getValue() == null) {
@@ -961,15 +1083,17 @@ public class KeyInfoSupport {
// Iterate over the supported key types until one produces a public key.
for (final String keyType : supportedKeyTypes) {
+ getLogger().trace("Attempting to decode DER key as type: {}", keyType);
try {
final KeyFactory keyFactory = KeyFactory.getInstance(keyType);
final X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey);
final PublicKey publicKey = keyFactory.generatePublic(keySpec);
if (publicKey != null) {
+ getLogger().trace("DER key decoded successfully as type: {}", keyType);
return publicKey;
}
} catch (final NoSuchAlgorithmException | InvalidKeySpecException e) {
- // Do nothing, try the next type
+ getLogger().trace("DER key failed decoding as: {}", keyType);
}
}
throw new KeyException("DEREncodedKeyValue did not contain a supported key type");
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/signature/KeyValue.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/signature/KeyValue.java
index 0acc86627..c0776ab5c 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/signature/KeyValue.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/signature/KeyValue.java
@@ -21,6 +21,7 @@ import javax.annotation.Nullable;
import javax.xml.namespace.QName;
import org.opensaml.core.xml.XMLObject;
+import org.opensaml.xmlsec.encryption.DHKeyValue;
import org.opensaml.xmlsec.signature.support.SignatureConstants;
/**
@@ -84,6 +85,26 @@ public interface KeyValue extends XMLObject {
*/
public void setECKeyValue(@Nullable final ECKeyValue newECKeyValue);
+ /**
+ * Get the DHKeyValue child element.
+ *
+ * @return DHKeyValue child element
+ */
+ @Nullable public default DHKeyValue getDHKeyValue() {
+ //TODO remove 'default' and method body in next major version (v5)
+ throw new UnsupportedOperationException("Method not implemented");
+ }
+
+ /**
+ * Set the DHKeyValue child element.
+ *
+ * @param newDHKeyValue the new DHKeyValue child element
+ */
+ public default void setDHKeyValue(@Nullable final DHKeyValue newDHKeyValue) {
+ //TODO remove 'default' and method body in next major version (v5)
+ throw new UnsupportedOperationException("Method not implemented");
+ }
+
/**
* Get the wildcard <any> XMLObject child element.
*
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/DHWithExplicitKDFKeyAgreementProcessor.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/DHWithExplicitKDFKeyAgreementProcessor.java
new file mode 100644
index 000000000..493b86016
--- /dev/null
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/DHWithExplicitKDFKeyAgreementProcessor.java
@@ -0,0 +1,102 @@
+/*
+ * 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.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+
+import javax.annotation.Nonnull;
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
+
+import org.opensaml.security.credential.BasicCredential;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.crypto.dh.DHSupport;
+import org.opensaml.xmlsec.agreement.KeyAgreementException;
+import org.opensaml.xmlsec.agreement.KeyAgreementParameters;
+import org.opensaml.xmlsec.agreement.KeyAgreementProcessor;
+import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of {@link KeyAgreementProcessor} which performs Diffie-Hellman
+ * Ephemeral-Static Mode key agreement with Explicit Key Derivation Function as defined in XML Encryption 1.1.
+ */
+public class DHWithExplicitKDFKeyAgreementProcessor extends AbstractDerivationKeyAgreementProcessor {
+
+ /** Logger. */
+ private final Logger log = LoggerFactory.getLogger(DHWithExplicitKDFKeyAgreementProcessor.class);
+
+ /** {@inheritDoc} */
+ public String getAlgorithm() {
+ return EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH_EXPLICIT_KDF;
+ }
+
+ /** {@inheritDoc} */
+ protected Credential obtainPrivateCredential(@Nonnull final Credential publicCredential,
+ @Nonnull final KeyAgreementParameters parameters) throws KeyAgreementException {
+
+ final Credential suppliedCredential = super.obtainPrivateCredential(publicCredential, parameters);
+ if (suppliedCredential != null) {
+ return suppliedCredential;
+ }
+
+ log.debug("Found no supplied PrivateCredential in KeyAgreementParameters, generating ephemeral key pair");
+
+
+ if (!DHPublicKey.class.isInstance(publicCredential.getPublicKey())) {
+ throw new KeyAgreementException("Public credential's public key is not an instance of DHPublicKey");
+ }
+
+ final DHPublicKey publicKey = DHPublicKey.class.cast(publicCredential.getPublicKey());
+
+ try {
+ final KeyPair privateKeyPair = DHSupport.generateCompatibleKeyPair(publicKey, null);
+ return new BasicCredential(privateKeyPair.getPublic(), privateKeyPair.getPrivate());
+ } catch (final NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
+ throw new KeyAgreementException("Error generating private KeyPair from DH public key", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected byte[] generateAgreementSecret(@Nonnull final Credential publicCredential,
+ @Nonnull final Credential privateCredential, @Nonnull final KeyAgreementParameters parameters)
+ throws KeyAgreementException {
+
+ if (!DHPublicKey.class.isInstance(publicCredential.getPublicKey())) {
+ throw new KeyAgreementException("Public credential's public key is not an instance of DHPublicKey");
+ }
+ if (!DHPrivateKey.class.isInstance(privateCredential.getPrivateKey())) {
+ throw new KeyAgreementException("Private credential's private key is not an instance of DHPrivateKey");
+ }
+
+ final DHPublicKey publicKey = DHPublicKey.class.cast(publicCredential.getPublicKey());
+ final DHPrivateKey privateKey = DHPrivateKey.class.cast(privateCredential.getPrivateKey());
+
+ try {
+ return DHSupport.performKeyAgreement(publicKey, privateKey, null);
+ } catch (final InvalidKeyException | NoSuchAlgorithmException | NoSuchProviderException e) {
+ throw new KeyAgreementException("Error generating secret from public and private DH keys", e);
+ }
+ }
+
+}
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/DHWithLegacyKDFKeyAgreementProcessor.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/DHWithLegacyKDFKeyAgreementProcessor.java
new file mode 100644
index 000000000..a8638fbc0
--- /dev/null
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/DHWithLegacyKDFKeyAgreementProcessor.java
@@ -0,0 +1,146 @@
+/*
+ * 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.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+
+import javax.annotation.Nonnull;
+import javax.crypto.SecretKey;
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
+
+import org.opensaml.security.credential.BasicCredential;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.crypto.dh.DHSupport;
+import org.opensaml.xmlsec.agreement.KeyAgreementException;
+import org.opensaml.xmlsec.agreement.KeyAgreementParameters;
+import org.opensaml.xmlsec.agreement.KeyAgreementProcessor;
+import org.opensaml.xmlsec.agreement.KeyAgreementSupport;
+import org.opensaml.xmlsec.derivation.KeyDerivationException;
+import org.opensaml.xmlsec.derivation.impl.DHLegacyKDF;
+import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of {@link KeyAgreementProcessor} which performs Diffie-Hellman
+ * Ephemeral-Static Mode key agreement with Legacy Key Derivation Function as defined in XML Encryption 1.1.
+ */
+public class DHWithLegacyKDFKeyAgreementProcessor extends AbstractKeyAgreementProcessor {
+
+ /** Default digest method. */
+ public static final String DEFAULT_DIGEST_METHOD = EncryptionConstants.ALGO_ID_DIGEST_SHA256;
+
+ /** Logger. */
+ private final Logger log = LoggerFactory.getLogger(DHWithLegacyKDFKeyAgreementProcessor.class);
+
+ /** {@inheritDoc} */
+ public String getAlgorithm() {
+ return EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH;
+ }
+
+ /** {@inheritDoc} */
+ protected Credential obtainPrivateCredential(@Nonnull final Credential publicCredential,
+ @Nonnull final KeyAgreementParameters parameters) throws KeyAgreementException {
+
+ final Credential suppliedCredential = super.obtainPrivateCredential(publicCredential, parameters);
+ if (suppliedCredential != null) {
+ return suppliedCredential;
+ }
+
+ log.debug("Found no supplied PrivateCredential in KeyAgreementParameters, generating ephemeral key pair");
+
+
+ if (!DHPublicKey.class.isInstance(publicCredential.getPublicKey())) {
+ throw new KeyAgreementException("Public credential's public key is not an instance of DHPublicKey");
+ }
+
+ final DHPublicKey publicKey = DHPublicKey.class.cast(publicCredential.getPublicKey());
+
+ try {
+ final KeyPair privateKeyPair = DHSupport.generateCompatibleKeyPair(publicKey, null);
+ return new BasicCredential(privateKeyPair.getPublic(), privateKeyPair.getPrivate());
+ } catch (final NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
+ throw new KeyAgreementException("Error generating private KeyPair from DH public key", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected byte[] generateAgreementSecret(@Nonnull final Credential publicCredential,
+ @Nonnull final Credential privateCredential, @Nonnull final KeyAgreementParameters parameters)
+ throws KeyAgreementException {
+
+ if (!DHPublicKey.class.isInstance(publicCredential.getPublicKey())) {
+ throw new KeyAgreementException("Public credential's public key is not an instance of DHPublicKey");
+ }
+ if (!DHPrivateKey.class.isInstance(privateCredential.getPrivateKey())) {
+ throw new KeyAgreementException("Private credential's private key is not an instance of DHPrivateKey");
+ }
+
+ final DHPublicKey publicKey = DHPublicKey.class.cast(publicCredential.getPublicKey());
+ final DHPrivateKey privateKey = DHPrivateKey.class.cast(privateCredential.getPrivateKey());
+
+ try {
+ return DHSupport.performKeyAgreement(publicKey, privateKey, null);
+ } catch (final InvalidKeyException | NoSuchAlgorithmException | NoSuchProviderException e) {
+ throw new KeyAgreementException("Error generating secret from public and private DH keys", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected SecretKey deriveSecretKey(final byte[] secret, @Nonnull final String keyAlgorithm,
+ @Nonnull final KeyAgreementParameters parameters) throws KeyAgreementException {
+
+ final Integer keySize = parameters.contains(KeySize.class) ? parameters.get(KeySize.class).getSize() : null;
+
+ KeyAgreementSupport.validateKeyAlgorithmAndSize(keyAlgorithm, keySize);
+
+ String digestMethod = null;
+ if (parameters.contains(DigestMethod.class)) {
+ digestMethod = parameters.get(DigestMethod.class).getAlgorithm();
+ } else {
+ digestMethod = DEFAULT_DIGEST_METHOD;
+ // Need to add this to params so can be expressed on credential and in XML
+ final DigestMethod dm = new DigestMethod();
+ dm.setAlgorithm(digestMethod);
+ parameters.add(dm);
+ }
+
+ // Nonce is optional
+ String nonce = null;
+ if (parameters.contains(KANonce.class)) {
+ nonce = parameters.get(KANonce.class).getValue();
+ }
+
+ final DHLegacyKDF kdf = new DHLegacyKDF();
+ kdf.setDigestMethod(digestMethod);
+ kdf.setNonce(nonce);
+
+ try {
+ return kdf.derive(secret, keyAlgorithm, keySize);
+ } catch (final KeyDerivationException e) {
+ throw new KeyAgreementException("Key derivation failed using supplied KeyDerivation parameter", 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 9b317cc72..3a7715dab 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,23 @@
package org.opensaml.xmlsec.agreement.impl;
+import java.security.SecureRandom;
+
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.XMLRuntimeException;
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.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.codec.Base64Support;
+import net.shibboleth.utilities.java.support.codec.DecodingException;
+import net.shibboleth.utilities.java.support.codec.EncodingException;
import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
@@ -39,22 +46,27 @@ import net.shibboleth.utilities.java.support.primitive.StringSupport;
public class KANonce extends AbstractInitializableComponent
implements XMLExpressableKeyAgreementParameter, CloneableKeyAgreementParameter {
+ /** Default length for generated salt, in bytes. */
+ public static final Integer DEFAULT_GENERATED_LENGTH = 8;
+
/** Base64-encoded nonce value. */
@Nullable private String value;
- /** {@inheritDoc} */
- protected void doInitialize() throws ComponentInitializationException {
- if (value == null) {
- throw new ComponentInitializationException("KANonce value was null");
- }
- }
-
+ /** Generated salt length, in bytes. */
+ @NonnullAfterInit private Integer generatedLength;
+
+ /** SecureRandom generator for salt. */
+ @NonnullAfterInit private SecureRandom secureRandom;
+
/**
* Get the Base64-encoded nonce value.
*
* @return the nonce value
*/
@Nullable public String getValue() {
+ if (value == null && isInitialized()) {
+ value = generateValue();
+ }
return value;
}
@@ -67,6 +79,87 @@ public class KANonce extends AbstractInitializableComponent
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
value = StringSupport.trimOrNull(newValue);
}
+
+ /**
+ * Get the generated length, in bytes.
+ *
+ * @return the generated length, in bytes
+ */
+ @NonnullAfterInit public Integer getGeneratedLength() {
+ return generatedLength;
+ }
+
+ /**
+ * Set the generated length, in bytes.
+ *
+ * @param length the generated length
+ */
+ public void setGeneratedLength(@Nullable final Integer length) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ generatedLength = length;
+ }
+
+ /**
+ * Get the secure random generator.
+ *
+ * <p>
+ * Defaults to the platform default via <code>new SecureRandom()</code>
+ * </p>
+ *
+ * @return the secure random instance
+ */
+ @NonnullAfterInit public SecureRandom getRandom() {
+ return secureRandom;
+ }
+
+ /**
+ * Set the secure random generator.
+ *
+ * <p>
+ * Defaults to the platform default via <code>new SecureRandom()</code>
+ * </p>
+ *
+ * @param sr the secure random generator to set
+ */
+ public void setRandom(@Nullable final SecureRandom sr) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ secureRandom = sr;
+ }
+
+ /** {@inheritDoc} */
+ protected void doInitialize() throws ComponentInitializationException {
+ if (value != null) {
+ try {
+ Base64Support.decode(value);
+ } catch (final DecodingException e) {
+ throw new ComponentInitializationException("Nonce value was not valid Base64", e);
+ }
+ }
+
+ if (generatedLength == null) {
+ generatedLength = DEFAULT_GENERATED_LENGTH;
+ }
+
+ if (secureRandom == null) {
+ secureRandom = new SecureRandom();
+ }
+ }
+
+ /**
+ * Generate a new random value.
+ *
+ * @return the generated value
+ */
+ protected String generateValue() {
+ try {
+ final byte[] valueBytes = new byte[generatedLength];
+ secureRandom.nextBytes(valueBytes);
+ return Base64Support.encode(valueBytes, false);
+ } catch (final EncodingException e) {
+ // This should never really happen
+ throw new XMLRuntimeException("Error Base64-encoding generated nonce value salt", e);
+ }
+ }
/** {@inheritDoc} */
public KANonce clone() {
@@ -102,6 +195,10 @@ public class KANonce extends AbstractInitializableComponent
throws ComponentInitializationException {
Constraint.isNotNull(xmlObject, "XMLObject was null");
+ if (StringSupport.trimOrNull(xmlObject.getValue()) == null) {
+ throw new ComponentInitializationException("XML KANonce had a null or empty value");
+ }
+
final KANonce parameter = new KANonce();
parameter.setValue(xmlObject.getValue());
parameter.initialize();
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 1dbbd939c..d21a30df8 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
@@ -27,6 +27,8 @@ import java.util.Set;
import javax.annotation.Nonnull;
import org.opensaml.security.crypto.JCAConstants;
+import org.opensaml.xmlsec.agreement.impl.DigestMethod;
+import org.opensaml.xmlsec.agreement.impl.KANonce;
import org.opensaml.xmlsec.derivation.impl.ConcatKDF;
import org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver;
import org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;
@@ -126,6 +128,18 @@ public class DefaultSecurityConfigurationBootstrap {
ecConfig.setParameters(Set.of(ecConcatKDF));
kaConfigs.put(JCAConstants.KEY_ALGO_EC, ecConfig);
+ // For DH we default the Legacy KDF variant as that is mandatory for DH support.
+ final KeyAgreementEncryptionConfiguration dhConfig = new KeyAgreementEncryptionConfiguration();
+ dhConfig.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH);
+ final DigestMethod digestMethod = new DigestMethod();
+ digestMethod.setAlgorithm(EncryptionConstants.ALGO_ID_DIGEST_SHA256);
+ digestMethod.initialize();
+ KANonce nonce = new KANonce();
+ // This will use an auto-generated nonce value each time
+ nonce.initialize();
+ dhConfig.setParameters(Set.of(digestMethod, nonce));
+ kaConfigs.put(JCAConstants.KEY_ALGO_DH, dhConfig);
+
config.setKeyAgreementConfigurations(kaConfigs);
} catch (final ComponentInitializationException e) {
LOG.error("Initialization failure on global key agreement encryption configuration, will be unusable", e);
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/DHLegacyKDF.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/DHLegacyKDF.java
new file mode 100644
index 000000000..889b894c7
--- /dev/null
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/DHLegacyKDF.java
@@ -0,0 +1,186 @@
+/*
+ * 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.derivation.impl;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.codec.binary.Hex;
+import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
+import org.opensaml.xmlsec.derivation.KeyDerivationException;
+import org.opensaml.xmlsec.derivation.KeyDerivationSupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Charsets;
+import com.google.common.primitives.Bytes;
+
+import net.shibboleth.utilities.java.support.codec.Base64Support;
+import net.shibboleth.utilities.java.support.codec.DecodingException;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * Implementation of the key derivation function used with Diffie-Hellman Key Agreement With Legacy Key Derivation
+ * Function as defined in XML Encryption 1.1.
+ */
+public class DHLegacyKDF {
+
+ /** Logger. */
+ private final Logger log = LoggerFactory.getLogger(DHLegacyKDF.class);
+
+ /** Digest method. */
+ @Nullable private String digestMethod;
+
+ /** Nonce. */
+ @Nullable private String nonce;
+
+ /**
+ * Get the digest method algorithm URI.
+ *
+ * @return the algorithm URI
+ */
+ @Nullable public String getDigestMethod() {
+ return digestMethod;
+ }
+
+ /**
+ * Set the digest method algorithm URI.
+ *
+ * @param newDigestMethod the algorithm URI
+ */
+ public void setDigestMethod(@Nullable final String newDigestMethod) {
+ digestMethod = StringSupport.trimOrNull(newDigestMethod);
+ }
+
+ /**
+ * Get the Base64-encoded nonce value.
+ *
+ * @return the nonce value
+ */
+ @Nullable public String getNonce() {
+ return nonce;
+ }
+
+ /**
+ * Set the digest method algorithm URI.
+ *
+ * @param newNonce the algorithm URI
+ */
+ public void setNonce(@Nullable final String newNonce) {
+ nonce = StringSupport.trimOrNull(newNonce);
+ }
+
+ /** {@inheritDoc} */
+ public SecretKey derive(@Nonnull final byte[] secret, @Nonnull final String keyAlgorithm,
+ @Nullable final Integer keyLength) throws KeyDerivationException {
+ Constraint.isNotNull(secret, "Secret byte[] was null");
+ Constraint.isNotNull(keyAlgorithm, "Key algorithm was null");
+
+ final String jcaKeyAlgorithm = KeyDerivationSupport.getJCAKeyAlgorithm(keyAlgorithm);
+
+ final Integer jcaKeyLength = KeyDerivationSupport.getEffectiveKeyLength(keyAlgorithm, keyLength);
+
+ final byte[] keyBytes = deriveBytes(secret, keyAlgorithm, jcaKeyLength);
+
+ return new SecretKeySpec(keyBytes, jcaKeyAlgorithm);
+ }
+
+ /**
+ * Derive the key bytes from the specified inputs.
+ *
+ * @param secret the input secret
+ * @param encryptionAlgorithm the encryption algorithm URI to be used with the derived key
+ * @param keyLength the key length
+ *
+ * @return derived bytes the derived key bytes
+ *
+ * @throws KeyDerivationException if any of the inputs are invalid
+ */
+ protected byte[] deriveBytes(@Nonnull final byte[] secret, @Nonnull final String encryptionAlgorithm,
+ @Nonnull final Integer keyLength) throws KeyDerivationException {
+
+ byte[] derived = new byte[] {};
+
+ final String jcaDigest = AlgorithmSupport.getAlgorithmID(digestMethod);
+ if (jcaDigest == null) {
+ log.warn("Could not resolve JCA algorithm ID from URI: {}", jcaDigest);
+ throw new KeyDerivationException("Could not resolve JCA digest from URI: " + digestMethod);
+ }
+
+ try {
+ final byte[] nonceBytes = nonce != null ? Base64Support.decode(nonce) : new byte[] {};
+ int counter = 0;
+ while ((derived.length * 8) < keyLength) {
+ derived = Bytes.concat(derived, digest(++counter, jcaDigest, secret, encryptionAlgorithm, keyLength,
+ nonceBytes));
+ }
+ } catch (final DecodingException e) {
+ log.error("Fatal error Base64-decoding supplied nonce value: {}", nonce, e);
+ throw new KeyDerivationException("Fatal error decoding nonce", e);
+ }
+
+ return Arrays.copyOfRange(derived, 0, keyLength/8);
+ }
+
+ /**
+ * Produce the digest of the specified inputs according to XML Encryption section 1.1, section 5.6.2.2.
+ *
+ * @param counter the counter value
+ * @param digestAlgorithm the JCA digest algorithm
+ * @param secret the input secret
+ * @param encryptionAlgorithm the encryption algorithm URI to be used with the derived key
+ * @param keyLength the key length
+ * @param nonceBytes the nonce, which may be an empty byte[] array, but not null
+ *
+ * @return digest output for the specified inputs
+ *
+ * @throws KeyDerivationException if any of the inputs are invalid
+ */
+ // CheckStyle: ParameterNumber OFF
+ protected byte[] digest(final int counter, @Nonnull final String digestAlgorithm, @Nonnull final byte[] secret,
+ @Nonnull final String encryptionAlgorithm, @Nonnull final Integer keyLength,
+ @Nonnull final byte[] nonceBytes) throws KeyDerivationException {
+
+ final byte[] digestInput = Bytes.concat(
+ secret,
+ String.format("%02d", counter).getBytes(Charsets.UTF_8),
+ encryptionAlgorithm.getBytes(Charsets.UTF_8),
+ nonceBytes,
+ keyLength.toString().getBytes(Charsets.UTF_8));
+
+ log.trace("Digest input for counter={} in hex was: {}", counter, Hex.encodeHexString(digestInput, false));
+
+ try {
+ final MessageDigest md = MessageDigest.getInstance(digestAlgorithm);
+ final byte[] output = md.digest(digestInput);
+ log.trace("Digest output for counter={} in hex was: {}", counter, Hex.encodeHexString(output, false));
+ return output;
+ } catch (final NoSuchAlgorithmException e) {
+ throw new KeyDerivationException("Fatal error computing digest for key derivation", e);
+ }
+ }
+ // CheckStyle: ParameterNumber ON
+
+}
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/signature/impl/KeyValueImpl.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/signature/impl/KeyValueImpl.java
index d1186209c..fb798e8fc 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/signature/impl/KeyValueImpl.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/signature/impl/KeyValueImpl.java
@@ -23,6 +23,7 @@ import java.util.List;
import org.opensaml.core.xml.AbstractXMLObject;
import org.opensaml.core.xml.XMLObject;
+import org.opensaml.xmlsec.encryption.DHKeyValue;
import org.opensaml.xmlsec.signature.DSAKeyValue;
import org.opensaml.xmlsec.signature.ECKeyValue;
import org.opensaml.xmlsec.signature.KeyValue;
@@ -32,6 +33,9 @@ import org.opensaml.xmlsec.signature.RSAKeyValue;
* Concrete implementation of {@link org.opensaml.xmlsec.signature.KeyValue}.
*/
public class KeyValueImpl extends AbstractXMLObject implements KeyValue {
+
+ /** DHKeyValue child element. */
+ private DHKeyValue dhKeyValue;
/** DSAKeyValue child element. */
private DSAKeyValue dsaKeyValue;
@@ -56,6 +60,16 @@ public class KeyValueImpl extends AbstractXMLObject implements KeyValue {
super(namespaceURI, elementLocalName, namespacePrefix);
}
+ /** {@inheritDoc} */
+ public DHKeyValue getDHKeyValue() {
+ return dhKeyValue;
+ }
+
+ /** {@inheritDoc} */
+ public void setDHKeyValue(final DHKeyValue newDHKeyValue) {
+ dhKeyValue = prepareForAssignment(dhKeyValue, newDHKeyValue);
+ }
+
/** {@inheritDoc} */
public DSAKeyValue getDSAKeyValue() {
return dsaKeyValue;
@@ -109,6 +123,9 @@ public class KeyValueImpl extends AbstractXMLObject implements KeyValue {
if (ecKeyValue != null) {
children.add(ecKeyValue);
}
+ if (dhKeyValue != null) {
+ children.add(dhKeyValue);
+ }
if (unknownXMLObject != null) {
children.add(unknownXMLObject);
}
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/signature/impl/KeyValueUnmarshaller.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/signature/impl/KeyValueUnmarshaller.java
index 8e3aa8940..d1d76fc22 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/signature/impl/KeyValueUnmarshaller.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/signature/impl/KeyValueUnmarshaller.java
@@ -19,6 +19,7 @@ package org.opensaml.xmlsec.signature.impl;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.core.xml.io.UnmarshallingException;
+import org.opensaml.xmlsec.encryption.DHKeyValue;
import org.opensaml.xmlsec.signature.DSAKeyValue;
import org.opensaml.xmlsec.signature.ECKeyValue;
import org.opensaml.xmlsec.signature.KeyValue;
@@ -40,6 +41,8 @@ public class KeyValueUnmarshaller extends AbstractXMLSignatureUnmarshaller {
keyValue.setRSAKeyValue((RSAKeyValue) childXMLObject);
} else if (childXMLObject instanceof ECKeyValue) {
keyValue.setECKeyValue((ECKeyValue) childXMLObject);
+ } else if (childXMLObject instanceof DHKeyValue) {
+ keyValue.setDHKeyValue((DHKeyValue) childXMLObject);
} else {
// There can be only one...
if (keyValue.getUnknownXMLObject() == null) {
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
index b5d636f8b..ff767caf6 100644
--- 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
@@ -1 +1,3 @@
-org.opensaml.xmlsec.agreement.impl.ECDHKeyAgreementProcessor
\ No newline at end of file
+org.opensaml.xmlsec.agreement.impl.ECDHKeyAgreementProcessor
+org.opensaml.xmlsec.agreement.impl.DHWithExplicitKDFKeyAgreementProcessor
+org.opensaml.xmlsec.agreement.impl.DHWithLegacyKDFKeyAgreementProcessor
\ No newline at end of file
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/ECDHKeyAgreementProcessorTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/DHWithExplicitKDFKeyAgreementProcessorTest.java
similarity index 88%
copy from opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/ECDHKeyAgreementProcessorTest.java
copy to opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/DHWithExplicitKDFKeyAgreementProcessorTest.java
index 0a909e659..9e0ed3648 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/ECDHKeyAgreementProcessorTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/DHWithExplicitKDFKeyAgreementProcessorTest.java
@@ -18,7 +18,6 @@
package org.opensaml.xmlsec.agreement.impl;
import java.security.KeyPair;
-import java.security.spec.ECGenParameterSpec;
import java.util.ArrayList;
import java.util.Collection;
@@ -40,18 +39,18 @@ import org.testng.annotations.Test;
/**
*
*/
-public class ECDHKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
+public class DHWithExplicitKDFKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
- private ECDHKeyAgreementProcessor processor;
+ private DHWithExplicitKDFKeyAgreementProcessor processor;
@BeforeMethod
public void setUp() {
- processor = new ECDHKeyAgreementProcessor();
+ processor = new DHWithExplicitKDFKeyAgreementProcessor();
}
@Test
public void encryptingCase() throws Exception {
- KeyPair recipientKeyPair = KeySupport.generateKeyPair("EC", new ECGenParameterSpec("secp256r1"), null);
+ KeyPair recipientKeyPair = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN, 2048, null);
Credential recipientCredential = CredentialSupport.getSimpleCredential(recipientKeyPair.getPublic(), null);
KeyAgreementParameters params = new KeyAgreementParameters();
@@ -81,21 +80,21 @@ public class ECDHKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
Assert.assertNotNull(keyAgreementCredential.getOriginatorCredential().getPrivateKey());
Assert.assertNull(keyAgreementCredential.getOriginatorCredential().getSecretKey());
- Assert.assertEquals(keyAgreementCredential.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+ Assert.assertEquals(keyAgreementCredential.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH_EXPLICIT_KDF);
Assert.assertEquals(keyAgreementCredential.getParameters().size(), 2);
Assert.assertTrue(keyAgreementCredential.getParameters().contains(MockKeyDerivation.class));
Assert.assertTrue(keyAgreementCredential.getParameters().contains(KANonce.class));
- Assert.assertEquals(keyAgreementCredential.getParameters().get(KANonce.class).getValue(), "someBase64");
+ Assert.assertEquals(keyAgreementCredential.getParameters().get(KANonce.class).getValue(), "AABBCCDD");
}
@Test
public void decryptingCase() throws Exception {
- KeyPair originatorKeyPair = KeySupport.generateKeyPair("EC", new ECGenParameterSpec("secp256r1"), null);
+ KeyPair originatorKeyPair = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN, 2048, null);
Credential originatorCredential = CredentialSupport.getSimpleCredential(originatorKeyPair.getPublic(), null);
- KeyPair recipientKeyPair = KeySupport.generateKeyPair("EC", new ECGenParameterSpec("secp256r1"), null);
+ KeyPair recipientKeyPair = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN, 2048, null);
Credential recipientCredential = CredentialSupport.getSimpleCredential(recipientKeyPair.getPublic(), recipientKeyPair.getPrivate());
KeyAgreementParameters params = new KeyAgreementParameters();
@@ -126,13 +125,13 @@ public class ECDHKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
Assert.assertNull(keyAgreementCredential.getOriginatorCredential().getPrivateKey());
Assert.assertNull(keyAgreementCredential.getOriginatorCredential().getSecretKey());
- Assert.assertEquals(keyAgreementCredential.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+ Assert.assertEquals(keyAgreementCredential.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH_EXPLICIT_KDF);
Assert.assertEquals(keyAgreementCredential.getParameters().size(), 3);
Assert.assertTrue(keyAgreementCredential.getParameters().contains(PrivateCredential.class));
Assert.assertTrue(keyAgreementCredential.getParameters().contains(MockKeyDerivation.class));
Assert.assertTrue(keyAgreementCredential.getParameters().contains(KANonce.class));
- Assert.assertEquals(keyAgreementCredential.getParameters().get(KANonce.class).getValue(), "someBase64");
+ Assert.assertEquals(keyAgreementCredential.getParameters().get(KANonce.class).getValue(), "AABBCCDD");
}
@@ -152,7 +151,7 @@ public class ECDHKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
@Test(expectedExceptions = KeyAgreementException.class)
public void keyDerivationError() throws Exception {
- KeyPair kp = KeySupport.generateKeyPair("EC", new ECGenParameterSpec("secp256r1"), null);
+ KeyPair kp = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN, 2048, null);
Credential publicCredential = CredentialSupport.getSimpleCredential(kp.getPublic(), null);
KeyAgreementParameters params = new KeyAgreementParameters();
@@ -166,7 +165,7 @@ public class ECDHKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
@Test(expectedExceptions = KeyAgreementException.class)
public void missingKeyDerivationParam() throws Exception {
- KeyPair kp = KeySupport.generateKeyPair("EC", new ECGenParameterSpec("secp256r1"), null);
+ KeyPair kp = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN, 2048, null);
Credential publicCredential = CredentialSupport.getSimpleCredential(kp.getPublic(), null);
KeyAgreementParameters params = new KeyAgreementParameters();
@@ -179,7 +178,7 @@ public class ECDHKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
@Test(expectedExceptions = KeyAgreementException.class)
public void specifiedKeySizeMismatch() throws Exception {
- KeyPair kp = KeySupport.generateKeyPair("EC", new ECGenParameterSpec("secp256r1"), null);
+ KeyPair kp = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN, 2048, null);
Credential publicCredential = CredentialSupport.getSimpleCredential(kp.getPublic(), null);
KeyAgreementParameters params = new KeyAgreementParameters();
@@ -194,7 +193,7 @@ public class ECDHKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
private Collection<KeyAgreementParameter> getMockParams() {
ArrayList<KeyAgreementParameter> params = new ArrayList<>();
KANonce nonce = new KANonce();
- nonce.setValue("someBase64");
+ nonce.setValue("AABBCCDD");
params.add(nonce);
return params;
}
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/ECDHKeyAgreementProcessorTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/DHWithLegacyKDFKeyAgreementProcessorTest.java
similarity index 73%
copy from opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/ECDHKeyAgreementProcessorTest.java
copy to opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/DHWithLegacyKDFKeyAgreementProcessorTest.java
index 0a909e659..6dd82b3c7 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/ECDHKeyAgreementProcessorTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/DHWithLegacyKDFKeyAgreementProcessorTest.java
@@ -18,7 +18,6 @@
package org.opensaml.xmlsec.agreement.impl;
import java.security.KeyPair;
-import java.security.spec.ECGenParameterSpec;
import java.util.ArrayList;
import java.util.Collection;
@@ -33,6 +32,7 @@ import org.opensaml.xmlsec.agreement.KeyAgreementParameter;
import org.opensaml.xmlsec.agreement.KeyAgreementParameters;
import org.opensaml.xmlsec.derivation.impl.MockKeyDerivation;
import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
+import org.opensaml.xmlsec.signature.support.SignatureConstants;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -40,23 +40,29 @@ import org.testng.annotations.Test;
/**
*
*/
-public class ECDHKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
+public class DHWithLegacyKDFKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
- private ECDHKeyAgreementProcessor processor;
+ private DHWithLegacyKDFKeyAgreementProcessor processor;
@BeforeMethod
public void setUp() {
- processor = new ECDHKeyAgreementProcessor();
+ processor = new DHWithLegacyKDFKeyAgreementProcessor();
}
@Test
public void encryptingCase() throws Exception {
- KeyPair recipientKeyPair = KeySupport.generateKeyPair("EC", new ECGenParameterSpec("secp256r1"), null);
+ KeyPair recipientKeyPair = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN, 2048, null);
Credential recipientCredential = CredentialSupport.getSimpleCredential(recipientKeyPair.getPublic(), null);
KeyAgreementParameters params = new KeyAgreementParameters();
- params.add(new MockKeyDerivation());
- params.addAll(getMockParams());
+ DigestMethod dm = new DigestMethod();
+ dm.setAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA256);
+ dm.initialize();
+ params.add(dm);
+ KANonce nonce = new KANonce();
+ nonce.setValue("AABBCCDD");
+ nonce.initialize();
+ params.add(nonce);
KeyAgreementCredential keyAgreementCredential = processor.execute(recipientCredential,
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128,
@@ -81,27 +87,33 @@ public class ECDHKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
Assert.assertNotNull(keyAgreementCredential.getOriginatorCredential().getPrivateKey());
Assert.assertNull(keyAgreementCredential.getOriginatorCredential().getSecretKey());
- Assert.assertEquals(keyAgreementCredential.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+ Assert.assertEquals(keyAgreementCredential.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH);
Assert.assertEquals(keyAgreementCredential.getParameters().size(), 2);
- Assert.assertTrue(keyAgreementCredential.getParameters().contains(MockKeyDerivation.class));
+ Assert.assertTrue(keyAgreementCredential.getParameters().contains(DigestMethod.class));
+ Assert.assertEquals(keyAgreementCredential.getParameters().get(DigestMethod.class).getAlgorithm(), SignatureConstants.ALGO_ID_DIGEST_SHA256);
Assert.assertTrue(keyAgreementCredential.getParameters().contains(KANonce.class));
- Assert.assertEquals(keyAgreementCredential.getParameters().get(KANonce.class).getValue(), "someBase64");
-
+ Assert.assertEquals(keyAgreementCredential.getParameters().get(KANonce.class).getValue(), "AABBCCDD");
}
@Test
public void decryptingCase() throws Exception {
- KeyPair originatorKeyPair = KeySupport.generateKeyPair("EC", new ECGenParameterSpec("secp256r1"), null);
+ KeyPair originatorKeyPair = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN, 2048, null);
Credential originatorCredential = CredentialSupport.getSimpleCredential(originatorKeyPair.getPublic(), null);
- KeyPair recipientKeyPair = KeySupport.generateKeyPair("EC", new ECGenParameterSpec("secp256r1"), null);
+ KeyPair recipientKeyPair = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN, 2048, null);
Credential recipientCredential = CredentialSupport.getSimpleCredential(recipientKeyPair.getPublic(), recipientKeyPair.getPrivate());
KeyAgreementParameters params = new KeyAgreementParameters();
params.add(new PrivateCredential(recipientCredential));
- params.add(new MockKeyDerivation());
- params.addAll(getMockParams());
+ DigestMethod dm = new DigestMethod();
+ dm.setAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA256);
+ dm.initialize();
+ params.add(dm);
+ KANonce nonce = new KANonce();
+ nonce.setValue("AABBCCDD");
+ nonce.initialize();
+ params.add(nonce);
KeyAgreementCredential keyAgreementCredential = processor.execute(originatorCredential,
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128,
@@ -126,13 +138,14 @@ public class ECDHKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
Assert.assertNull(keyAgreementCredential.getOriginatorCredential().getPrivateKey());
Assert.assertNull(keyAgreementCredential.getOriginatorCredential().getSecretKey());
- Assert.assertEquals(keyAgreementCredential.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+ Assert.assertEquals(keyAgreementCredential.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH);
Assert.assertEquals(keyAgreementCredential.getParameters().size(), 3);
Assert.assertTrue(keyAgreementCredential.getParameters().contains(PrivateCredential.class));
- Assert.assertTrue(keyAgreementCredential.getParameters().contains(MockKeyDerivation.class));
+ Assert.assertTrue(keyAgreementCredential.getParameters().contains(DigestMethod.class));
+ Assert.assertEquals(keyAgreementCredential.getParameters().get(DigestMethod.class).getAlgorithm(), SignatureConstants.ALGO_ID_DIGEST_SHA256);
Assert.assertTrue(keyAgreementCredential.getParameters().contains(KANonce.class));
- Assert.assertEquals(keyAgreementCredential.getParameters().get(KANonce.class).getValue(), "someBase64");
+ Assert.assertEquals(keyAgreementCredential.getParameters().get(KANonce.class).getValue(), "AABBCCDD");
}
@@ -142,8 +155,14 @@ public class ECDHKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
Credential publicCredential = CredentialSupport.getSimpleCredential(kp.getPublic(), null);
KeyAgreementParameters params = new KeyAgreementParameters();
- params.add(new MockKeyDerivation());
- params.addAll(getMockParams());
+ DigestMethod dm = new DigestMethod();
+ dm.setAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA256);
+ dm.initialize();
+ params.add(dm);
+ KANonce nonce = new KANonce();
+ nonce.setValue("AABBCCDD");
+ nonce.initialize();
+ params.add(nonce);
processor.execute(publicCredential,
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128,
@@ -151,53 +170,45 @@ public class ECDHKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
}
@Test(expectedExceptions = KeyAgreementException.class)
- public void keyDerivationError() throws Exception {
- KeyPair kp = KeySupport.generateKeyPair("EC", new ECGenParameterSpec("secp256r1"), null);
+ public void invalidKeyAlgorithm() throws Exception {
+ KeyPair kp = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN, 2048, null);
Credential publicCredential = CredentialSupport.getSimpleCredential(kp.getPublic(), null);
KeyAgreementParameters params = new KeyAgreementParameters();
- params.add(new MockKeyDerivation());
- params.addAll(getMockParams());
+ DigestMethod dm = new DigestMethod();
+ dm.setAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA256);
+ dm.initialize();
+ params.add(dm);
+ KANonce nonce = new KANonce();
+ nonce.setValue("AABBCCDD");
+ nonce.initialize();
+ params.add(nonce);
processor.execute(publicCredential,
"urn:test:InvalidBlockEncryption",
params);
}
- @Test(expectedExceptions = KeyAgreementException.class)
- public void missingKeyDerivationParam() throws Exception {
- KeyPair kp = KeySupport.generateKeyPair("EC", new ECGenParameterSpec("secp256r1"), null);
- Credential publicCredential = CredentialSupport.getSimpleCredential(kp.getPublic(), null);
-
- KeyAgreementParameters params = new KeyAgreementParameters();
- params.addAll(getMockParams());
-
- processor.execute(publicCredential,
- EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128,
- params);
- }
-
@Test(expectedExceptions = KeyAgreementException.class)
public void specifiedKeySizeMismatch() throws Exception {
- KeyPair kp = KeySupport.generateKeyPair("EC", new ECGenParameterSpec("secp256r1"), null);
+ KeyPair kp = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN, 2048, null);
Credential publicCredential = CredentialSupport.getSimpleCredential(kp.getPublic(), null);
KeyAgreementParameters params = new KeyAgreementParameters();
- params.add(new MockKeyDerivation());
params.add(new KeySize(256));
+ DigestMethod dm = new DigestMethod();
+ dm.setAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA256);
+ dm.initialize();
+ params.add(dm);
+ KANonce nonce = new KANonce();
+ nonce.setValue("AABBCCDD");
+ nonce.initialize();
+ params.add(nonce);
+
processor.execute(publicCredential,
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128,
params);
}
-
- private Collection<KeyAgreementParameter> getMockParams() {
- ArrayList<KeyAgreementParameter> params = new ArrayList<>();
- KANonce nonce = new KANonce();
- nonce.setValue("someBase64");
- params.add(nonce);
- return params;
- }
-
}
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/ECDHKeyAgreementProcessorTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/ECDHKeyAgreementProcessorTest.java
index 0a909e659..af74f296e 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/ECDHKeyAgreementProcessorTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/ECDHKeyAgreementProcessorTest.java
@@ -86,7 +86,7 @@ public class ECDHKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
Assert.assertEquals(keyAgreementCredential.getParameters().size(), 2);
Assert.assertTrue(keyAgreementCredential.getParameters().contains(MockKeyDerivation.class));
Assert.assertTrue(keyAgreementCredential.getParameters().contains(KANonce.class));
- Assert.assertEquals(keyAgreementCredential.getParameters().get(KANonce.class).getValue(), "someBase64");
+ Assert.assertEquals(keyAgreementCredential.getParameters().get(KANonce.class).getValue(), "AABBCCDD");
}
@@ -132,7 +132,7 @@ public class ECDHKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
Assert.assertTrue(keyAgreementCredential.getParameters().contains(PrivateCredential.class));
Assert.assertTrue(keyAgreementCredential.getParameters().contains(MockKeyDerivation.class));
Assert.assertTrue(keyAgreementCredential.getParameters().contains(KANonce.class));
- Assert.assertEquals(keyAgreementCredential.getParameters().get(KANonce.class).getValue(), "someBase64");
+ Assert.assertEquals(keyAgreementCredential.getParameters().get(KANonce.class).getValue(), "AABBCCDD");
}
@@ -194,7 +194,7 @@ public class ECDHKeyAgreementProcessorTest extends OpenSAMLInitBaseTestCase {
private Collection<KeyAgreementParameter> getMockParams() {
ArrayList<KeyAgreementParameter> params = new ArrayList<>();
KANonce nonce = new KANonce();
- nonce.setValue("someBase64");
+ nonce.setValue("AABBCCDD");
params.add(nonce);
return params;
}
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 fb5989803..64520465d 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
@@ -22,6 +22,7 @@ import org.opensaml.core.xml.XMLObject;
import org.testng.Assert;
import org.testng.annotations.Test;
+import net.shibboleth.utilities.java.support.codec.Base64Support;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.UnmodifiableComponentException;
@@ -31,14 +32,14 @@ import net.shibboleth.utilities.java.support.component.UnmodifiableComponentExce
public class KANonceTest extends XMLObjectBaseTestCase {
@Test
- public void basic() throws ComponentInitializationException {
+ public void basic() throws Exception {
KANonce nonce = new KANonce();
- nonce.setValue("someBase64== ");
+ nonce.setValue("AABBCCDD");
nonce.initialize();
- Assert.assertEquals(nonce.getValue(), "someBase64==");
+ Assert.assertEquals(nonce.getValue(), "AABBCCDD");
try {
- nonce.setValue("foo");
+ nonce.setValue("BBCCDDEE");
Assert.fail("Modify of initialzied component should have failed");
} catch (UnmodifiableComponentException e) {
// expected
@@ -46,25 +47,51 @@ public class KANonceTest extends XMLObjectBaseTestCase {
KANonce cloned = nonce.clone();
Assert.assertTrue(cloned.isInitialized());
- Assert.assertEquals(cloned.getValue(), "someBase64==");
+ Assert.assertEquals(cloned.getValue(), "AABBCCDD");
XMLObject xmlObject = nonce.buildXMLObject();
Assert.assertNotNull(xmlObject);
Assert.assertTrue(org.opensaml.xmlsec.encryption.KANonce.class.isInstance(xmlObject));
org.opensaml.xmlsec.encryption.KANonce xmlNonce = org.opensaml.xmlsec.encryption.KANonce.class.cast(xmlObject);
- Assert.assertEquals(xmlNonce.getValue(), "someBase64==");
+ Assert.assertEquals(xmlNonce.getValue(), "AABBCCDD");
}
+ @Test()
+ public void generatedValue() throws Exception {
+ KANonce nonce = new KANonce();
+ // Don't generate a value unless initialized
+ Assert.assertNull(nonce.getValue());
+ nonce.initialize();
+
+ String initValue = nonce.getValue();
+ Assert.assertNotNull(initValue);
+ Assert.assertEquals(Base64Support.decode(initValue).length, nonce.getGeneratedLength().intValue());
+ // Once generated value shouldn't change
+ Assert.assertEquals(nonce.getValue(), initValue);
+ Assert.assertEquals(nonce.getValue(), initValue);
+ }
+
+ @Test()
+ public void generatedLength() throws Exception {
+ KANonce nonce = new KANonce();
+ nonce.setGeneratedLength(16);
+ nonce.initialize();
+
+ String initValue = nonce.getValue();
+ Assert.assertNotNull(initValue);
+ Assert.assertEquals(Base64Support.decode(initValue).length, nonce.getGeneratedLength().intValue());
+ }
+
@Test
public void fromXMLObject() throws Exception {
org.opensaml.xmlsec.encryption.KANonce xmlObject = buildXMLObject(org.opensaml.xmlsec.encryption.KANonce.DEFAULT_ELEMENT_NAME);
- xmlObject.setValue("someBase64==");
+ xmlObject.setValue("AABBCCDD");
KANonce parameter = KANonce.fromXMLObject(xmlObject);
Assert.assertNotNull(parameter);
Assert.assertTrue(parameter.isInitialized());
- Assert.assertEquals(parameter.getValue(), "someBase64==");
+ Assert.assertEquals(parameter.getValue(), "AABBCCDD");
xmlObject.setValue(null);
@@ -77,10 +104,4 @@ public class KANonceTest extends XMLObjectBaseTestCase {
}
}
- @Test(expectedExceptions = ComponentInitializationException.class)
- public void missingValue() throws ComponentInitializationException {
- KANonce nonce = new KANonce();
- nonce.initialize();
- }
-
}
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
index 337e649f1..aba9cbf3a 100644
--- 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
@@ -22,6 +22,8 @@ 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.DHWithExplicitKDFKeyAgreementProcessor;
+import org.opensaml.xmlsec.agreement.impl.DHWithLegacyKDFKeyAgreementProcessor;
import org.opensaml.xmlsec.agreement.impl.ECDHKeyAgreementProcessor;
import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
import org.testng.Assert;
@@ -37,12 +39,22 @@ public class GlobalKeyAgreementProcessorRegistryTest extends OpenSAMLInitBaseTes
KeyAgreementProcessorRegistry registry = KeyAgreementSupport.getGlobalProcessorRegistry();
Assert.assertNotNull(registry);
- Assert.assertEquals(registry.getRegisteredAlgorithms().size(), 1);
+ Assert.assertEquals(registry.getRegisteredAlgorithms().size(), 3);
+
+ Assert.assertEquals(registry.getRegisteredAlgorithms(), Set.of(EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES,
+ EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH, EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH_EXPLICIT_KDF));
- 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)));
+
+ Assert.assertNotNull(registry.getProcessor(EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH));
+ Assert.assertTrue(DHWithLegacyKDFKeyAgreementProcessor.class.isInstance(
+ registry.getProcessor(EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH)));
+
+ Assert.assertNotNull(registry.getProcessor(EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH_EXPLICIT_KDF));
+ Assert.assertTrue(DHWithExplicitKDFKeyAgreementProcessor.class.isInstance(
+ registry.getProcessor(EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH_EXPLICIT_KDF)));
}
}
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 85561c287..d2ef571ee 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
@@ -21,11 +21,9 @@ 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;
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/DHLegacyKDFTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/DHLegacyKDFTest.java
new file mode 100644
index 000000000..3e5e15ebe
--- /dev/null
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/DHLegacyKDFTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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.derivation.impl;
+
+import javax.crypto.SecretKey;
+
+import org.apache.commons.codec.binary.Hex;
+import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
+import org.opensaml.security.crypto.JCAConstants;
+import org.opensaml.xmlsec.derivation.KeyDerivationException;
+import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
+import org.opensaml.xmlsec.signature.support.SignatureConstants;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import net.shibboleth.utilities.java.support.codec.Base64Support;
+
+/**
+ *
+ */
+public class DHLegacyKDFTest extends OpenSAMLInitBaseTestCase {
+
+ @Test
+ public void specTestVector() throws Exception {
+ // This tests the example test data from XML Encryption 1.1 section 5.6.2.2.
+ DHLegacyKDF kdf = new DHLegacyKDF();
+ kdf.setDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA1);
+ kdf.setNonce("Zm9v");
+
+ byte[] digestCounter1 = kdf.digest(
+ 1,
+ JCAConstants.DIGEST_SHA1,
+ Hex.decodeHex("DEADBEEF"),
+ "Example:Block/Alg",
+ 80,
+ Base64Support.decode(kdf.getNonce()));
+
+ // The value in the original spec document Example 41 is incorrect, as indicated by the errata:
+ // https://www.w3.org/2008/xmlsec/errata/xmlenc-core-11-errata.html
+ // This is the correct value from the errata. (Yes, this wasted a lot of time...).
+ Assert.assertEquals(digestCounter1, Hex.decodeHex("59D9BA5E06072C1194091952B01B8360534AB11E"));
+
+ byte[] derived = kdf.deriveBytes(
+ Hex.decodeHex("DEADBEEF"),
+ "Example:Block/Alg",
+ 80);
+ Assert.assertEquals(derived.length * 8, 80);
+
+ // Note we can't really test an actual SecretKey derivation b/c
+ // "Example:Block/Alg" above is not a real algorithm and so can't resolve the JCA ID.
+ }
+
+ @Test
+ public void basic() throws Exception {
+ DHLegacyKDF kdf = new DHLegacyKDF();
+ kdf.setDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA256);
+ kdf.setNonce("Zm9v");
+
+ SecretKey secretKey = null;
+
+ secretKey = kdf.derive(Hex.decodeHex("DEADBEEF"), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128, null);
+ Assert.assertNotNull(secretKey);
+ Assert.assertEquals(secretKey.getEncoded().length * 8, 128);
+
+ secretKey = kdf.derive(Hex.decodeHex("DEADBEEF"), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128, 128);
+ Assert.assertNotNull(secretKey);
+ Assert.assertEquals(secretKey.getEncoded().length * 8, 128);
+
+ secretKey = kdf.derive(Hex.decodeHex("DEADBEEF"), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES192, null);
+ Assert.assertNotNull(secretKey);
+ Assert.assertEquals(secretKey.getEncoded().length * 8, 192);
+
+ secretKey = kdf.derive(Hex.decodeHex("DEADBEEF"), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES192, 192);
+ Assert.assertNotNull(secretKey);
+ Assert.assertEquals(secretKey.getEncoded().length * 8, 192);
+
+ secretKey = kdf.derive(Hex.decodeHex("DEADBEEF"), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256, null);
+ Assert.assertNotNull(secretKey);
+ Assert.assertEquals(secretKey.getEncoded().length * 8, 256);
+
+ secretKey = kdf.derive(Hex.decodeHex("DEADBEEF"), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256, 256);
+ Assert.assertNotNull(secretKey);
+ Assert.assertEquals(secretKey.getEncoded().length * 8, 256);
+ }
+
+ @Test
+ public void missingNonce() throws Exception {
+ DHLegacyKDF kdf = new DHLegacyKDF();
+ kdf.setDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA256);
+
+ SecretKey secretKey = null;
+
+ secretKey = kdf.derive(Hex.decodeHex("DEADBEEF"), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128, null);
+ Assert.assertNotNull(secretKey);
+ Assert.assertEquals(secretKey.getEncoded().length * 8, 128);
+ }
+
+ @Test(expectedExceptions=KeyDerivationException.class)
+ public void missingDigest() throws Exception {
+ DHLegacyKDF kdf = new DHLegacyKDF();
+ kdf.setNonce("Zm9v");
+
+ kdf.derive(Hex.decodeHex("DEADBEEF"), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128, null);
+ }
+
+ @Test(expectedExceptions=KeyDerivationException.class)
+ public void unknownKeyAlgorithm() throws Exception {
+ DHLegacyKDF kdf = new DHLegacyKDF();
+ kdf.setDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA256);
+ kdf.setNonce("Zm9v");
+
+ kdf.derive(Hex.decodeHex("DEADBEEF"), "urn:test:invalid", null);
+ }
+
+ @Test(expectedExceptions=KeyDerivationException.class)
+ public void unknownDigestAlgorithm() throws Exception {
+ DHLegacyKDF kdf = new DHLegacyKDF();
+ kdf.setDigestMethod("urn:test:invalid");
+ kdf.setNonce("Zm9v");
+
+ kdf.derive(Hex.decodeHex("DEADBEEF"), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128, 128);
+ }
+
+ @Test(expectedExceptions=KeyDerivationException.class)
+ public void keyLengthMismatch() throws Exception {
+ DHLegacyKDF kdf = new DHLegacyKDF();
+ kdf.setDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA256);
+ kdf.setNonce("Zm9v");
+
+ kdf.derive(Hex.decodeHex("DEADBEEF"), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128, 256);
+ }
+
+ @Test(expectedExceptions=KeyDerivationException.class)
+ public void invalidNonce() throws Exception {
+ DHLegacyKDF kdf = new DHLegacyKDF();
+ kdf.setDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA256);
+ kdf.setNonce("INVALID!!!!@@$$##");
+
+ kdf.derive(Hex.decodeHex("DEADBEEF"), EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128, 128);
+ }
+
+}
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/ECDHTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/DHWithExplicitKDFTest.java
similarity index 84%
copy from opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/ECDHTest.java
copy to opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/DHWithExplicitKDFTest.java
index f102eeafd..313cd007f 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/ECDHTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/DHWithExplicitKDFTest.java
@@ -20,7 +20,6 @@ package org.opensaml.xmlsec.encryption.support.tests;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.security.KeyPair;
-import java.security.spec.ECGenParameterSpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -35,6 +34,7 @@ import org.opensaml.core.xml.util.XMLObjectSupport;
import org.opensaml.security.credential.BasicCredential;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.credential.impl.CollectionCredentialResolver;
+import org.opensaml.security.crypto.JCAConstants;
import org.opensaml.security.crypto.KeySupport;
import org.opensaml.xmlsec.DecryptionConfiguration;
import org.opensaml.xmlsec.DecryptionParameters;
@@ -44,7 +44,9 @@ import org.opensaml.xmlsec.EncryptionParameters;
import org.opensaml.xmlsec.EncryptionParametersResolver;
import org.opensaml.xmlsec.criterion.DecryptionConfigurationCriterion;
import org.opensaml.xmlsec.criterion.EncryptionConfigurationCriterion;
+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.EncryptedData;
import org.opensaml.xmlsec.encryption.KeyDerivationMethod;
import org.opensaml.xmlsec.encryption.support.DataEncryptionParameters;
@@ -74,7 +76,7 @@ import net.shibboleth.utilities.java.support.xml.SerializeSupport;
/**
*
*/
-public class ECDHTest extends XMLObjectBaseTestCase {
+public class DHWithExplicitKDFTest extends XMLObjectBaseTestCase {
private String targetFile;
@@ -86,7 +88,7 @@ public class ECDHTest extends XMLObjectBaseTestCase {
private Encrypter encrypter;
private EncryptionParametersResolver encParamsResolver;
private CriteriaSet encCriteria;
- private BasicEncryptionConfiguration encConfig;
+ private BasicEncryptionConfiguration encConfig, encConfig2;
private DecryptionParametersResolver decryptParamsResolver;
private CriteriaSet decryptCriteria;
@@ -96,7 +98,7 @@ public class ECDHTest extends XMLObjectBaseTestCase {
public void beforeClass() throws Exception {
targetFile = "/org/opensaml/xmlsec/encryption/support/SimpleEncryptionTest.xml";
- KeyPair kp = KeySupport.generateKeyPair("EC", new ECGenParameterSpec("secp256r1"), null);
+ KeyPair kp = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN, 2048, null);
recipientCredPrivate = new BasicCredential(kp.getPublic(), kp.getPrivate());
recipientCredPublic = new BasicCredential(kp.getPublic());
@@ -115,9 +117,21 @@ public class ECDHTest extends XMLObjectBaseTestCase {
@BeforeMethod
public void beforeMethod() throws Exception {
encConfig = new BasicEncryptionConfiguration();
- encCriteria = new CriteriaSet(new EncryptionConfigurationCriterion(encConfig,
+ encConfig2 = new BasicEncryptionConfiguration();
+ encCriteria = new CriteriaSet(new EncryptionConfigurationCriterion(encConfig, encConfig2,
ConfigurationService.get(EncryptionConfiguration.class)));
+ // Configure the middle slot explicitly so that we aren't relying on whichever DH variant the library wide config has.
+ KeyAgreementEncryptionConfiguration kaConfig = new KeyAgreementEncryptionConfiguration();
+ kaConfig.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH_EXPLICIT_KDF);
+ ConcatKDF kdf = new ConcatKDF();
+ kdf.setAlgorithmID("00");
+ kdf.setPartyUInfo("00");
+ kdf.setPartyVInfo("00");
+ kdf.initialize();
+ kaConfig.setParameters(Set.of(kdf));
+ encConfig2.setKeyAgreementConfigurations(Map.of("DH", kaConfig));
+
decryptConfig = new BasicDecryptionConfiguration();
decryptConfig.setDataKeyInfoCredentialResolver(localKeyInfoResolver);
decryptConfig.setKEKKeyInfoCredentialResolver(localKeyInfoResolver);
@@ -165,7 +179,7 @@ public class ECDHTest extends XMLObjectBaseTestCase {
PBKDF2 kdf = new PBKDF2();
kdf.initialize();
kaConfig.setParameters(Set.of(kdf));
- encConfig.setKeyAgreementConfigurations(Map.of("EC", kaConfig));
+ encConfig.setKeyAgreementConfigurations(Map.of("DH", kaConfig));
testRoundtrip(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128, null, EncryptionConstants.ALGO_ID_KEYDERIVATION_PBKDF2);
}
@@ -198,15 +212,19 @@ public class ECDHTest extends XMLObjectBaseTestCase {
Assert.assertEquals(encryptedDataOrig.getKeyInfo().getEncryptedKeys().get(0).getEncryptionMethod().getAlgorithm(), expectedKEKAlgo);
}
+ AgreementMethod agreementMethod = null;
+ if (!encryptedDataOrig.getKeyInfo().getEncryptedKeys().isEmpty()) {
+ agreementMethod = encryptedDataOrig.getKeyInfo().getEncryptedKeys().get(0).getKeyInfo().getAgreementMethods().get(0);
+ } else {
+ agreementMethod = encryptedDataOrig.getKeyInfo().getAgreementMethods().get(0);
+ }
+ Assert.assertNotNull(agreementMethod);
+ Assert.assertEquals(agreementMethod.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH_EXPLICIT_KDF);
+
if (expectedKDFAlgo != null) {
- KeyDerivationMethod kdm = null;
- if (!encryptedDataOrig.getKeyInfo().getEncryptedKeys().isEmpty()) {
- kdm = (KeyDerivationMethod) encryptedDataOrig.getKeyInfo().getEncryptedKeys().get(0).getKeyInfo().getAgreementMethods().get(0).getUnknownXMLObjects(KeyDerivationMethod.DEFAULT_ELEMENT_NAME).get(0);
- } else {
- kdm = (KeyDerivationMethod) encryptedDataOrig.getKeyInfo().getAgreementMethods().get(0).getUnknownXMLObjects(KeyDerivationMethod.DEFAULT_ELEMENT_NAME).get(0);
- }
- Assert.assertNotNull(kdm);
- Assert.assertEquals(kdm.getAlgorithm(), expectedKDFAlgo);
+ KeyDerivationMethod kdm = (KeyDerivationMethod) agreementMethod.getUnknownXMLObjects(KeyDerivationMethod.DEFAULT_ELEMENT_NAME).get(0);
+ Assert.assertNotNull(kdm);
+ Assert.assertEquals(kdm.getAlgorithm(), expectedKDFAlgo);
}
// Serialize out and back in
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/ECDHTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/DHWithLegacyKDFTest.java
similarity index 74%
copy from opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/ECDHTest.java
copy to opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/DHWithLegacyKDFTest.java
index f102eeafd..9847576d6 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/ECDHTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/DHWithLegacyKDFTest.java
@@ -20,7 +20,6 @@ package org.opensaml.xmlsec.encryption.support.tests;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.security.KeyPair;
-import java.security.spec.ECGenParameterSpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -35,6 +34,7 @@ import org.opensaml.core.xml.util.XMLObjectSupport;
import org.opensaml.security.credential.BasicCredential;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.credential.impl.CollectionCredentialResolver;
+import org.opensaml.security.crypto.JCAConstants;
import org.opensaml.security.crypto.KeySupport;
import org.opensaml.xmlsec.DecryptionConfiguration;
import org.opensaml.xmlsec.DecryptionParameters;
@@ -42,11 +42,12 @@ import org.opensaml.xmlsec.DecryptionParametersResolver;
import org.opensaml.xmlsec.EncryptionConfiguration;
import org.opensaml.xmlsec.EncryptionParameters;
import org.opensaml.xmlsec.EncryptionParametersResolver;
+import org.opensaml.xmlsec.agreement.impl.DigestMethod;
+import org.opensaml.xmlsec.agreement.impl.KANonce;
import org.opensaml.xmlsec.criterion.DecryptionConfigurationCriterion;
import org.opensaml.xmlsec.criterion.EncryptionConfigurationCriterion;
-import org.opensaml.xmlsec.derivation.impl.PBKDF2;
+import org.opensaml.xmlsec.encryption.AgreementMethod;
import org.opensaml.xmlsec.encryption.EncryptedData;
-import org.opensaml.xmlsec.encryption.KeyDerivationMethod;
import org.opensaml.xmlsec.encryption.support.DataEncryptionParameters;
import org.opensaml.xmlsec.encryption.support.Decrypter;
import org.opensaml.xmlsec.encryption.support.Encrypter;
@@ -61,6 +62,7 @@ import org.opensaml.xmlsec.keyinfo.impl.KeyInfoProvider;
import org.opensaml.xmlsec.keyinfo.impl.LocalKeyInfoCredentialResolver;
import org.opensaml.xmlsec.keyinfo.impl.provider.AgreementMethodKeyInfoProvider;
import org.opensaml.xmlsec.mock.SignableSimpleXMLObject;
+import org.opensaml.xmlsec.signature.support.SignatureConstants;
import org.opensaml.xmlsec.testing.XMLSecurityTestingSupport;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
@@ -74,7 +76,7 @@ import net.shibboleth.utilities.java.support.xml.SerializeSupport;
/**
*
*/
-public class ECDHTest extends XMLObjectBaseTestCase {
+public class DHWithLegacyKDFTest extends XMLObjectBaseTestCase {
private String targetFile;
@@ -86,7 +88,7 @@ public class ECDHTest extends XMLObjectBaseTestCase {
private Encrypter encrypter;
private EncryptionParametersResolver encParamsResolver;
private CriteriaSet encCriteria;
- private BasicEncryptionConfiguration encConfig;
+ private BasicEncryptionConfiguration encConfig, encConfig2;
private DecryptionParametersResolver decryptParamsResolver;
private CriteriaSet decryptCriteria;
@@ -96,7 +98,7 @@ public class ECDHTest extends XMLObjectBaseTestCase {
public void beforeClass() throws Exception {
targetFile = "/org/opensaml/xmlsec/encryption/support/SimpleEncryptionTest.xml";
- KeyPair kp = KeySupport.generateKeyPair("EC", new ECGenParameterSpec("secp256r1"), null);
+ KeyPair kp = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN, 2048, null);
recipientCredPrivate = new BasicCredential(kp.getPublic(), kp.getPrivate());
recipientCredPublic = new BasicCredential(kp.getPublic());
@@ -115,9 +117,21 @@ public class ECDHTest extends XMLObjectBaseTestCase {
@BeforeMethod
public void beforeMethod() throws Exception {
encConfig = new BasicEncryptionConfiguration();
- encCriteria = new CriteriaSet(new EncryptionConfigurationCriterion(encConfig,
+ encConfig2 = new BasicEncryptionConfiguration();
+ encCriteria = new CriteriaSet(new EncryptionConfigurationCriterion(encConfig, encConfig2,
ConfigurationService.get(EncryptionConfiguration.class)));
+ // Configure the middle slot explicitly so that we aren't relying on whichever DH variant the library wide config has.
+ KeyAgreementEncryptionConfiguration kaConfig = new KeyAgreementEncryptionConfiguration();
+ kaConfig.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH);
+ DigestMethod dm = new DigestMethod();
+ dm.setAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA256);
+ dm.initialize();
+ KANonce nonce = new KANonce();
+ nonce.initialize();
+ kaConfig.setParameters(Set.of(dm, nonce));
+ encConfig2.setKeyAgreementConfigurations(Map.of("DH", kaConfig));
+
decryptConfig = new BasicDecryptionConfiguration();
decryptConfig.setDataKeyInfoCredentialResolver(localKeyInfoResolver);
decryptConfig.setKEKKeyInfoCredentialResolver(localKeyInfoResolver);
@@ -130,7 +144,7 @@ public class ECDHTest extends XMLObjectBaseTestCase {
public void roundtripDirectDataEncryption() throws Exception {
encConfig.setDataEncryptionCredentials(List.of(recipientCredPublic));
- testRoundtrip(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128, null);
+ testRoundtrip(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128, null, SignatureConstants.ALGO_ID_DIGEST_SHA256, true);
}
@Test
@@ -138,14 +152,14 @@ public class ECDHTest extends XMLObjectBaseTestCase {
encConfig.setDataEncryptionCredentials(List.of(recipientCredPublic));
encConfig.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM));
- testRoundtrip(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM, null);
+ testRoundtrip(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM, null, SignatureConstants.ALGO_ID_DIGEST_SHA256, true);
}
@Test
public void roundtripWithKeyWrap() throws Exception {
encConfig.setKeyTransportEncryptionCredentials(List.of(recipientCredPublic));
- testRoundtrip(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128, EncryptionConstants.ALGO_ID_KEYWRAP_AES128);
+ testRoundtrip(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128, EncryptionConstants.ALGO_ID_KEYWRAP_AES128, SignatureConstants.ALGO_ID_DIGEST_SHA256, true);
}
@Test
@@ -154,27 +168,23 @@ public class ECDHTest extends XMLObjectBaseTestCase {
encConfig.setKeyTransportEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_KEYWRAP_AES256));
encConfig.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM));
- testRoundtrip(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM, EncryptionConstants.ALGO_ID_KEYWRAP_AES256);
+ testRoundtrip(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM, EncryptionConstants.ALGO_ID_KEYWRAP_AES256, SignatureConstants.ALGO_ID_DIGEST_SHA256, true);
}
@Test
- public void roundtripWithPBKDF2() throws Exception {
+ public void roundtripWithSHA512AndNoNonce() throws Exception {
encConfig.setDataEncryptionCredentials(List.of(recipientCredPublic));
KeyAgreementEncryptionConfiguration kaConfig = new KeyAgreementEncryptionConfiguration();
- PBKDF2 kdf = new PBKDF2();
- kdf.initialize();
- kaConfig.setParameters(Set.of(kdf));
- encConfig.setKeyAgreementConfigurations(Map.of("EC", kaConfig));
+ DigestMethod dm = new DigestMethod();
+ dm.setAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA512);
+ kaConfig.setParameters(Set.of(dm));
+ encConfig.setKeyAgreementConfigurations(Map.of("DH", kaConfig));
- testRoundtrip(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128, null, EncryptionConstants.ALGO_ID_KEYDERIVATION_PBKDF2);
- }
-
- private void testRoundtrip(String expectedDataAlgo, String expectedKEKAlgo) throws Exception {
- testRoundtrip(expectedDataAlgo, expectedKEKAlgo, null);
+ testRoundtrip(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128, null, SignatureConstants.ALGO_ID_DIGEST_SHA512, false);
}
- private void testRoundtrip(String expectedDataAlgo, String expectedKEKAlgo, String expectedKDFAlgo) throws Exception {
+ private void testRoundtrip(String expectedDataAlgo, String expectedKEKAlgo, String expectedDigestMethod, boolean nonceExpected) throws Exception {
// Encrypt
SignableSimpleXMLObject sxoOrig = (SignableSimpleXMLObject) unmarshallElement(targetFile);
@@ -198,15 +208,29 @@ public class ECDHTest extends XMLObjectBaseTestCase {
Assert.assertEquals(encryptedDataOrig.getKeyInfo().getEncryptedKeys().get(0).getEncryptionMethod().getAlgorithm(), expectedKEKAlgo);
}
- if (expectedKDFAlgo != null) {
- KeyDerivationMethod kdm = null;
- if (!encryptedDataOrig.getKeyInfo().getEncryptedKeys().isEmpty()) {
- kdm = (KeyDerivationMethod) encryptedDataOrig.getKeyInfo().getEncryptedKeys().get(0).getKeyInfo().getAgreementMethods().get(0).getUnknownXMLObjects(KeyDerivationMethod.DEFAULT_ELEMENT_NAME).get(0);
- } else {
- kdm = (KeyDerivationMethod) encryptedDataOrig.getKeyInfo().getAgreementMethods().get(0).getUnknownXMLObjects(KeyDerivationMethod.DEFAULT_ELEMENT_NAME).get(0);
- }
- Assert.assertNotNull(kdm);
- Assert.assertEquals(kdm.getAlgorithm(), expectedKDFAlgo);
+ AgreementMethod agreementMethod = null;
+ if (!encryptedDataOrig.getKeyInfo().getEncryptedKeys().isEmpty()) {
+ agreementMethod = encryptedDataOrig.getKeyInfo().getEncryptedKeys().get(0).getKeyInfo().getAgreementMethods().get(0);
+ } else {
+ agreementMethod = encryptedDataOrig.getKeyInfo().getAgreementMethods().get(0);
+ }
+ Assert.assertNotNull(agreementMethod);
+ Assert.assertEquals(agreementMethod.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH);
+
+ if (expectedDigestMethod != null) {
+ org.opensaml.xmlsec.signature.DigestMethod digestMethod =
+ (org.opensaml.xmlsec.signature.DigestMethod) agreementMethod
+ .getUnknownXMLObjects(org.opensaml.xmlsec.signature.DigestMethod.DEFAULT_ELEMENT_NAME).get(0);
+ Assert.assertNotNull(digestMethod);
+ Assert.assertEquals(digestMethod.getAlgorithm(), expectedDigestMethod);
+ }
+
+ org.opensaml.xmlsec.encryption.KANonce nonce = agreementMethod.getKANonce();
+ if (nonceExpected) {
+ Assert.assertNotNull(nonce);
+ Assert.assertNotNull(nonce.getValue());
+ } else {
+ Assert.assertNull(nonce);
}
// Serialize out and back in
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/ECDHTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/ECDHTest.java
index f102eeafd..bff58a61e 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/ECDHTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/ECDHTest.java
@@ -45,6 +45,7 @@ import org.opensaml.xmlsec.EncryptionParametersResolver;
import org.opensaml.xmlsec.criterion.DecryptionConfigurationCriterion;
import org.opensaml.xmlsec.criterion.EncryptionConfigurationCriterion;
import org.opensaml.xmlsec.derivation.impl.PBKDF2;
+import org.opensaml.xmlsec.encryption.AgreementMethod;
import org.opensaml.xmlsec.encryption.EncryptedData;
import org.opensaml.xmlsec.encryption.KeyDerivationMethod;
import org.opensaml.xmlsec.encryption.support.DataEncryptionParameters;
@@ -198,15 +199,19 @@ public class ECDHTest extends XMLObjectBaseTestCase {
Assert.assertEquals(encryptedDataOrig.getKeyInfo().getEncryptedKeys().get(0).getEncryptionMethod().getAlgorithm(), expectedKEKAlgo);
}
+ AgreementMethod agreementMethod = null;
+ if (!encryptedDataOrig.getKeyInfo().getEncryptedKeys().isEmpty()) {
+ agreementMethod = encryptedDataOrig.getKeyInfo().getEncryptedKeys().get(0).getKeyInfo().getAgreementMethods().get(0);
+ } else {
+ agreementMethod = encryptedDataOrig.getKeyInfo().getAgreementMethods().get(0);
+ }
+ Assert.assertNotNull(agreementMethod);
+ Assert.assertEquals(agreementMethod.getAlgorithm(), EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+
if (expectedKDFAlgo != null) {
- KeyDerivationMethod kdm = null;
- if (!encryptedDataOrig.getKeyInfo().getEncryptedKeys().isEmpty()) {
- kdm = (KeyDerivationMethod) encryptedDataOrig.getKeyInfo().getEncryptedKeys().get(0).getKeyInfo().getAgreementMethods().get(0).getUnknownXMLObjects(KeyDerivationMethod.DEFAULT_ELEMENT_NAME).get(0);
- } else {
- kdm = (KeyDerivationMethod) encryptedDataOrig.getKeyInfo().getAgreementMethods().get(0).getUnknownXMLObjects(KeyDerivationMethod.DEFAULT_ELEMENT_NAME).get(0);
- }
- Assert.assertNotNull(kdm);
- Assert.assertEquals(kdm.getAlgorithm(), expectedKDFAlgo);
+ KeyDerivationMethod kdm = (KeyDerivationMethod) agreementMethod.getUnknownXMLObjects(KeyDerivationMethod.DEFAULT_ELEMENT_NAME).get(0);
+ Assert.assertNotNull(kdm);
+ Assert.assertEquals(kdm.getAlgorithm(), expectedKDFAlgo);
}
// Serialize out and back in
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
index 89a744fb5..e1bb82a3a 100644
--- 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
@@ -32,6 +32,8 @@ 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.agreement.impl.DigestMethod;
+import org.opensaml.xmlsec.agreement.impl.KANonce;
import org.opensaml.xmlsec.derivation.impl.ConcatKDF;
import org.opensaml.xmlsec.derivation.impl.PBKDF2;
import org.opensaml.xmlsec.encryption.AgreementMethod;
@@ -56,12 +58,16 @@ import org.testng.annotations.Test;
public class KeyAgreementKeyInfoGeneratorTest extends XMLObjectBaseTestCase {
private KeyPair keyPairOriginatorECDH, keyPairRecipientECDH;
+ private KeyPair keyPairOriginatorDiffieHellman, keyPairRecipientDiffieHellman;
private Credential credOriginatorECDH, credRecipientECDH;
+ private Credential credOriginatorDiffieHellman, credRecipientDiffieHellman;
private SecretKey derivedKey;
private KeyAgreementCredential credECDH;
+ private KeyAgreementCredential credDiffieHellmanExplicitKDF;
+ private KeyAgreementCredential credDiffieHellmanLegacyKDF;
private KeyAgreementKeyInfoGeneratorFactory factory;
@@ -69,10 +75,14 @@ public class KeyAgreementKeyInfoGeneratorTest extends XMLObjectBaseTestCase {
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());
+ keyPairOriginatorDiffieHellman = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN, 1024, null);
+ credOriginatorDiffieHellman = new BasicCredential(keyPairOriginatorDiffieHellman.getPublic(), keyPairOriginatorDiffieHellman.getPrivate());
+ keyPairRecipientDiffieHellman = KeySupport.generateKeyPair(JCAConstants.KEY_ALGO_DIFFIE_HELLMAN, 1024, null);
+ credRecipientDiffieHellman = new BasicCredential(keyPairRecipientDiffieHellman.getPublic());
+
derivedKey = KeySupport.generateKey(JCAConstants.KEY_ALGO_AES, 256, null);
}
@@ -81,11 +91,12 @@ public class KeyAgreementKeyInfoGeneratorTest extends XMLObjectBaseTestCase {
factory = new KeyAgreementKeyInfoGeneratorFactory();
credECDH = new BasicKeyAgreementCredential(derivedKey, EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES, credOriginatorECDH, credRecipientECDH);
+ credDiffieHellmanExplicitKDF = new BasicKeyAgreementCredential(derivedKey, EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH_EXPLICIT_KDF, credOriginatorDiffieHellman, credRecipientDiffieHellman);
+ credDiffieHellmanLegacyKDF = new BasicKeyAgreementCredential(derivedKey, EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH, credOriginatorDiffieHellman, credRecipientDiffieHellman);
}
-
@Test
- void ECDHWithConcatKDFWithDefaults() throws Exception {
+ public void ECDHWithConcatKDFWithDefaults() throws Exception {
ConcatKDF kdf = new ConcatKDF();
kdf.setDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA512);
kdf.setAlgorithmID("AA");
@@ -106,6 +117,8 @@ public class KeyAgreementKeyInfoGeneratorTest extends XMLObjectBaseTestCase {
Assert.assertEquals(keyInfo.getAgreementMethods().size(), 1);
AgreementMethod agreementMethod = keyInfo.getAgreementMethods().get(0);
+ Assert.assertEquals(agreementMethod.getAlgorithm(), credECDH.getAlgorithm());
+
Assert.assertEquals(agreementMethod.getOrderedChildren().size(), 3);
//Originator
@@ -143,7 +156,7 @@ public class KeyAgreementKeyInfoGeneratorTest extends XMLObjectBaseTestCase {
}
@Test
- void ECDHWithPBKDF2WithDefaults() throws Exception {
+ public void ECDHWithPBKDF2WithDefaults() throws Exception {
PBKDF2 kdf = new PBKDF2();
kdf.setIterationCount(1500);
kdf.setKeyLength(256);
@@ -162,6 +175,8 @@ public class KeyAgreementKeyInfoGeneratorTest extends XMLObjectBaseTestCase {
Assert.assertEquals(keyInfo.getAgreementMethods().size(), 1);
AgreementMethod agreementMethod = keyInfo.getAgreementMethods().get(0);
+ Assert.assertEquals(agreementMethod.getAlgorithm(), credECDH.getAlgorithm());
+
Assert.assertEquals(agreementMethod.getOrderedChildren().size(), 3);
//Originator
@@ -200,6 +215,121 @@ public class KeyAgreementKeyInfoGeneratorTest extends XMLObjectBaseTestCase {
Assert.assertEquals(kdfParams.getSalt().getSpecified().getValue(), "ABCD");
}
+ @Test
+ public void DiffieHellmanWithConcatKDFWithDefaults() 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();
+
+ credDiffieHellmanExplicitKDF.getParameters().add(kdf);
+
+ KeyInfoGenerator generator = factory.newInstance();
+ KeyInfo keyInfo = generator.generate(credDiffieHellmanExplicitKDF);
+
+ 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.getAlgorithm(), credDiffieHellmanExplicitKDF.getAlgorithm());
+
+ Assert.assertEquals(agreementMethod.getOrderedChildren().size(), 3);
+
+ //Originator
+ Assert.assertNotNull(agreementMethod.getOriginatorKeyInfo());
+ OriginatorKeyInfo originatorKeyInfo = agreementMethod.getOriginatorKeyInfo();
+ Assert.assertEquals(originatorKeyInfo.getOrderedChildren().size(), 2);
+ Assert.assertEquals(originatorKeyInfo.getDEREncodedKeyValues().size(), 1);
+ Assert.assertEquals(KeyInfoSupport.getKey(originatorKeyInfo.getDEREncodedKeyValues().get(0)), keyPairOriginatorDiffieHellman.getPublic());
+ Assert.assertEquals(originatorKeyInfo.getKeyValues().size(), 1);
+ Assert.assertEquals(KeyInfoSupport.getKey(originatorKeyInfo.getKeyValues().get(0)), keyPairOriginatorDiffieHellman.getPublic());
+
+ //Recipient
+ Assert.assertNotNull(agreementMethod.getRecipientKeyInfo());
+ RecipientKeyInfo recipientKeyInfo = agreementMethod.getRecipientKeyInfo();
+ Assert.assertEquals(recipientKeyInfo.getOrderedChildren().size(), 2);
+ Assert.assertEquals(recipientKeyInfo.getDEREncodedKeyValues().size(), 1);
+ Assert.assertEquals(KeyInfoSupport.getKey(recipientKeyInfo.getDEREncodedKeyValues().get(0)), keyPairRecipientDiffieHellman.getPublic());
+ Assert.assertEquals(recipientKeyInfo.getKeyValues().size(), 1);
+ Assert.assertEquals(KeyInfoSupport.getKey(recipientKeyInfo.getKeyValues().get(0)), keyPairRecipientDiffieHellman.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
+ public void DiffieHellmanWithLegacyKDFWithDefaults() throws Exception {
+ DigestMethod dm = new DigestMethod();
+ dm.setAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA512);
+ dm.initialize();
+
+ KANonce nonce = new KANonce();
+ nonce.setValue("ABCD");
+ nonce.initialize();
+
+ credDiffieHellmanLegacyKDF.getParameters().add(dm);
+ credDiffieHellmanLegacyKDF.getParameters().add(nonce);
+
+ KeyInfoGenerator generator = factory.newInstance();
+ KeyInfo keyInfo = generator.generate(credDiffieHellmanLegacyKDF);
+
+ 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.getAlgorithm(), credDiffieHellmanLegacyKDF.getAlgorithm());
+
+ Assert.assertEquals(agreementMethod.getOrderedChildren().size(), 4);
+
+ //Originator
+ Assert.assertNotNull(agreementMethod.getOriginatorKeyInfo());
+ OriginatorKeyInfo originatorKeyInfo = agreementMethod.getOriginatorKeyInfo();
+ Assert.assertEquals(originatorKeyInfo.getOrderedChildren().size(), 2);
+ Assert.assertEquals(originatorKeyInfo.getDEREncodedKeyValues().size(), 1);
+ Assert.assertEquals(KeyInfoSupport.getKey(originatorKeyInfo.getDEREncodedKeyValues().get(0)), keyPairOriginatorDiffieHellman.getPublic());
+ Assert.assertEquals(originatorKeyInfo.getKeyValues().size(), 1);
+ Assert.assertEquals(KeyInfoSupport.getKey(originatorKeyInfo.getKeyValues().get(0)), keyPairOriginatorDiffieHellman.getPublic());
+
+ //Recipient
+ Assert.assertNotNull(agreementMethod.getRecipientKeyInfo());
+ RecipientKeyInfo recipientKeyInfo = agreementMethod.getRecipientKeyInfo();
+ Assert.assertEquals(recipientKeyInfo.getOrderedChildren().size(), 2);
+ Assert.assertEquals(recipientKeyInfo.getDEREncodedKeyValues().size(), 1);
+ Assert.assertEquals(KeyInfoSupport.getKey(recipientKeyInfo.getDEREncodedKeyValues().get(0)), keyPairRecipientDiffieHellman.getPublic());
+ Assert.assertEquals(recipientKeyInfo.getKeyValues().size(), 1);
+ Assert.assertEquals(KeyInfoSupport.getKey(recipientKeyInfo.getKeyValues().get(0)), keyPairRecipientDiffieHellman.getPublic());
+
+ //Params
+ Assert.assertNotNull(agreementMethod.getKANonce());
+ Assert.assertEquals(agreementMethod.getKANonce().getValue(), "ABCD");
+ Assert.assertEquals(agreementMethod.getUnknownXMLObjects(org.opensaml.xmlsec.signature.DigestMethod.DEFAULT_ELEMENT_NAME).size(), 1);
+ org.opensaml.xmlsec.signature.DigestMethod xmlDigest =
+ (org.opensaml.xmlsec.signature.DigestMethod) agreementMethod.getUnknownXMLObjects(
+ org.opensaml.xmlsec.signature.DigestMethod.DEFAULT_ELEMENT_NAME).get(0);
+ Assert.assertEquals(xmlDigest.getAlgorithm(), SignatureConstants.ALGO_ID_DIGEST_SHA512);
+ }
+
+
@Test
public void noEmitKeyinfos() throws Exception {
factory.setEmitOriginatorKeyInfo(false);
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/tests/KeyInfoSupportTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/tests/KeyInfoSupportTest.java
index ced4a8f5a..440344917 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/tests/KeyInfoSupportTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/keyinfo/tests/KeyInfoSupportTest.java
@@ -39,6 +39,7 @@ import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.List;
+import javax.crypto.interfaces.DHPublicKey;
import javax.security.auth.x500.X500Principal;
import net.shibboleth.utilities.java.support.codec.Base64Support;
@@ -49,9 +50,13 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.opensaml.core.testing.XMLObjectBaseTestCase;
import org.opensaml.security.SecurityException;
import org.opensaml.security.crypto.KeySupport;
+import org.opensaml.security.crypto.dh.DHSupport;
import org.opensaml.security.crypto.ec.ECSupport;
import org.opensaml.security.crypto.ec.EnhancedECParameterSpec;
import org.opensaml.security.x509.X509Support;
+import org.opensaml.xmlsec.encryption.DHKeyValue;
+import org.opensaml.xmlsec.encryption.Generator;
+import org.opensaml.xmlsec.encryption.Public;
import org.opensaml.xmlsec.keyinfo.KeyInfoSupport;
import org.opensaml.xmlsec.signature.DEREncodedKeyValue;
import org.opensaml.xmlsec.signature.DSAKeyValue;
@@ -189,6 +194,14 @@ public class KeyInfoSupportTest extends XMLObjectBaseTestCase {
+ "BkhuSKX/2PbljnmIdGV7mJK9/XUHnyKgZBxXEul2mlvGkrgUvyv+qYsCFsKSSrkB"
+ "1Mj2Ql5xmTMaePMEmvOr6fDAP0OH8cvADEZjx0s/5vvoBFPGGmPrHJluEVS0Fu8I" + "9sROg9YjyuhRV0b8xHo=";
+ /** Test DH key 1. */
+ private final String dhPubKey1 = "MIIBJDCBmQYJKoZIhvcNAQMBMIGLAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0"
+ + "Agu+pjsTmyJRSgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL"
+ + "/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZTgf//////////AgECAgICAAOBhQACgYEAwICZ"
+ + "ws/L/QcxdYfg9AU/a0y3jEkgn6FaD0eaUTiWcXjpqEeVjPgqEeGnhffxI7z0B5n/ZSNB8bLVjrKe"
+ + "srlS9Opop6HBKW9yuC9bMisN69n0eZn1SJoM3CpX5eBuVx3pOca2vf4T3J1naVpgvDTyhaaZ4rqH"
+ + "3WC34FMOvm3rJio=";
+
/** Test EC key with named curve variant 1, curve: secp256r1, OID: 1.2.840.10045.3.1.7 */
private final String ecPubKey_NamedCurve1 = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBM0jGYrvVMpbVTT728+RfDLL0tPg"
+ "swfUSUXfrXKwAGOmrSbF1KHsErZdXhnEC1VSmm9kTd8VzIi4OihEVMoU+w==";
@@ -220,6 +233,8 @@ public class KeyInfoSupportTest extends XMLObjectBaseTestCase {
private DSAKeyValue xmlDSAKeyValue1, xmlDSAKeyValue1NoParams;
+ private DHKeyValue xmlDHKeyValue1;
+
private RSAKeyValue xmlRSAKeyValue1;
private ECKeyValue xmlECKeyValue_NamedCurve1, xmlECKeyValue_ExplicitParams1;
@@ -236,6 +251,8 @@ public class KeyInfoSupportTest extends XMLObjectBaseTestCase {
private DSAPublicKey javaDSAPubKey1;
+ private DHPublicKey javaDHPubKey1;
+
private ECPublicKey javaECPubKey_NamedCurve1, javaECPubKey_ExplicitParams1;
private DSAParams javaDSAParams1;
@@ -277,6 +294,7 @@ public class KeyInfoSupportTest extends XMLObjectBaseTestCase {
javaCRL1 = X509Support.decodeCRL(crl1);
javaDSAPubKey1 = KeySupport.buildJavaDSAPublicKey(dsaPubKey1);
+ javaDHPubKey1 = KeySupport.buildJavaDHPublicKey(dhPubKey1);
javaRSAPubKey1 = KeySupport.buildJavaRSAPublicKey(rsaPubKey1);
javaECPubKey_NamedCurve1 = KeySupport.buildJavaECPublicKey(ecPubKey_NamedCurve1);
// SunEC provider doesn't support explicit params, so use a custom method.
@@ -290,6 +308,22 @@ public class KeyInfoSupportTest extends XMLObjectBaseTestCase {
xmlRSAKeyValue1.setModulus(modulus);
xmlRSAKeyValue1.setExponent(exponent);
+ xmlDHKeyValue1 = (DHKeyValue) buildXMLObject(DHKeyValue.DEFAULT_ELEMENT_NAME);
+ org.opensaml.xmlsec.encryption.P dhP =
+ (org.opensaml.xmlsec.encryption.P) buildXMLObject(org.opensaml.xmlsec.encryption.P.DEFAULT_ELEMENT_NAME);
+ org.opensaml.xmlsec.encryption.Q dhQ =
+ (org.opensaml.xmlsec.encryption.Q) buildXMLObject(org.opensaml.xmlsec.encryption.Q.DEFAULT_ELEMENT_NAME);
+ Generator gen = (Generator) buildXMLObject(Generator.DEFAULT_ELEMENT_NAME);
+ Public pub = (Public) buildXMLObject(Public.DEFAULT_ELEMENT_NAME);
+ dhP.setValueBigInt(javaDHPubKey1.getParams().getP());
+ dhQ.setValueBigInt(DHSupport.getPrimeQDomainParameter(javaDHPubKey1));
+ gen.setValueBigInt(javaDHPubKey1.getParams().getG());
+ pub.setValueBigInt(javaDHPubKey1.getY());
+ xmlDHKeyValue1.setP(dhP);
+ xmlDHKeyValue1.setQ(dhQ);
+ xmlDHKeyValue1.setGenerator(gen);
+ xmlDHKeyValue1.setPublic(pub);
+
xmlDSAKeyValue1 = (DSAKeyValue) buildXMLObject(DSAKeyValue.DEFAULT_ELEMENT_NAME);
P p = (P) buildXMLObject(P.DEFAULT_ELEMENT_NAME);
Q q = (Q) buildXMLObject(Q.DEFAULT_ELEMENT_NAME);
@@ -457,7 +491,7 @@ public class KeyInfoSupportTest extends XMLObjectBaseTestCase {
Assert.assertEquals(X509Support.decodeCRL(xmlCRL.getValue()), javaCRL1,
"Java X509CRL encoding to XMLObject failed");
}
-
+
/** Test conversion of DSA public keys from XML to Java security native type. */
@Test
public void testDSAConversionXMLToJava() {
@@ -490,6 +524,22 @@ public class KeyInfoSupportTest extends XMLObjectBaseTestCase {
}
}
+ /** Test conversion of DH public keys from XML to Java security native type. */
+ @Test
+ public void testDHConversionXMLToJava() {
+ PublicKey key = null;
+ DHPublicKey dhKey = null;
+
+ try {
+ key = KeyInfoSupport.getDHKey(xmlDHKeyValue1);
+ } catch (KeyException e) {
+ Assert.fail("DH key conversion XML to Java failed: " + e);
+ }
+ dhKey = (DHPublicKey) key;
+ Assert.assertNotNull(dhKey, "Generated key was not an instance of DHPublicKey");
+ Assert.assertEquals(dhKey, javaDHPubKey1, "Generated key was not the expected value");
+ }
+
/** Test conversion of RSA public keys from XML to Java security native type. */
@Test
public void testRSAConversionXMLToJava() {
@@ -545,6 +595,22 @@ public class KeyInfoSupportTest extends XMLObjectBaseTestCase {
}
+ /** Test conversion of DH public keys from Java security native type to XML.
+ * @throws EncodingException on base64 encoding error*/
+ @Test
+ public void testDHConversionJavaToXML() throws EncodingException {
+ DHKeyValue dhKeyValue = KeyInfoSupport.buildDHKeyValue(javaDHPubKey1);
+ Assert.assertNotNull(dhKeyValue);
+ Assert.assertEquals(dhKeyValue
+ .getPublic().getValueBigInt(), javaDHPubKey1.getY(), "Generated DHKeyValue Public component was not the expected value");
+ Assert.assertEquals(dhKeyValue.getP().getValueBigInt(), javaDHPubKey1.getParams().getP(),
+ "Generated DHKeyValue P component was not the expected value");
+ Assert.assertEquals(dhKeyValue.getGenerator().getValueBigInt(), javaDHPubKey1.getParams().getG(),
+ "Generated DHKeyValue Generator component was not the expected value");
+ Assert.assertEquals(dhKeyValue.getQ().getValueBigInt(), DHSupport.getPrimeQDomainParameter(javaDHPubKey1),
+ "Generated DHKeyValue Q component was not the expected value");
+ }
+
/** Test conversion of DSA public keys from Java security native type to XML.
* @throws EncodingException on base64 encoding error*/
@Test
@@ -601,6 +667,26 @@ public class KeyInfoSupportTest extends XMLObjectBaseTestCase {
javaECPubKey_ExplicitParams1.getW());
}
+ /** Tests extracting a DH public key from a KeyValue. */
+ @Test
+ public void testGetDHKey() {
+ keyValue.setRSAKeyValue(null);
+ keyValue.setDHKeyValue(xmlDHKeyValue1);
+
+ PublicKey pk = null;
+ DHPublicKey dhKey = null;
+ try {
+ pk = KeyInfoSupport.getKey(keyValue);
+ } catch (KeyException e) {
+ Assert.fail("Extraction of key from KeyValue failed: " + e);
+ }
+ Assert.assertTrue(pk instanceof DHPublicKey, "Generated key was not an instance of DHPublicKey");
+ dhKey = (DHPublicKey) pk;
+ Assert.assertEquals(dhKey, javaDHPubKey1, "Generated key was not the expected value");
+
+ keyValue.setDSAKeyValue(null);
+ }
+
/** Tests extracting a DSA public key from a KeyValue. */
@Test
public void testGetDSAKey() {
@@ -665,6 +751,30 @@ public class KeyInfoSupportTest extends XMLObjectBaseTestCase {
keyInfo.getKeyValues().clear();
}
+ /** Tests adding a public key as a KeyValue to KeyInfo.
+ * @throws EncodingException on base64 encoding error*/
+ @Test
+ public void testAddDHPublicKey() throws EncodingException {
+ keyInfo.getKeyValues().clear();
+
+ KeyInfoSupport.addPublicKey(keyInfo, javaDHPubKey1);
+ KeyValue kv = keyInfo.getKeyValues().get(0);
+ Assert.assertNotNull(kv, "KeyValue was null");
+ DHKeyValue dhKeyValue = kv.getDHKeyValue();
+ Assert.assertNotNull(dhKeyValue, "DHKeyValue was null");
+
+ DHPublicKey javaKey = null;
+ try {
+ javaKey = (DHPublicKey) KeyInfoSupport.getDHKey(dhKeyValue);
+ } catch (KeyException e) {
+ Assert.fail("Extraction of Java key failed: " + e);
+ }
+
+ Assert.assertEquals(javaKey, javaDHPubKey1, "Inserted DH public key was not the expected value");
+
+ keyInfo.getKeyValues().clear();
+ }
+
/** Tests adding a public key as a KeyValue to KeyInfo.
* @throws EncodingException on base64 encoding error*/
@Test
@@ -765,7 +875,7 @@ public class KeyInfoSupportTest extends XMLObjectBaseTestCase {
Assert.fail("Extraction of Java key failed: " + e);
}
- Assert.assertEquals(javaDSAPubKey1, javaKey, "Inserted RSA public key was not the expected value");
+ Assert.assertEquals(javaDSAPubKey1, javaKey, "Inserted DSA public key was not the expected value");
keyInfo.getDEREncodedKeyValues().clear();
}
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/testing/XMLSecurityTestingSupport.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/testing/XMLSecurityTestingSupport.java
index 1ae4500a7..f4ffcfe40 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/testing/XMLSecurityTestingSupport.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/testing/XMLSecurityTestingSupport.java
@@ -23,6 +23,7 @@ import java.util.List;
import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
import org.opensaml.xmlsec.keyinfo.impl.BasicProviderKeyInfoCredentialResolver;
import org.opensaml.xmlsec.keyinfo.impl.KeyInfoProvider;
+import org.opensaml.xmlsec.keyinfo.impl.provider.DEREncodedKeyValueProvider;
import org.opensaml.xmlsec.keyinfo.impl.provider.DSAKeyValueProvider;
import org.opensaml.xmlsec.keyinfo.impl.provider.ECKeyValueProvider;
import org.opensaml.xmlsec.keyinfo.impl.provider.InlineX509DataProvider;
@@ -50,6 +51,7 @@ public final class XMLSecurityTestingSupport {
providers.add( new RSAKeyValueProvider() );
providers.add( new DSAKeyValueProvider() );
providers.add( new ECKeyValueProvider() );
+ providers.add( new DEREncodedKeyValueProvider() );
providers.add( new InlineX509DataProvider() );
return providers;
}
diff --git a/opensaml-xmlsec-impl/src/test/resources/logback-test.xml b/opensaml-xmlsec-impl/src/test/resources/logback-test.xml
index b031592ce..897e843c9 100644
--- a/opensaml-xmlsec-impl/src/test/resources/logback-test.xml
+++ b/opensaml-xmlsec-impl/src/test/resources/logback-test.xml
@@ -8,6 +8,10 @@
</encoder>
</appender>
+ <logger name="org.opensaml.xmlsec.derivation.impl.DHLegacyKDF">
+ <level value="TRACE"/>
+ </logger>
+
<logger name="org.apache.xml.security.encryption.XMLCipher">
<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