[java-opensaml] 13/13: KeyDerivation API must account for algorithm URIs without key length.

Brent Putman putmanb at georgetown.edu
Wed Jan 6 01:27:27 UTC 2021


This is an automated email from the git hooks/post-receive script.

putmanb pushed a commit to branch dev/OSJ-82
in repository java-opensaml.

View the commit online:
http://git.shibboleth.net/view/?p=java-opensaml.git;a=commit;h=0b6c403a234765ea0e31752c30704b77589906ff

commit 0b6c403a234765ea0e31752c30704b77589906ff
Author: Brent Putman <putmanb at georgetown.edu>
AuthorDate: Tue Jan 5 18:16:42 2021 -0500

    KeyDerivation API must account for algorithm URIs without key length.
---
 .../xmlsec/agreement/KeyAgreementSupport.java      | 29 ++++++-
 .../opensaml/xmlsec/derivation/KeyDerivation.java  | 12 ++-
 .../xmlsec/derivation/KeyDerivationSupport.java    | 92 ++++++++++++++++++++++
 .../derivation/KeyDerivationSupportTest.java       | 85 ++++++++++++++++++++
 .../AbstractDerivationKeyAgreementProcessor.java   |  4 +-
 .../impl/KeyAgreementParametersParser.java         |  7 ++
 .../opensaml/xmlsec/agreement/impl/KeySize.java    | 35 +++++---
 .../opensaml/xmlsec/derivation/impl/ConcatKDF.java | 15 ++--
 .../opensaml/xmlsec/derivation/impl/PBKDF2.java    | 24 +++---
 .../provider/AgreementMethodKeyInfoProvider.java   | 11 ++-
 .../impl/KeyAgreementParametersParserTest.java     | 60 ++++++++++++++
 .../xmlsec/derivation/impl/ConcatKDFTest.java      | 24 +++++-
 .../xmlsec/derivation/impl/MockKeyDerivation.java  |  2 +-
 .../xmlsec/derivation/impl/PBKDF2Test.java         | 28 +++++--
 14 files changed, 372 insertions(+), 56 deletions(-)

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 39a4c4a17..241ac63e6 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementSupport.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementSupport.java
@@ -17,9 +17,14 @@
 
 package org.opensaml.xmlsec.agreement;
 
+import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 import org.opensaml.core.config.ConfigurationService;
+import org.opensaml.xmlsec.encryption.AgreementMethod;
+import org.opensaml.xmlsec.encryption.EncryptedType;
+import org.opensaml.xmlsec.encryption.EncryptionMethod;
+import org.opensaml.xmlsec.encryption.KeySize;
 
 /**
  * Support for key agreement operations.
@@ -33,9 +38,31 @@ public final class KeyAgreementSupport {
     /**
      * Get the global {@link KeyAgreementProcessorRegistry} instance.
      * 
-     * @return the global procesor registry, or null if nothing registered
+     * @return the global processor registry, or null if nothing registered
      */
     @Nullable public static KeyAgreementProcessorRegistry getGlobalProcessorRegistry() {
         return ConfigurationService.get(KeyAgreementProcessorRegistry.class);
     }
+    
+    /**
+     * Look for an explicit key size via an {@link AgreementMethod}'s grandparent's {@link EncryptionMethod}
+     * child's {@link KeySize} child element.
+     * 
+     * @param agreementMethod the AgreementMethod to process
+     * 
+     * @return the key size, or null if not present
+     */
+    @Nullable public static Integer getExplicitKeySize(@Nonnull final AgreementMethod agreementMethod) {
+        if (agreementMethod.getParent() == null || agreementMethod.getParent().getParent() == null
+                || ! EncryptedType.class.isInstance(agreementMethod.getParent().getParent())) {
+            return null;
+        }
+        
+        final EncryptedType et = EncryptedType.class.cast(agreementMethod.getParent().getParent());
+        if (et.getEncryptionMethod() == null || et.getEncryptionMethod().getKeySize() == null) {
+            return null;
+        }
+        
+        return et.getEncryptionMethod().getKeySize().getValue();
+    }
 }
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/derivation/KeyDerivation.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/derivation/KeyDerivation.java
index 411e2abda..a5063dd1f 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/derivation/KeyDerivation.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/derivation/KeyDerivation.java
@@ -17,9 +17,12 @@
 
 package org.opensaml.xmlsec.derivation;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import javax.crypto.SecretKey;
 
 import org.opensaml.xmlsec.agreement.KeyAgreementParameter;
