This is an automated email from the ASF dual-hosted git repository.

coheigea pushed a commit to branch 3_0_x-fixes
in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git

commit b26872be9bcfa9299cb6f75aa48cdcdd1f6a8a2a
Author: jrihtarsic <[email protected]>
AuthorDate: Mon Jul 15 15:15:13 2024 +0200

    [WSS-710] Implementation of the configurations for KeyDerivation functions 
(#298)
    
    * [WSS-710] Implementation of the configuration options to set 
KeyDerivation parameters
    
    * The PR updates
    
    * Changed Random with the SecureRandom for the Default HKDF salt parameter
    
    * Update PR with the lasted xmlsec updated
    
    * Add key derivation function URI validation with the 
AlgorithmSuiteValidator
    
    ---------
    
    Co-authored-by: RIHTARSIC Joze <[email protected]>
---
 parent/pom.xml                                     |   2 +-
 .../wss4j/common/ConfigurationConstants.java       |  38 ++++++
 .../apache/wss4j/common/EncryptionActionToken.java |  20 +++
 .../org/apache/wss4j/common/WSS4JConstants.java    |   8 +-
 .../common/crypto/AlgorithmSuiteValidator.java     |  20 +++
 .../apache/wss4j/dom/action/EncryptionAction.java  |  11 ++
 .../org/apache/wss4j/dom/handler/WSHandler.java    |  17 ++-
 .../wss4j/dom/message/WSSecEncryptedKey.java       | 151 +++++++++++++++++----
 .../wss4j/dom/processor/EncryptedKeyProcessor.java |  30 +++-
 .../apache/wss4j/dom/message/EncryptionTest.java   | 137 +++++++++++++++++--
 10 files changed, 389 insertions(+), 45 deletions(-)

diff --git a/parent/pom.xml b/parent/pom.xml
index 99934bbbd..44bd688dc 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -48,7 +48,7 @@
         <wsdl4j.version>1.6.3</wsdl4j.version>
         <woodstox.version>6.5.1</woodstox.version>
         <xz.version>1.9</xz.version>
-        <xmlsec.version>3.0.4</xmlsec.version>
+        <xmlsec.version>3.0.5-SNAPSHOT</xmlsec.version>
         <xmlunit.version>2.10.0</xmlunit.version>
         <!-- OSGi related properties -->
         <wss4j.osgi.import />
diff --git 
a/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java
 
b/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java
index 131ea60c8..3eb0fa520 100644
--- 
a/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java
+++ 
b/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java
@@ -752,6 +752,44 @@ public class ConfigurationConstants {
      */
     public static final String ENC_KEY_AGREEMENT_METHOD = 
"encryptionKeyAgreementMethod";
 
+    /**
+     * Defines the Key Derivation algorithm to derive encryption key used with 
the keyAgreement method.
+     * The default algorithm is:
+     * "http://www.w3.org/2021/04/xmldsig-more#hkdf";
+     *
+     * <p/>
+     * The application may set this parameter using the following method:
+     * <pre>
+     *      
call.setProperty(ConfigurationConstants.ENC_KEY_DERIVATION_FUNCTION,
+     *          WSConstants.KEYDERIVATION_HKDF);
+     * </pre>
+     *
+     */
+    public static final String ENC_KEY_DERIVATION_FUNCTION = 
"encryptionKeyDerivationFunction";
+
+    /**
+     * Defines the Key Derivation parameters to derive encryption key used 
with the keyAgreement method. In case the
+     * property value is set  it supersede the ENC_KEY_DERIVATION_FUNCTION 
value.
+     * The value for the property must implement the 
<code>org.apache.xml.security.encryption.params.KeyDerivationParameters</code>
+     * interface. Currently, only the 
<code>org.apache.xml.security.encryption.params.HKDFParams</code> and
+     * <code>org.apache.xml.security.encryption.params.ConcatKDFParams</code>.
+     *
+     *
+     * The application may set this parameter using the following method:
+     * <pre>
+     *     KeyDerivationParameters kdfParams = new ConcatKDFParams(keyBitLen, 
MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256);
+     *     kdfParams.setAlgorithmId("00363532534541");
+     *     kdfParams.setPartyUInfo("00DFC9DB773C588F8F");
+     *     kdfParams.setPartyVInfo("00DFDA76F7AB09B7C9");
+     *     kdfParams.setSuppPubInfo(null);
+     *     kdfParams.setSuppPrivInfo(null);
+     *
+     *     
call.set(ConfigurationConstants.ENC_KEY_DERIVATION_PARAMS,kdfParams);
+     * </pre>
+     *
+     */
+    public static final String ENC_KEY_DERIVATION_PARAMS = 
"encryptionKeyDerivationParams";
+
     /**
      * Parameter to define which parts of the request shall be encrypted.
      * <p/>
diff --git 
a/ws-security-common/src/main/java/org/apache/wss4j/common/EncryptionActionToken.java
 
b/ws-security-common/src/main/java/org/apache/wss4j/common/EncryptionActionToken.java
index 472e09fbf..cf6672643 100644
--- 
a/ws-security-common/src/main/java/org/apache/wss4j/common/EncryptionActionToken.java
+++ 
b/ws-security-common/src/main/java/org/apache/wss4j/common/EncryptionActionToken.java
@@ -19,6 +19,8 @@
 package org.apache.wss4j.common;
 
 
+import org.apache.xml.security.encryption.params.KeyDerivationParameters;
+
 /**
  * This class encapsulates configuration for Encryption Actions.
  */
@@ -28,6 +30,8 @@ public class EncryptionActionToken extends 
SignatureEncryptionActionToken {
     private String mgfAlgorithm;
     private String symmetricAlgorithm;
     private String keyAgreementMethodAlgorithm;
+    private String keyDerivationFunction;
+    private KeyDerivationParameters keyDerivationParameters;
     private String keyTransportAlgorithm;
     private boolean getSymmetricKeyFromCallbackHandler;
 
@@ -55,6 +59,7 @@ public class EncryptionActionToken extends 
SignatureEncryptionActionToken {
     public void setKeyTransportAlgorithm(String keyTransportAlgorithm) {
         this.keyTransportAlgorithm = keyTransportAlgorithm;
     }
+
     public String getKeyAgreementMethodAlgorithm() {
         return keyAgreementMethodAlgorithm;
     }
@@ -68,5 +73,20 @@ public class EncryptionActionToken extends 
SignatureEncryptionActionToken {
         this.getSymmetricKeyFromCallbackHandler = 
getSymmetricKeyFromCallbackHandler;
     }
 
+    public String getKeyDerivationFunction() {
+        return keyDerivationFunction;
+    }
+
+    public void setKeyDerivationFunction(String keyDerivationFunction) {
+        this.keyDerivationFunction = keyDerivationFunction;
+    }
+
+    public KeyDerivationParameters getKeyDerivationParameters() {
+        return keyDerivationParameters;
+    }
+
+    public void setKeyDerivationParameters(KeyDerivationParameters 
keyDerivationParameters) {
+        this.keyDerivationParameters = keyDerivationParameters;
+    }
 }
 
diff --git 
a/ws-security-common/src/main/java/org/apache/wss4j/common/WSS4JConstants.java 
b/ws-security-common/src/main/java/org/apache/wss4j/common/WSS4JConstants.java
index 9e5c80291..33fc0dd2a 100644
--- 
a/ws-security-common/src/main/java/org/apache/wss4j/common/WSS4JConstants.java
+++ 
b/ws-security-common/src/main/java/org/apache/wss4j/common/WSS4JConstants.java
@@ -116,10 +116,16 @@ public class WSS4JConstants {
             "http://www.w3.org/2001/04/xmlenc#kw-aes256";;
     public static final String KEYWRAP_TRIPLEDES =
             "http://www.w3.org/2001/04/xmlenc#kw-tripledes";;
-    public static final String KDF_CONCAT =
+    public static final String KEYDERIVATION_CONCATKDF =
             "http://www.w3.org/2009/xmlenc11#ConcatKDF";;
+    public static final String KEYDERIVATION_HKDF =
+            "http://www.w3.org/2021/04/xmldsig-more#hkdf";;
     public static final String AGREEMENT_METHOD_ECDH_ES =
             "http://www.w3.org/2009/xmlenc11#ECDH-ES";;
+    public static final String AGREEMENT_METHOD_X25519 =
+            "http://www.w3.org/2021/04/xmldsig-more#x25519";;
+    public static final String AGREEMENT_METHOD_X448 =
+            "http://www.w3.org/2021/04/xmldsig-more#x448";;
     public static final String TRIPLE_DES =
         "http://www.w3.org/2001/04/xmlenc#tripledes-cbc";;
     public static final String AES_128 =
diff --git 
a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/AlgorithmSuiteValidator.java
 
b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/AlgorithmSuiteValidator.java
index 923ea7c37..3f707ca19 100644
--- 
a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/AlgorithmSuiteValidator.java
+++ 
b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/AlgorithmSuiteValidator.java
@@ -152,6 +152,26 @@ public class AlgorithmSuiteValidator {
         }
     }
 
+    /**
+     * Method to check the Key Derivation algorithm is on the approved list  
of the
+     * AlgorithmSuite configuration.
+     * @param keyDerivationFunction the key derivation function to be validated
+     * @throws WSSecurityException if the approved list is not empty and the 
key
+     * derivation function is not on the list
+     */
+    public void checkKeyDerivationFunction(
+            String keyDerivationFunction
+    ) throws WSSecurityException {
+        Set<String> keyDerivationFunctions = 
algorithmSuite.getDerivedKeyAlgorithms();
+        if (!keyDerivationFunctions.isEmpty()
+                && !keyDerivationFunctions.contains(keyDerivationFunction)) {
+            LOG.warn(
+                    "The Key derivation function does not match the 
requirement"
+            );
+            throw new 
WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
+        }
+    }
+
     public void checkSymmetricEncryptionAlgorithm(
         String symmetricAlgorithm
     ) throws WSSecurityException {
diff --git 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionAction.java
 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionAction.java
index 08de7af59..7bc498714 100644
--- 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionAction.java
+++ 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionAction.java
@@ -61,12 +61,23 @@ public class EncryptionAction implements Action {
         if (encryptionToken.getSymmetricAlgorithm() != null) {
             
wsEncrypt.setSymmetricEncAlgorithm(encryptionToken.getSymmetricAlgorithm());
         }
+
         if (encryptionToken.getKeyTransportAlgorithm() != null) {
             
wsEncrypt.setKeyEncAlgo(encryptionToken.getKeyTransportAlgorithm());
         }
+
         if (encryptionToken.getKeyAgreementMethodAlgorithm() != null) {
             
wsEncrypt.setKeyAgreementMethod(encryptionToken.getKeyAgreementMethodAlgorithm());
         }
+
+        if (encryptionToken.getKeyDerivationFunction() != null) {
+            
wsEncrypt.setKeyDerivationMethod(encryptionToken.getKeyDerivationFunction());
+        }
+
+        if (encryptionToken.getKeyDerivationParameters() != null) {
+            
wsEncrypt.setKeyDerivationParameters(encryptionToken.getKeyDerivationParameters());
+        }
+
         if (encryptionToken.getDigestAlgorithm() != null) {
             wsEncrypt.setDigestAlgorithm(encryptionToken.getDigestAlgorithm());
         }
diff --git 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/WSHandler.java 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/WSHandler.java
index 7869346c7..242803038 100644
--- a/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/WSHandler.java
+++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/WSHandler.java
@@ -47,6 +47,7 @@ import org.apache.wss4j.common.util.Loader;
 import org.apache.wss4j.dom.message.WSSecHeader;
 import org.apache.wss4j.dom.message.token.SignatureConfirmation;
 import org.apache.wss4j.dom.util.WSSecurityUtil;
+import org.apache.xml.security.encryption.params.KeyDerivationParameters;
 import org.w3c.dom.Document;
 
 /**
@@ -662,7 +663,12 @@ public abstract class WSHandler {
 
         String keyAgreementMethodAlgorithm = 
getString(WSHandlerConstants.ENC_KEY_AGREEMENT_METHOD, mc);
         if (keyAgreementMethodAlgorithm != null && 
!keyAgreementMethodAlgorithm.isEmpty()) {
-            algorithmSuite.addKeyAgreementMethodAlgorithm(transportAlgorithm);
+            
algorithmSuite.addKeyAgreementMethodAlgorithm(keyAgreementMethodAlgorithm);
+        }
+
+        String keyDerivationAlgorithm = 
getString(WSHandlerConstants.ENC_KEY_DERIVATION_FUNCTION, mc);
+        if (keyDerivationAlgorithm != null && 
!keyDerivationAlgorithm.isEmpty()) {
+            algorithmSuite.addDerivedKeyAlgorithm(keyDerivationAlgorithm);
         }
 
         reqData.setAlgorithmSuite(algorithmSuite);
@@ -718,6 +724,15 @@ public abstract class WSHandler {
                 getString(WSHandlerConstants.ENC_KEY_AGREEMENT_METHOD, mc);
         actionToken.setKeyAgreementMethodAlgorithm(encKeyAgreementMethod);
 
+        String encKeyDerivationAlgorithm =
+                getString(WSHandlerConstants.ENC_KEY_DERIVATION_FUNCTION, mc);
+        actionToken.setKeyDerivationFunction(encKeyDerivationAlgorithm);
+
+        Object obj = getProperty(mc, 
WSHandlerConstants.ENC_KEY_DERIVATION_PARAMS);
+        if (obj instanceof KeyDerivationParameters) {
+            
actionToken.setKeyDerivationParameters((KeyDerivationParameters)obj);
+        }
+
         String derivedKeyReference = 
getString(WSHandlerConstants.DERIVED_TOKEN_REFERENCE, mc);
         actionToken.setDerivedKeyTokenReference(derivedKeyReference);
 
diff --git 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java
 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java
index adda858c2..537c9678a 100644
--- 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java
+++ 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java
@@ -19,28 +19,11 @@
 
 package org.apache.wss4j.dom.message;
 
-import java.security.*;
-import java.security.cert.X509Certificate;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.OAEPParameterSpec;
-import javax.xml.crypto.MarshalException;
-import javax.xml.crypto.dom.DOMStructure;
-import javax.xml.crypto.dsig.XMLSignatureFactory;
-import javax.xml.crypto.dsig.keyinfo.KeyInfo;
-import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
-import javax.xml.crypto.dsig.keyinfo.KeyValue;
-
+import org.apache.wss4j.common.WSS4JConstants;
 import org.apache.wss4j.common.crypto.Crypto;
 import org.apache.wss4j.common.crypto.CryptoType;
 import org.apache.wss4j.common.ext.WSSecurityException;
-import org.apache.wss4j.common.token.BinarySecurity;
-import org.apache.wss4j.common.token.DOMX509Data;
-import org.apache.wss4j.common.token.DOMX509IssuerSerial;
-import org.apache.wss4j.common.token.Reference;
-import org.apache.wss4j.common.token.SecurityTokenReference;
-import org.apache.wss4j.common.token.X509Security;
+import org.apache.wss4j.common.token.*;
 import org.apache.wss4j.common.util.AttachmentUtils;
 import org.apache.wss4j.common.util.KeyUtils;
 import org.apache.wss4j.dom.WSConstants;
@@ -48,6 +31,8 @@ import org.apache.wss4j.dom.util.WSSecurityUtil;
 import org.apache.xml.security.encryption.XMLCipherUtil;
 import org.apache.xml.security.encryption.XMLEncryptionException;
 import org.apache.xml.security.encryption.keys.content.AgreementMethodImpl;
+import org.apache.xml.security.encryption.params.ConcatKDFParams;
+import org.apache.xml.security.encryption.params.HKDFParams;
 import org.apache.xml.security.encryption.params.KeyAgreementParameters;
 import org.apache.xml.security.encryption.params.KeyDerivationParameters;
 import org.apache.xml.security.exceptions.XMLSecurityException;
@@ -58,6 +43,19 @@ import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Text;
 
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.OAEPParameterSpec;
+import javax.xml.crypto.MarshalException;
+import javax.xml.crypto.dom.DOMStructure;
+import javax.xml.crypto.dsig.XMLSignatureFactory;
+import javax.xml.crypto.dsig.keyinfo.KeyInfo;
+import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
+import javax.xml.crypto.dsig.keyinfo.KeyValue;
+import java.security.*;
+import java.security.cert.X509Certificate;
+
 /**
  * Builder class to build an EncryptedKey.
  *
@@ -80,10 +78,23 @@ public class WSSecEncryptedKey extends WSSecBase {
     /**
      * Key agreement method algorithm used to encrypt the transport key.
      * Example for ECDH-ES: http://www.w3.org/2009/xmlenc11#ECDH-ES
-     *
+     * and xec example: X25519: http://www.w3.org/2021/04/xmldsig-more#x25519,
+     *  x448: http://www.w3.org/2021/04/xmldsig-more#x448
      */
     private String keyAgreementMethod;
 
+    /**
+     * Method to derive the key to be used to encrypt the data with the Key 
keyAgreementMethod
+     *
+     */
+    private String keyDerivationMethod = WSS4JConstants.KEYDERIVATION_HKDF;
+
+
+    /**
+     * The Key Derivation Parameters for the with the Key keyAgreementMethod
+     */
+    private KeyDerivationParameters keyDerivationParameters;
+
     /**
      * Digest Algorithm to be used with RSA-OAEP. The default is SHA-1 (which 
is not
      * written out unless it is explicitly configured).
@@ -215,7 +226,7 @@ public class WSSecEncryptedKey extends WSSecBase {
 
             Key kek;
             KeyAgreementParameters dhSpec = null;
-            if 
(WSConstants.AGREEMENT_METHOD_ECDH_ES.equals(keyAgreementMethod)) {
+            if (isKeyAgreementConfigured(keyAgreementMethod)) {
                 // generate ephemeral keys the key must match receivers keys
                 dhSpec = buildKeyAgreementParameter(remoteCert.getPublicKey());
                 kek = generateEncryptionKey(dhSpec);
@@ -373,7 +384,7 @@ public class WSSecEncryptedKey extends WSSecBase {
             keyInfoElement.setAttributeNS(
                 WSConstants.XMLNS_NS, "xmlns:" + WSConstants.SIG_PREFIX, 
WSConstants.SIG_NS
             );
-            if 
(WSConstants.AGREEMENT_METHOD_ECDH_ES.equals(keyAgreementMethod)) {
+            if (isKeyAgreementConfigured(keyAgreementMethod)) {
                 try {
                     AgreementMethodImpl agreementMethod = new 
AgreementMethodImpl(getDocument(), dhSpec);
                     
agreementMethod.getRecipientKeyInfo().addUnknownElement(secToken.getElement());
@@ -389,7 +400,15 @@ public class WSSecEncryptedKey extends WSSecBase {
             }
             encryptedKeyElement.appendChild(keyInfoElement);
         }
+    }
 
+    /**
+     * Method verifies is the key agreement method is configured(not empty)
+     * @param keyAgreementMethod the key agreement method
+     * @return true if the key agreement method is not empty else false
+     */
+    private boolean isKeyAgreementConfigured(String keyAgreementMethod) {
+         return keyAgreementMethod != null && !keyAgreementMethod.isEmpty();
     }
 
     private void addIssuerSerial(X509Certificate remoteCert, 
SecurityTokenReference secToken, boolean isCommaDelimited)
@@ -534,21 +553,29 @@ public class WSSecEncryptedKey extends WSSecBase {
     }
 
     /**
-     * Method builds the KeyAgreementParameterSpec for the ECDH-ES Key 
Agreement Method using
-     * the recipient's public key and preconfigured values: keyEncAlgo, 
digestAlgo and keyAgreementMethod
+     * Method builds the KeyAgreementParameterSpec for the ECDH-ES, X25519 or 
X448
+     * Key Agreement Method using the recipient's public key and preconfigured 
values:
+     * keyEncAlgo, digestAlgo and keyAgreementMethod. If the 
keyDerivationParameters
+     * are not set, the default values for the key derivation method are used.
      *
      * @param recipientPublicKey the recipient's public key
      * @return KeyAgreementParameterSpec the {@link 
java.security.spec.AlgorithmParameterSpec} for generating the
      * key for encrypting transport key and generating XML elements.
      *
-     * @throws WSSecurityException if the KeyAgreementParameterSpec cannot be 
created
+     * @throws WSSecurityException if the KeyAgreementParameterSpec cannot be 
created due to
+     * invalid key agreement method or key derivation method and wrap 
algorithm not supported
      */
     private KeyAgreementParameters buildKeyAgreementParameter(PublicKey 
recipientPublicKey)
             throws WSSecurityException {
         KeyAgreementParameters dhSpec;
         try {
             int keyBitLength = 
org.apache.xml.security.utils.KeyUtils.getAESKeyBitSizeForWrapAlgorithm(keyEncAlgo);
-            KeyDerivationParameters kdf = 
XMLCipherUtil.constructConcatKeyDerivationParameter(keyBitLength, digestAlgo);
+            KeyDerivationParameters kdf = keyDerivationParameters;
+            if (keyDerivationParameters == null) {
+                LOG.debug("Set default KeyDerivationParameters for key 
derivation method: [{}]",
+                        keyDerivationMethod);
+                kdf = buildDefaultKeyDerivationParameters(keyBitLength);
+            }
             KeyPair dhKeyPair = 
org.apache.xml.security.utils.KeyUtils.generateEphemeralDHKeyPair(recipientPublicKey,
 null);
             dhSpec = 
XMLCipherUtil.constructAgreementParameters(keyAgreementMethod,
                     KeyAgreementParameters.ActorType.ORIGINATOR, kdf, null, 
recipientPublicKey);
@@ -561,6 +588,62 @@ public class WSSecEncryptedKey extends WSSecBase {
         return dhSpec;
     }
 
+    /**
+     * Method builds the KeyDerivationParameters for keyDerivationMethod with 
default values.
+     * The default values for the key derivation method are:
+     * <ul>
+     *   <li>ConcatKDF
+     *     <ul>
+     *       <li> DigestAlgorithm: 
"http://www.w3.org/2001/04/xmlenc#sha256";</li>
+     *       <li> AlgorithmID: "0000"</li>
+     *       <li> PartyUInfo: ""</li>
+     *       <li> PartyVInfo: ""</li>
+     *       <li> SuppPubInfo: null</li>
+     *       <li> SuppPrivInfo: null</li>
+     *     </ul>
+     *   <li>HKDF: SHA-256
+     *     <ul>
+     *       <li> PRF: http://www.w3.org/2001/04/xmldsig-more#hmac-sha256 </li>
+     *       <li> Salt: random 256 bit value</li>
+     *       <li> Info: null</li>
+     *     </ul>
+     *   </li>
+     * </ul>
+     *
+     * @param keyBitLength the length of the derived key in bits
+     * @return KeyDerivationParameters the {@link KeyDerivationParameters} for 
generating the
+     * key for encrypting transport key and generating XML elements.
+     * @throws WSSecurityException if the KeyDerivationParameters cannot be 
created
+     */
+    private KeyDerivationParameters buildDefaultKeyDerivationParameters(int 
keyBitLength) throws WSSecurityException {
+
+        switch (keyDerivationMethod) {
+            case WSS4JConstants.KEYDERIVATION_CONCATKDF:
+                return ConcatKDFParams.createBuilder(keyBitLength, 
WSConstants.SHA256)
+                        .algorithmID("0000")
+                        .partyUInfo("")
+                        .partyVInfo("")
+                        .build();
+            case WSS4JConstants.KEYDERIVATION_HKDF:
+                // use semi random value for salt.
+                // rfc5869: Yet, even a salt value of less quality (shorter in
+                //   size or with limited entropy) may still make a significant
+                //   contribution to the security of the output keying material
+                byte[] semiRandom = new byte[keyBitLength / 8];
+                new SecureRandom().nextBytes(semiRandom);
+                return HKDFParams.createBuilder(keyBitLength, 
WSS4JConstants.HMAC_SHA256)
+                        .salt(semiRandom)
+                        .info(null)
+                        .build();
+            default:
+                throw new WSSecurityException(
+                        WSSecurityException.ErrorCode.FAILED_ENCRYPTION, 
"unsupportedKeyDerivationMethod",
+                        new Object[]{keyDerivationMethod}
+                );
+        }
+    }
+
+
     /**
      * Method generates the key for encrypting the transport key using the 
KeyAgreementParameterSpec
      *
@@ -793,6 +876,22 @@ public class WSSecEncryptedKey extends WSSecBase {
         this.keyAgreementMethod = keyAgreementMethod;
     }
 
+    public String getKeyDerivationMethod() {
+        return keyDerivationMethod;
+    }
+
+    public void setKeyDerivationMethod(String keyDerivationMethod) {
+        this.keyDerivationMethod = keyDerivationMethod;
+    }
+
+    public KeyDerivationParameters getKeyDerivationParameters() {
+        return keyDerivationParameters;
+    }
+
+    public void setKeyDerivationParameters(KeyDerivationParameters 
keyDerivationParameters) {
+        this.keyDerivationParameters = keyDerivationParameters;
+    }
+
     /**
      * Get the id of the BSt generated during <code>prepare()</code>.
      *
diff --git 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/EncryptedKeyProcessor.java
 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/EncryptedKeyProcessor.java
index 06622e319..e7a12412b 100644
--- 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/EncryptedKeyProcessor.java
+++ 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/EncryptedKeyProcessor.java
@@ -35,6 +35,7 @@ import javax.crypto.spec.OAEPParameterSpec;
 import javax.xml.crypto.dsig.XMLSignatureFactory;
 
 import org.apache.xml.security.encryption.AgreementMethod;
+import org.apache.xml.security.encryption.KeyDerivationMethod;
 import org.apache.xml.security.encryption.XMLCipherUtil;
 import org.apache.xml.security.encryption.keys.RecipientKeyInfo;
 import org.apache.xml.security.encryption.keys.content.AgreementMethodImpl;
@@ -150,9 +151,11 @@ public class EncryptedKeyProcessor implements Processor {
         PublicKey publicKey = null;
         boolean symmetricKeyWrap = 
isSymmetricKeyWrap(encryptedKeyTransportMethod);
         AgreementMethod agreementMethod = null;
+        KeyDerivationMethod keyDerivationMethod = null;
         if (isDHKeyWrap) {
             // get key agreement method value
             agreementMethod = 
getAgreementMethodFromElement(keyInfoChildElement);
+            keyDerivationMethod = getKeyDerivationFunction(agreementMethod);
             //  get the recipient key info element
             keyInfoChildElement = 
getRecipientKeyInfoChildElement(agreementMethod);
             if (keyInfoChildElement == null) {
@@ -184,6 +187,11 @@ public class EncryptedKeyProcessor implements Processor {
                         agreementMethod.getAlgorithm()
                 );
             }
+            if (keyDerivationMethod != null) {
+                algorithmSuiteValidator.checkKeyDerivationFunction(
+                        keyDerivationMethod.getAlgorithm()
+                );
+            }
         }
 
         byte[] encryptedEphemeralKey = null;
@@ -419,7 +427,9 @@ public class EncryptedKeyProcessor implements Processor {
                 && 
WSConstants.ENC_NS.equals(keyInfoChildElement.getNamespaceURI())) {
             String algorithmURI = keyInfoChildElement.getAttributeNS(null, 
"Algorithm");
             // Only ECDH_ES is supported for AgreementMethod
-            if (!WSConstants.AGREEMENT_METHOD_ECDH_ES.equals(algorithmURI)) {
+            if (!(WSConstants.AGREEMENT_METHOD_ECDH_ES.equals(algorithmURI)
+                || WSConstants.AGREEMENT_METHOD_X25519.equals(algorithmURI)
+                || WSConstants.AGREEMENT_METHOD_X448.equals(algorithmURI))) {
                 throw new WSSecurityException(
                         WSSecurityException.ErrorCode.UNSUPPORTED_ALGORITHM,
                         "unknownAlgorithm", new Object[]{algorithmURI});
@@ -562,6 +572,24 @@ public class EncryptedKeyProcessor implements Processor {
         }
     }
 
+    /**
+     * Method retrieved the KeyDerivationMethod child element from the 
AgreementMethod object.
+     *
+     * @param agreementMethod object containing th KeyDerivationMethod.
+     * @return the {@link KeyDerivationMethod} object or null if no 
KeyDerivation element is specified in the agreementMethod.
+     * @throws WSSecurityException if KeyDerivationMethod can not be parsed 
from Element structure.
+     */
+    private KeyDerivationMethod getKeyDerivationFunction(AgreementMethod 
agreementMethod) throws WSSecurityException {
+        if (agreementMethod == null) {
+            return null;
+        }
+        try {
+            return agreementMethod.getKeyDerivationMethod();
+        } catch (XMLSecurityException ex) {
+            throw new 
WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, ex);
+        }
+    }
+
     private X509Certificate[] getCertificatesFromX509Data(
         Element keyInfoChildElement,
         RequestData data
diff --git 
a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java
 
b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java
index 9779d3a1b..cbcf1ada0 100644
--- 
a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java
+++ 
b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java
@@ -23,6 +23,7 @@ import java.security.Security;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.UUID;
 
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
@@ -52,7 +53,7 @@ import org.apache.wss4j.dom.handler.WSHandlerConstants;
 import org.apache.wss4j.dom.handler.WSHandlerResult;
 import org.apache.wss4j.dom.str.STRParser.REFERENCE_TYPE;
 import org.apache.wss4j.dom.util.WSSecurityUtil;
-
+import org.apache.xml.security.encryption.params.HKDFParams;
 import org.apache.xml.security.utils.EncryptionConstants;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -106,7 +107,7 @@ public class EncryptionTest {
     }
 
     @AfterEach
-    public void cleanup(){
+    public void cleanup() {
         JDKTestUtils.unregisterAuxiliaryProvider();
     }
 
@@ -323,28 +324,38 @@ public class EncryptionTest {
 
     /**
      * Test that encrypt and decrypt a WS-Security envelope.
-     * This test uses the ECDSA-ES algorithm to (wrap) the symmetric key.
+     * This test uses the key agreement algorithm to (wrap) the symmetric key 
with generating KDF with
+     * default parameter
      * <p/>
      *
+     * @param algorithm The key type algorithm
+     * @param certAlias The certificate alias from the configuration defined 
in wss-ecdh.properties
+     * @param keyAgreementMethod The key agreement method
+     * @param kdfAlgorithm The key derivation method
      * @throws Exception Thrown when there is any problem in signing or 
verification
      */
     @ParameterizedTest
-    @CsvSource({"xdh, X25519",
-            "xdh, X448",
-            "ec, secp256r1",
-            "ec, secp384r1",
-            "ec, secp521r1",
+    @CsvSource({"xdh, X25519, http://www.w3.org/2021/04/xmldsig-more#x25519, 
http://www.w3.org/2009/xmlenc11#ConcatKDF";,
+            "xdh, X448, http://www.w3.org/2021/04/xmldsig-more#x448, 
http://www.w3.org/2009/xmlenc11#ConcatKDF";,
+            "ec, secp256r1, http://www.w3.org/2009/xmlenc11#ECDH-ES, 
http://www.w3.org/2009/xmlenc11#ConcatKDF";,
+            "ec, secp384r1, http://www.w3.org/2009/xmlenc11#ECDH-ES, 
http://www.w3.org/2009/xmlenc11#ConcatKDF";,
+            "ec, secp521r1, http://www.w3.org/2009/xmlenc11#ECDH-ES, 
http://www.w3.org/2009/xmlenc11#ConcatKDF";,
+            "xdh, X25519, http://www.w3.org/2021/04/xmldsig-more#x25519, 
http://www.w3.org/2021/04/xmldsig-more#hkdf";,
+            "xdh, X448, http://www.w3.org/2021/04/xmldsig-more#x448, 
http://www.w3.org/2021/04/xmldsig-more#hkdf";,
+            "ec, secp256r1, http://www.w3.org/2009/xmlenc11#ECDH-ES, 
http://www.w3.org/2021/04/xmldsig-more#hkdf";,
+            "ec, secp384r1, http://www.w3.org/2009/xmlenc11#ECDH-ES, 
http://www.w3.org/2021/04/xmldsig-more#hkdf";,
+            "ec, secp521r1, http://www.w3.org/2009/xmlenc11#ECDH-ES, 
http://www.w3.org/2021/04/xmldsig-more#hkdf";,
     })
-    public void testEncryptionDecryptionECDSA_ES(String algorithm, String 
certAlias) throws Exception {
+    public void testEncryptionDecryptionWithKeyAgreementAndDefaultKDF(String 
algorithm, String certAlias, String keyAgreementMethod, String kdfAlgorithm) 
throws Exception {
         try {
             if (!JDKTestUtils.isAlgorithmSupportedByJDK(algorithm)) {
-                LOG.info("Add AuxiliaryProvider to execute test with algorithm 
[{}] and cert alias [{}]", algorithm,  certAlias);
+                LOG.info("Add AuxiliaryProvider to execute test with algorithm 
[{}] and cert alias [{}]", algorithm, certAlias);
                 Security.addProvider(JDKTestUtils.getAuxiliaryProvider());
-            } else if (JDKTestUtils.getJDKVersion() == 11 && 
algorithm.equals("xdh") ) {
+            } else if (JDKTestUtils.getJDKVersion() == 11 && 
algorithm.equals("xdh")) {
                 // workaround for jdk11 and xdh keys
                 // https://bugs.openjdk.java.net/browse/JDK-8219381 or 
https://bugs.openjdk.org/browse/JDK-8213363
                 // set the auxiliary provider as first provider to parse the 
xdh private key
-                Security.insertProviderAt(JDKTestUtils.getAuxiliaryProvider(), 
1 );
+                Security.insertProviderAt(JDKTestUtils.getAuxiliaryProvider(), 
1);
             }
             Crypto encCrypto = 
CryptoFactory.getInstance("wss-ecdh.properties");
 
@@ -355,8 +366,11 @@ public class EncryptionTest {
             WSSecEncrypt builder = new WSSecEncrypt(secHeader);
             builder.setUserInfo(certAlias);
             builder.setKeyEncAlgo(WSConstants.KEYWRAP_AES128);
-            
builder.setKeyAgreementMethod(WSConstants.AGREEMENT_METHOD_ECDH_ES);
-            builder.setDigestAlgorithm(WSS4JConstants.SHA256);
+            builder.setKeyAgreementMethod(keyAgreementMethod);
+            builder.setKeyDerivationMethod(kdfAlgorithm);
+            if 
(kdfAlgorithm.equalsIgnoreCase(WSS4JConstants.KEYDERIVATION_CONCATKDF)){
+                builder.setDigestAlgorithm(WSS4JConstants.SHA256);
+            }
             builder.setKeyIdentifierType(WSConstants.SKI_KEY_IDENTIFIER);
 
             LOG.info("Before Encryption ...");
@@ -368,6 +382,7 @@ public class EncryptionTest {
 
             String outputString =
                     XMLUtils.prettyDocumentToString(encryptedDoc);
+
             if (LOG.isDebugEnabled()) {
                 LOG.debug("Encrypted message:");
                 LOG.debug(outputString);
@@ -376,7 +391,99 @@ public class EncryptionTest {
             // Check for algorithms and agreement method element
             
assertTrue(outputString.contains(EncryptionConstants._TAG_AGREEMENTMETHOD));
             assertTrue(outputString.contains(WSConstants.KEYWRAP_AES128));
-            
assertTrue(outputString.contains(WSConstants.AGREEMENT_METHOD_ECDH_ES));
+            assertTrue(outputString.contains(keyAgreementMethod));
+
+            WSSecurityEngine newEngine = new WSSecurityEngine();
+            WSHandlerResult results =
+                    newEngine.processSecurityHeader(encryptedDoc, null, 
keystoreCallbackHandler, encCrypto);
+
+            WSSecurityEngineResult actionResult =
+                    results.getActionResults().get(WSConstants.ENCR).get(0);
+            assertNotNull(actionResult);
+        } finally {
+            
Security.removeProvider(JDKTestUtils.getAuxiliaryProvider().getName());
+        }
+    }
+
+    /**
+     * Test that encrypt and decrypt a WS-Security envelope.
+     * This test uses the ECDSA-ES algorithm to (wrap) the symmetric key.
+     * <p/>
+     *
+     * @throws Exception Thrown when there is any problem in signing or 
verification
+     */
+    @ParameterizedTest
+    @CsvSource({
+            "xdh, X25519, http://www.w3.org/2021/04/xmldsig-more#x25519, 
http://www.w3.org/2001/04/xmlenc#kw-aes128, 128",
+            "xdh, X448, http://www.w3.org/2021/04/xmldsig-more#x448, 
http://www.w3.org/2001/04/xmlenc#kw-aes128, 128",
+            "ec, secp256r1, http://www.w3.org/2009/xmlenc11#ECDH-ES, 
http://www.w3.org/2001/04/xmlenc#kw-aes128, 128",
+            "ec, secp384r1, http://www.w3.org/2009/xmlenc11#ECDH-ES, 
http://www.w3.org/2001/04/xmlenc#kw-aes128, 128",
+            "ec, secp521r1, http://www.w3.org/2009/xmlenc11#ECDH-ES, 
http://www.w3.org/2001/04/xmlenc#kw-aes128, 128",
+            "xdh, X25519, http://www.w3.org/2021/04/xmldsig-more#x25519, 
http://www.w3.org/2001/04/xmlenc#kw-aes192, 192",
+            "xdh, X448, http://www.w3.org/2021/04/xmldsig-more#x448, 
http://www.w3.org/2001/04/xmlenc#kw-aes192, 192",
+            "ec, secp256r1, http://www.w3.org/2009/xmlenc11#ECDH-ES, 
http://www.w3.org/2001/04/xmlenc#kw-aes192, 192",
+            "ec, secp384r1, http://www.w3.org/2009/xmlenc11#ECDH-ES, 
http://www.w3.org/2001/04/xmlenc#kw-aes192, 192",
+            "ec, secp521r1, http://www.w3.org/2009/xmlenc11#ECDH-ES, 
http://www.w3.org/2001/04/xmlenc#kw-aes192, 192",
+            "xdh, X25519, http://www.w3.org/2021/04/xmldsig-more#x25519, 
http://www.w3.org/2001/04/xmlenc#kw-aes256, 256",
+            "xdh, X448, http://www.w3.org/2021/04/xmldsig-more#x448, 
http://www.w3.org/2001/04/xmlenc#kw-aes256, 256",
+            "ec, secp256r1, http://www.w3.org/2009/xmlenc11#ECDH-ES, 
http://www.w3.org/2001/04/xmlenc#kw-aes256, 256",
+            "ec, secp384r1, http://www.w3.org/2009/xmlenc11#ECDH-ES, 
http://www.w3.org/2001/04/xmlenc#kw-aes256, 256",
+            "ec, secp521r1, http://www.w3.org/2009/xmlenc11#ECDH-ES, 
http://www.w3.org/2001/04/xmlenc#kw-aes256, 256",
+    })
+    public void testEncryptionDecryptionWithKeyAgreementAndHKDF(String 
algorithm, String certAlias, String keyAgreementMethod, String keyWrapAlg, int 
keySize ) throws Exception {
+        String hkdfMacFunction = 
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256";;
+        try {
+            if (!JDKTestUtils.isAlgorithmSupportedByJDK(algorithm)) {
+                LOG.info("Add AuxiliaryProvider to execute test with algorithm 
[{}] and cert alias [{}]", algorithm, certAlias);
+                Security.addProvider(JDKTestUtils.getAuxiliaryProvider());
+            } else if (JDKTestUtils.getJDKVersion() == 11 && 
algorithm.equals("xdh")) {
+                // workaround for jdk11 and xdh keys
+                // https://bugs.openjdk.java.net/browse/JDK-8219381 or 
https://bugs.openjdk.org/browse/JDK-8213363
+                // set the auxiliary provider as first provider to parse the 
xdh private key
+                Security.insertProviderAt(JDKTestUtils.getAuxiliaryProvider(), 
1);
+            }
+            Crypto encCrypto = 
CryptoFactory.getInstance("wss-ecdh.properties");
+
+            Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
+            WSSecHeader secHeader = new WSSecHeader(doc);
+            secHeader.insertSecurityHeader();
+
+            HKDFParams keyDerivationParameters = 
HKDFParams.createBuilder(keySize, hkdfMacFunction)
+                    .info("test-key-info".getBytes())
+                    .salt(UUID.randomUUID().toString().getBytes())
+                    .build();
+
+            WSSecEncrypt builder = new WSSecEncrypt(secHeader);
+            builder.setUserInfo(certAlias);
+            builder.setKeyEncAlgo(keyWrapAlg);
+            builder.setKeyAgreementMethod(keyAgreementMethod);
+            builder.setKeyDerivationParameters(keyDerivationParameters);
+
+
+            LOG.info("Before Encryption ...");
+            KeyGenerator keyGen = 
KeyUtils.getKeyGenerator(WSConstants.AES_128_GCM);
+            SecretKey symmetricKey = keyGen.generateKey();
+
+            Document encryptedDoc = builder.build(encCrypto, symmetricKey);
+            LOG.info("After Encryption ....");
+
+            String outputString =
+                    XMLUtils.prettyDocumentToString(encryptedDoc);
+
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Encrypted message:");
+                LOG.debug(outputString);
+            }
+            assertFalse(outputString.contains("counter_port_type"));
+            // Check for algorithms and agreement method element
+            
assertTrue(outputString.contains(EncryptionConstants._TAG_AGREEMENTMETHOD));
+            
assertTrue(outputString.contains(EncryptionConstants._TAG_HKDFPARAMS));
+            assertTrue(outputString.contains(EncryptionConstants._TAG_INFO));
+            
assertTrue(outputString.contains(EncryptionConstants._TAG_KEYLENGTH+">"+(keySize/8)+"</"));
+            assertTrue(outputString.contains(hkdfMacFunction));
+
+            assertTrue(outputString.contains(keyWrapAlg));
+            assertTrue(outputString.contains(keyAgreementMethod));
 
             WSSecurityEngine newEngine = new WSSecurityEngine();
             WSHandlerResult results =


Reply via email to