+import org.opensaml.xmlsec.algorithm.KeyLengthSpecifiedAlgorithm;
 
 /**
  * Component which represents a specific key derivation algorithm, and supports deriving a new {@link SecretKey}
@@ -36,18 +39,23 @@ public interface KeyDerivation extends KeyAgreementParameter {
      * 
      * @return the algorithm
      */
-    public String getAlgorithm();
+    @Nonnull public String getAlgorithm();
 
     /**
      * Derive a {@link SecretKey} from the specified secret.
      * 
      * @param secret the input secret from which to derive the key.
      * @param keyAlgorithm the algorithm URI for which the derived key will be used
+     * @param keyLength the length of the derived key.  This may be null if the keyAlgorithm URI
+     *                  implies a key length, for example if the URI represents a {@link KeyLengthSpecifiedAlgorithm}.
+     *                  However if the URI implies a key length and this parameter value does not match that length,
+     *                  that is an error and and exception will be thrown
      * 
      * @return the derived key
      * 
      * @throws KeyDerivationException
      */
-    public SecretKey derive(byte[] secret, String keyAlgorithm) throws KeyDerivationException;
+    @Nonnull public SecretKey derive(@Nonnull final byte[] secret, @Nonnull final String keyAlgorithm,
+            @Nullable final Integer keyLength) throws KeyDerivationException;
     
 }
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/derivation/KeyDerivationSupport.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/derivation/KeyDerivationSupport.java
new file mode 100644
index 000000000..e19165156
--- /dev/null
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/derivation/KeyDerivationSupport.java
@@ -0,0 +1,92 @@
+/*
+ * 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;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Support key derivation operations.
+ */
+public final class KeyDerivationSupport {
+    
+    /** Constructor. */
+    private KeyDerivationSupport() { }
+
+    /**
+     * Get the JCA key algorithm which corresponds to the specified algorithm URI.
+     * 
+     * @param algorithmURI the algorithm URI with which the derived key will be user
+     * 
+     * @return the JCA key algorithm
+     * 
+     * @throws KeyDerivationException
+     */
+    @Nonnull public static String getJCAKeyAlgorithm(@Nonnull final String algorithmURI)
+            throws KeyDerivationException {
+        Constraint.isNotNull(algorithmURI, "Algorithm URI was null");
+        
+        final String jcaKeyAlgorithm = AlgorithmSupport.getKeyAlgorithm(algorithmURI);
+        if (jcaKeyAlgorithm == null) {
+            throw new KeyDerivationException("Could not determine JCA key algorithm from URI: " + algorithmURI);
+        }
+        return jcaKeyAlgorithm;
+    }
+    
+    /**
+     * Get the effective key length based on the specified algorithm URI and the specified key length, if present.
+     * 
+     * <p>
+     * If the algorithm URI implies a key length and the specified key length is non-null, the lengths must
+     * match or an exception will be thrown.  If the algorithm URI does not imply a key length and the specified 
+     * length is null, and exception will be thrown.
+     * </p>
+     * 
+     * @param algorithmURI the algorithm URI with which the derived key will be used
+     * @param specifiedKeyLength an explicitly specified key length
+     * 
+     * @return the effective key length
+     * 
+     * @throws KeyDerivationException
+     */
+    @Nonnull public static Integer getEffectiveKeyLength(@Nonnull final String algorithmURI,
+            @Nullable final Integer specifiedKeyLength) throws KeyDerivationException {
+        Constraint.isNotNull(algorithmURI, "Algorithm URI was null");
+        
+        final Integer algoKeyLength = AlgorithmSupport.getKeyLength(algorithmURI);
+        if (algoKeyLength == null) {
+            if (specifiedKeyLength == null) {
+                throw new KeyDerivationException(String.format("Could not determine algorithm key length from URI '%s'"
+                        + "and no length was specified", algorithmURI));
+            }
+            return specifiedKeyLength;
+        } 
+        
+        if (specifiedKeyLength != null && ! specifiedKeyLength.equals(algoKeyLength)) {
+            throw new KeyDerivationException(String.format("Algorithm URI '%s' key length (%d) "
+                    + "does not match specified (%d)", algorithmURI, algoKeyLength, specifiedKeyLength));
+        }
+        
+        return algoKeyLength;
+    }
+    
+}
diff --git a/opensaml-xmlsec-api/src/test/java/org/opensaml/xmlsec/derivation/KeyDerivationSupportTest.java b/opensaml-xmlsec-api/src/test/java/org/opensaml/xmlsec/derivation/KeyDerivationSupportTest.java
new file mode 100644
index 000000000..6aeb75711
--- /dev/null
+++ b/opensaml-xmlsec-api/src/test/java/org/opensaml/xmlsec/derivation/KeyDerivationSupportTest.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.xmlsec.derivation;
+
+import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
+import org.opensaml.security.crypto.JCAConstants;
+import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ *
+ */
+public class KeyDerivationSupportTest extends OpenSAMLInitBaseTestCase {
+    
+    @Test
+    public void getJCAKeyAlgorithm() throws Exception {
+        Assert.assertEquals(KeyDerivationSupport.getJCAKeyAlgorithm(
+                EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM), JCAConstants.KEY_ALGO_AES);
+        
+        Assert.assertEquals(KeyDerivationSupport.getJCAKeyAlgorithm(
+                EncryptionConstants.ALGO_ID_BLOCKCIPHER_TRIPLEDES), JCAConstants.KEY_ALGO_DESEDE);
+        
+        Assert.assertEquals(KeyDerivationSupport.getJCAKeyAlgorithm(
+                EncryptionConstants.ALGO_ID_KEYWRAP_AES128), JCAConstants.KEY_ALGO_AES);
+        
+        try {
+            KeyDerivationSupport.getJCAKeyAlgorithm("INVALID");
+            Assert.fail("Should have failed invalid URI");
+        } catch (KeyDerivationException e) {
+            // expected 
+        }
+    }
+
+    @Test
+    public void getEffectiveKeyLength() throws Exception {
+        Assert.assertEquals(KeyDerivationSupport.getEffectiveKeyLength(
+                EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM, null).intValue(), 128);
+        Assert.assertEquals(KeyDerivationSupport.getEffectiveKeyLength(
+                EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM, 128).intValue(), 128);
+        
+        Assert.assertEquals(KeyDerivationSupport.getEffectiveKeyLength(
+                EncryptionConstants.ALGO_ID_BLOCKCIPHER_TRIPLEDES, null).intValue(), 192);
+        Assert.assertEquals(KeyDerivationSupport.getEffectiveKeyLength(
+                EncryptionConstants.ALGO_ID_BLOCKCIPHER_TRIPLEDES, 192).intValue(), 192);
+        
+        Assert.assertEquals(KeyDerivationSupport.getEffectiveKeyLength(
+                EncryptionConstants.ALGO_ID_KEYWRAP_AES128, null).intValue(), 128);
+        Assert.assertEquals(KeyDerivationSupport.getEffectiveKeyLength(
+                EncryptionConstants.ALGO_ID_KEYWRAP_AES128, 128).intValue(), 128);
+        
+        // Non-length algorithm with non-null specified length should succeed as specified length
+        Assert.assertEquals(KeyDerivationSupport.getEffectiveKeyLength("SomeAlgo", 128).intValue(), 128);
+        
+        try {
+            KeyDerivationSupport.getEffectiveKeyLength(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM, 256);
+            Assert.fail("Should have failed mismatched specified length");
+        } catch (KeyDerivationException e) {
+           // expected 
+        }
+        
+        try {
+            KeyDerivationSupport.getEffectiveKeyLength( "SomeAlgo", null);
+            Assert.fail("Should have failed non-length URI and null specified length");
+        } catch (KeyDerivationException e) {
+           //expected 
+        }
+    }
+
+}
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/AbstractDerivationKeyAgreementProcessor.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/AbstractDerivationKeyAgreementProcessor.java
index d079888bc..81a6f667c 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/AbstractDerivationKeyAgreementProcessor.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/AbstractDerivationKeyAgreementProcessor.java
@@ -46,8 +46,10 @@ public abstract class AbstractDerivationKeyAgreementProcessor extends AbstractKe
             throw new KeyAgreementException("Required KeyDerivation parameter was not supplied");
         }
         
+        final Integer keySize = parameters.contains(KeySize.class) ? parameters.get(KeySize.class).getSize() : null;
+        
         try {
-            return keyDerivation.derive(secret, keyAlgorithm);
+            return keyDerivation.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/KeyAgreementParametersParser.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/KeyAgreementParametersParser.java
index e74f5fb1a..01352340d 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/KeyAgreementParametersParser.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/KeyAgreementParametersParser.java
@@ -25,6 +25,7 @@ import javax.annotation.Nonnull;
 import org.opensaml.core.xml.XMLObject;
 import org.opensaml.xmlsec.agreement.KeyAgreementException;
 import org.opensaml.xmlsec.agreement.KeyAgreementParameters;
+import org.opensaml.xmlsec.agreement.KeyAgreementSupport;
 import org.opensaml.xmlsec.encryption.AgreementMethod;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -83,6 +84,12 @@ public class KeyAgreementParametersParser {
             }
         }
         
+        // The grandparent's EncryptionMethod KeySize element is an implicit parameter to the agreement operation
+        final Integer keySize = KeyAgreementSupport.getExplicitKeySize(agreementMethod);
+        if (keySize != null) {
+            parameters.add(new KeySize(keySize));
+        }
+
         parameters.initializeAll();
         
         return parameters;
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementSupport.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/KeySize.java
similarity index 54%
copy from opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementSupport.java
copy to opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/KeySize.java
index 39a4c4a17..30e67d60a 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/agreement/KeyAgreementSupport.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/agreement/impl/KeySize.java
@@ -15,27 +15,38 @@
  * limitations under the License.
  */
 
-package org.opensaml.xmlsec.agreement;
+package org.opensaml.xmlsec.agreement.impl;
 
-import javax.annotation.Nullable;
+import javax.annotation.Nonnull;
 
-import org.opensaml.core.config.ConfigurationService;
+import org.opensaml.xmlsec.agreement.KeyAgreementParameter;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
 
 /**
- * Support for key agreement operations.
+ * Key agreement parameter used to explicitly represent the size of the derived key.
  */
-public final class KeyAgreementSupport {
+public class KeySize implements KeyAgreementParameter {
     
-    /** Constructor. */
-    private KeyAgreementSupport() {}
-
+    /** Key size. */
+    @Nonnull private Integer size;
     
     /**
-     * Get the global {@link KeyAgreementProcessorRegistry} instance.
+     * Constructor.
+     *
+     * @param keySize the key size, in bits
+     */
+    public KeySize(@Nonnull final Integer keySize) {
+        size = Constraint.isNotNull(keySize, "Specified key size was null");
+    }
+    
+    /**
+     * Get the key size, in bits.
      * 
-     * @return the global procesor registry, or null if nothing registered
+     * @return the key size in bits
      */
-    @Nullable public static KeyAgreementProcessorRegistry getGlobalProcessorRegistry() {
-        return ConfigurationService.get(KeyAgreementProcessorRegistry.class);
+    @Nonnull public Integer getSize() {
+       return size; 
     }
+
 }
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/ConcatKDF.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/ConcatKDF.java
index 32d8d7ebb..4f746b8be 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/ConcatKDF.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/ConcatKDF.java
@@ -45,6 +45,7 @@ import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
 import org.opensaml.xmlsec.algorithm.DigestAlgorithm;
 import org.opensaml.xmlsec.derivation.KeyDerivation;
 import org.opensaml.xmlsec.derivation.KeyDerivationException;
+import org.opensaml.xmlsec.derivation.KeyDerivationSupport;
 import org.opensaml.xmlsec.encryption.ConcatKDFParams;
 import org.opensaml.xmlsec.encryption.EncryptedType;
 import org.opensaml.xmlsec.encryption.KeyDerivationMethod;
@@ -265,21 +266,15 @@ public class ConcatKDF extends AbstractInitializableComponent
     }
 
     /** {@inheritDoc} */
-    public SecretKey derive(@Nonnull final byte[] secret, @Nonnull final String keyAlgorithm)
-            throws KeyDerivationException {
+    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");
         ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
         
-        final String jcaKeyAlgorithm = AlgorithmSupport.getKeyAlgorithm(keyAlgorithm);
-        if (jcaKeyAlgorithm == null) {
-            throw new KeyDerivationException("Could not determine JCA key algorithm from URI: " + keyAlgorithm);
-        }
+        final String jcaKeyAlgorithm = KeyDerivationSupport.getJCAKeyAlgorithm(keyAlgorithm);
         
-        final Integer jcaKeyLength = AlgorithmSupport.getKeyLength(keyAlgorithm);
-        if (jcaKeyLength == null) {
-            throw new KeyDerivationException("Could not determine JCA key length from URI: " + keyAlgorithm);
-        }
+        final Integer jcaKeyLength = KeyDerivationSupport.getEffectiveKeyLength(keyAlgorithm, keyLength);
         
         final byte[] otherInfo = Bytes.concat(
                 decodeParam(algorithmID, "AlgorithmID"),
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/PBKDF2.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/PBKDF2.java
index 2b9e5a293..43325a3cd 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/PBKDF2.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/derivation/impl/PBKDF2.java
@@ -40,6 +40,7 @@ import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
 import org.opensaml.xmlsec.algorithm.MACAlgorithm;
 import org.opensaml.xmlsec.derivation.KeyDerivation;
 import org.opensaml.xmlsec.derivation.KeyDerivationException;
+import org.opensaml.xmlsec.derivation.KeyDerivationSupport;
 import org.opensaml.xmlsec.encryption.IterationCount;
 import org.opensaml.xmlsec.encryption.KeyDerivationMethod;
 import org.opensaml.xmlsec.encryption.KeyLength;
@@ -276,20 +277,17 @@ public class PBKDF2 extends AbstractInitializableComponent
     // Checkstyle: CyclomaticComplexity ON
 
     /** {@inheritDoc} */
-    public SecretKey derive(@Nonnull final byte[] secret, @Nonnull final String keyAlgorithm)
-            throws KeyDerivationException {
+    public SecretKey derive(@Nonnull final byte[] secret, @Nonnull final String keyAlgorithm,
+            @Nullable final Integer specifiedKeyLength) throws KeyDerivationException {
         Constraint.isNotNull(secret, "Secret byte[] was null");
         Constraint.isNotNull(keyAlgorithm, "Key algorithm was null");
         ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
         
-        final String jcaKeyAlgorithm = AlgorithmSupport.getKeyAlgorithm(keyAlgorithm);
-        if (jcaKeyAlgorithm == null) {
-            throw new KeyDerivationException("Could not determine JCA key algorithm from URI: " + keyAlgorithm);
-        }
+        final String jcaKeyAlgorithm = KeyDerivationSupport.getJCAKeyAlgorithm(keyAlgorithm);
         
         final byte[] saltBytes = getEffectiveSalt();
         
-        final Integer length = getEffectiveKeyLength(keyAlgorithm);
+        final Integer length = getEffectiveKeyLength(keyAlgorithm, specifiedKeyLength);
         
         final String jcaPRF = AlgorithmSupport.getAlgorithmID(prf);
         
@@ -338,17 +336,17 @@ public class PBKDF2 extends AbstractInitializableComponent
      * Get the effective key length, in bits.
      * 
      * @param keyAlgorithm the algorithm for which the derived key will be used
+     * @param specifiedKeyLength 
      * 
      * @return the effective key length, in bits
      * 
      * @throws KeyDerivationException
      */
-    protected Integer getEffectiveKeyLength(@Nonnull final String keyAlgorithm) throws KeyDerivationException {
-        final Integer jcaKeyLength = AlgorithmSupport.getKeyLength(keyAlgorithm);
-        if (jcaKeyLength == null) {
-            throw new KeyDerivationException("Failed to determine key length for algorithm URI: " + keyAlgorithm);
-        }
-            
+    protected Integer getEffectiveKeyLength(@Nonnull final String keyAlgorithm,
+            @Nullable final Integer specifiedKeyLength) throws KeyDerivationException {
+        
+        final Integer jcaKeyLength = KeyDerivationSupport.getEffectiveKeyLength(keyAlgorithm, specifiedKeyLength);
+        
         if (keyLength == null) {
             // Usually the originator/encrypting case. We set it internally here so can emit in XML later.
             keyLength = jcaKeyLength;
diff --git a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/provider/AgreementMethodKeyInfoProvider.java b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/provider/AgreementMethodKeyInfoProvider.java
index 9a77047d3..99ca9d92d 100644
--- a/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/provider/AgreementMethodKeyInfoProvider.java
+++ b/opensaml-xmlsec-impl/src/main/java/org/opensaml/xmlsec/keyinfo/impl/provider/AgreementMethodKeyInfoProvider.java
@@ -36,6 +36,8 @@ import org.opensaml.xmlsec.agreement.impl.KeyAgreementParametersParser;
 import org.opensaml.xmlsec.agreement.impl.PrivateCredential;
 import org.opensaml.xmlsec.encryption.AgreementMethod;
 import org.opensaml.xmlsec.encryption.EncryptedType;
+import org.opensaml.xmlsec.encryption.EncryptionMethod;
+import org.opensaml.xmlsec.encryption.KeySize;
 import org.opensaml.xmlsec.encryption.OriginatorKeyInfo;
 import org.opensaml.xmlsec.encryption.RecipientKeyInfo;
 import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolutionMode;
@@ -113,7 +115,7 @@ public class AgreementMethodKeyInfoProvider extends AbstractKeyInfoProvider {
             final KeyAgreementParameters parameters = parametersParser.parse(agreementMethod);
             parameters.add(new PrivateCredential(recipientCredential));
             
-            final String keyAlgorithm = resolveKeyAlgorithmAndSize(agreementMethod, parameters);
+            final String keyAlgorithm = resolveKeyAlgorithm(agreementMethod);
             
             cred = processor.execute(originatorCredential, keyAlgorithm, parameters);
             
@@ -143,14 +145,13 @@ public class AgreementMethodKeyInfoProvider extends AbstractKeyInfoProvider {
      * </p>
      * 
      * @param agreementMethod the AgreementMethod to process
-     * @param parameters the key agreement parameters
      * 
      * @return the encryption algorithm URI
      * 
      * @throws SecurityException if the algorithm URI can not be resolved
      */
-    @Nonnull private String resolveKeyAlgorithmAndSize(@Nonnull final AgreementMethod agreementMethod,
-            @Nonnull final KeyAgreementParameters parameters) throws SecurityException {
+    @Nonnull private String resolveKeyAlgorithm(@Nonnull final AgreementMethod agreementMethod)
+            throws SecurityException {
         
         // This was already validated in handles(...)
         final EncryptedType encrytpedType = EncryptedType.class.cast(agreementMethod.getParent().getParent());
@@ -159,8 +160,6 @@ public class AgreementMethodKeyInfoProvider extends AbstractKeyInfoProvider {
             throw new SecurityException("EncryptedType contains no EncryptionMethod algorithm");
         }
         
-        //TODO handle KeySize when new KeySize param is ready; add it to parameters
-        
         return encrytpedType.getEncryptionMethod().getAlgorithm();
     }
 
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/KeyAgreementParametersParserTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/KeyAgreementParametersParserTest.java
index aa13f143b..fb15474fc 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/KeyAgreementParametersParserTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/agreement/impl/KeyAgreementParametersParserTest.java
@@ -24,6 +24,8 @@ import org.opensaml.xmlsec.derivation.impl.ConcatKDF;
 import org.opensaml.xmlsec.derivation.impl.PBKDF2;
 import org.opensaml.xmlsec.encryption.AgreementMethod;
 import org.opensaml.xmlsec.encryption.ConcatKDFParams;
+import org.opensaml.xmlsec.encryption.EncryptedData;
+import org.opensaml.xmlsec.encryption.EncryptionMethod;
 import org.opensaml.xmlsec.encryption.IterationCount;
 import org.opensaml.xmlsec.encryption.KeyDerivationMethod;
 import org.opensaml.xmlsec.encryption.KeyLength;
@@ -32,6 +34,7 @@ import org.opensaml.xmlsec.encryption.PRF;
 import org.opensaml.xmlsec.encryption.Salt;
 import org.opensaml.xmlsec.encryption.Specified;
 import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
+import org.opensaml.xmlsec.signature.KeyInfo;
 import org.opensaml.xmlsec.signature.support.SignatureConstants;
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -82,6 +85,63 @@ public class KeyAgreementParametersParserTest extends XMLObjectBaseTestCase {
         Assert.assertEquals(kdf.getSuppPrivInfo(), "EE");
     }
     
+    @Test
+    public void ECDHWithConcatKDFWithKeySize() throws KeyAgreementException {
+        org.opensaml.xmlsec.encryption.KeySize xmlKeySize = buildXMLObject(org.opensaml.xmlsec.encryption.KeySize.DEFAULT_ELEMENT_NAME);
+        xmlKeySize.setValue(80);
+        
+        EncryptionMethod em = buildXMLObject(EncryptionMethod.DEFAULT_ELEMENT_NAME);
+        em.setKeySize(xmlKeySize);
+        
+        EncryptedData ed = buildXMLObject(EncryptedData.DEFAULT_ELEMENT_NAME);
+        ed.setEncryptionMethod(em);
+        
+        KeyInfo keyInfo = buildXMLObject(KeyInfo.DEFAULT_ELEMENT_NAME);
+        ed.setKeyInfo(keyInfo);
+        
+        AgreementMethod agreementMethod = buildXMLObject(AgreementMethod.DEFAULT_ELEMENT_NAME);
+        agreementMethod.setAlgorithm(EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES);
+        keyInfo.getAgreementMethods().add(agreementMethod);
+        
+        KeyDerivationMethod kdm = buildXMLObject(KeyDerivationMethod.DEFAULT_ELEMENT_NAME);
+        kdm.setAlgorithm(EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF);
+        
+        ConcatKDFParams xmlParams = buildXMLObject(ConcatKDFParams.DEFAULT_ELEMENT_NAME);
+        xmlParams.setAlgorithmID("00AA");
+        xmlParams.setPartyUInfo("00BB");
+        xmlParams.setPartyVInfo("00CC");
+        xmlParams.setSuppPubInfo("00DD");
+        xmlParams.setSuppPrivInfo("00EE");
+        
+        org.opensaml.xmlsec.signature.DigestMethod digestMethod = buildXMLObject(org.opensaml.xmlsec.signature.DigestMethod.DEFAULT_ELEMENT_NAME);
+        digestMethod.setAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA512);
+        xmlParams.setDigestMethod(digestMethod);
+        
+        kdm.getUnknownXMLObjects().add(xmlParams);
+        
+        agreementMethod.getUnknownXMLObjects().add(kdm);
+        
+        KeyAgreementParametersParser parser = new KeyAgreementParametersParser();
+        
+        KeyAgreementParameters parameters = parser.parse(agreementMethod);
+        Assert.assertNotNull(parameters);
+        Assert.assertEquals(parameters.size(), 2);
+        
+        Assert.assertTrue(parameters.contains(ConcatKDF.class));
+        
+        ConcatKDF kdf = parameters.get(ConcatKDF.class);
+        Assert.assertTrue(kdf.isInitialized());
+        Assert.assertEquals(kdf.getDigestMethod(), SignatureConstants.ALGO_ID_DIGEST_SHA512);
+        Assert.assertEquals(kdf.getAlgorithmID(), "AA");
+        Assert.assertEquals(kdf.getPartyUInfo(), "BB");
+        Assert.assertEquals(kdf.getPartyVInfo(), "CC");
+        Assert.assertEquals(kdf.getSuppPubInfo(), "DD");
+        Assert.assertEquals(kdf.getSuppPrivInfo(), "EE");
+        
+        Assert.assertTrue(parameters.contains(KeySize.class));
+        Assert.assertEquals(parameters.get(KeySize.class).getSize().intValue(), 80);
+    }
+    
     @Test
     public void ECDHWithPBKDF2() throws KeyAgreementException {
         AgreementMethod agreementMethod = buildXMLObject(AgreementMethod.DEFAULT_ELEMENT_NAME);
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 761a73f2f..85561c287 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
@@ -294,7 +294,7 @@ public class ConcatKDFTest extends XMLObjectBaseTestCase {
         
         byte[] secret = Hex.decodeHex("DEADBEEF");
         
-        SecretKey derivedKey = kdf.derive(secret, EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM);
+        SecretKey derivedKey = kdf.derive(secret, EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM, null);
         
         Assert.assertNotNull(derivedKey);
         Assert.assertEquals(derivedKey.getAlgorithm(), "AES");
@@ -313,7 +313,7 @@ public class ConcatKDFTest extends XMLObjectBaseTestCase {
         
         byte[] secret = Hex.decodeHex("DEADBEEF");
         
-        SecretKey derivedKey = kdf.derive(secret, EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM);
+        SecretKey derivedKey = kdf.derive(secret, EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM, null);
         
         Assert.assertNotNull(derivedKey);
         Assert.assertEquals(derivedKey.getAlgorithm(), "AES");
@@ -327,7 +327,7 @@ public class ConcatKDFTest extends XMLObjectBaseTestCase {
         
         byte[] secret = Hex.decodeHex("DEADBEEF");
         
-        kdf.derive(secret, "urn:test:InvalidKeyAlgorithm");
+        kdf.derive(secret, "urn:test:InvalidKeyAlgorithm", null);
     }
 
     @Test(expectedExceptions = KeyDerivationException.class)
@@ -338,7 +338,23 @@ public class ConcatKDFTest extends XMLObjectBaseTestCase {
         byte[] secret = Hex.decodeHex("DEADBEEF");
         
         // Just use this as a stand-in for something which is KeySpecifiedAlgorithm but not KeyLengthSpecifiedAlgorithm
-        kdf.derive(secret, SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
+        kdf.derive(secret, SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256, null);
+    }
+
+    @Test
+    public void nonKeyLengthAlgorithmWithSpecifiedLength() throws Exception {
+        ConcatKDF kdf = new ConcatKDF();
+        kdf.initialize();
+        
+        byte[] secret = Hex.decodeHex("DEADBEEF");
+        
+        // Just use this as a stand-in for something which is KeySpecifiedAlgorithm but not KeyLengthSpecifiedAlgorithm
+        SecretKey derivedKey = kdf.derive(secret, SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256, 256);
+        
+        // This is bogus obviously, but just need to test that a known algo URI that is non-key length works with specified key length
+        Assert.assertNotNull(derivedKey);
+        Assert.assertEquals(derivedKey.getAlgorithm(), "RSA");
+        Assert.assertEquals(derivedKey.getEncoded().length * 8, 256);
     }
 
     @Test
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/MockKeyDerivation.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/MockKeyDerivation.java
index fb4acbd17..b17f90398 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/MockKeyDerivation.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/MockKeyDerivation.java
@@ -35,7 +35,7 @@ public class MockKeyDerivation implements KeyDerivation {
     }
 
     /** {@inheritDoc} */
-    public SecretKey derive(byte[] secret, String keyAlgorithm) throws KeyDerivationException {
+    public SecretKey derive(byte[] secret, String keyAlgorithm, Integer keyLength) throws KeyDerivationException {
         try {
             String algo = AlgorithmSupport.getKeyAlgorithm(keyAlgorithm);
             Integer length = AlgorithmSupport.getKeyLength(keyAlgorithm);
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/PBKDF2Test.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/PBKDF2Test.java
index 70bdf4f33..aae1ef1d5 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/PBKDF2Test.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/derivation/impl/PBKDF2Test.java
@@ -277,7 +277,7 @@ public class PBKDF2Test extends XMLObjectBaseTestCase {
         
         byte[] secret = Hex.decodeHex("DEADBEEF");
         
-        SecretKey derivedKey = kdf.derive(secret, EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM);
+        SecretKey derivedKey = kdf.derive(secret, EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM, null);
         
         Assert.assertNotNull(derivedKey);
         Assert.assertEquals(derivedKey.getAlgorithm(), "AES");
@@ -303,7 +303,7 @@ public class PBKDF2Test extends XMLObjectBaseTestCase {
         
         byte[] secret = Hex.decodeHex("DEADBEEF");
         
-        SecretKey derivedKey = kdf.derive(secret, EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM);
+        SecretKey derivedKey = kdf.derive(secret, EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM, null);
         
         Assert.assertNotNull(derivedKey);
         Assert.assertEquals(derivedKey.getAlgorithm(), "AES");
@@ -318,7 +318,7 @@ public class PBKDF2Test extends XMLObjectBaseTestCase {
         
         byte[] secret = Hex.decodeHex("DEADBEEF");
         
-        kdf.derive(secret, EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM);
+        kdf.derive(secret, EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM, null);
     }
     
     @Test(expectedExceptions = KeyDerivationException.class)
@@ -328,7 +328,7 @@ public class PBKDF2Test extends XMLObjectBaseTestCase {
         
         byte[] secret = Hex.decodeHex("DEADBEEF");
         
-        kdf.derive(secret, "urn:test:InvalidKeyAlgorithm");
+        kdf.derive(secret, "urn:test:InvalidKeyAlgorithm", null);
     }
 
     @Test(expectedExceptions = KeyDerivationException.class)
@@ -339,7 +339,23 @@ public class PBKDF2Test extends XMLObjectBaseTestCase {
         byte[] secret = Hex.decodeHex("DEADBEEF");
         
         // Just use this as a stand-in for something which is KeySpecifiedAlgorithm but not KeyLengthSpecifiedAlgorithm
-        kdf.derive(secret, SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
+        kdf.derive(secret, SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256, null);
+    }
+    
+    @Test
+    public void nonKeyLengthAlgorithmWithSpecifiedLength() throws Exception {
+        PBKDF2 kdf = new PBKDF2();
+        kdf.initialize();
+        
+        byte[] secret = Hex.decodeHex("DEADBEEF");
+        
+        // Just use this as a stand-in for something which is KeySpecifiedAlgorithm but not KeyLengthSpecifiedAlgorithm
+        SecretKey derivedKey = kdf.derive(secret, SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256, 256);
+        
+        // This is bogus obviously, but just need to test that a known algo URI that is non-key length works with specified key length
+        Assert.assertNotNull(derivedKey);
+        Assert.assertEquals(derivedKey.getAlgorithm(), "RSA");
+        Assert.assertEquals(derivedKey.getEncoded().length * 8, 256);
     }
     
     
@@ -529,7 +545,7 @@ public class PBKDF2Test extends XMLObjectBaseTestCase {
         kdf.setSalt(salt);
         kdf.initialize();
         
-        SecretKey derivedKey = kdf.derive(secret, keyAlgorithm);
+        SecretKey derivedKey = kdf.derive(secret, keyAlgorithm, null);
         
         Assert.assertNotNull(derivedKey);
         Assert.assertEquals(derivedKey.getAlgorithm(), jcaKeyAlgorithm);

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list