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


The following commit(s) were added to refs/heads/3_0_x-fixes by this push:
     new ff333d734 Fix regression with KeyUtils
ff333d734 is described below

commit ff333d7346f961f9f442ab9b3ea4cfc623e9ed79
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Tue Sep 22 16:00:10 2026 +0100

    Fix regression with KeyUtils
---
 .../org/apache/wss4j/common/util/KeyUtils.java     | 35 ++++++++++++++++---
 .../org/apache/wss4j/common/util/KeyUtilsTest.java | 39 +++++++++++++++++++---
 .../apache/wss4j/dom/action/EncryptionAction.java  |  2 +-
 .../wss4j/dom/message/WSSecDerivedKeyBase.java     |  2 +-
 .../apache/wss4j/dom/message/WSSecSignature.java   |  2 +-
 .../dom/processor/EncryptedDataProcessor.java      |  2 +-
 .../wss4j/dom/processor/EncryptedKeyProcessor.java |  4 +--
 .../dom/processor/ReferenceListProcessor.java      |  4 +--
 .../wss4j/dom/processor/SignatureProcessor.java    |  2 +-
 .../apache/wss4j/dom/saml/WSSecSignatureSAML.java  |  2 +-
 .../securityToken/KerberosClientSecurityToken.java |  2 +-
 .../KerberosServiceSecurityTokenImpl.java          |  2 +-
 12 files changed, 76 insertions(+), 22 deletions(-)

diff --git 
a/ws-security-common/src/main/java/org/apache/wss4j/common/util/KeyUtils.java 
b/ws-security-common/src/main/java/org/apache/wss4j/common/util/KeyUtils.java
index dd2eba7bd..3960cf577 100644
--- 
a/ws-security-common/src/main/java/org/apache/wss4j/common/util/KeyUtils.java
+++ 
b/ws-security-common/src/main/java/org/apache/wss4j/common/util/KeyUtils.java
@@ -87,10 +87,33 @@ public final class KeyUtils {
     /**
      * Convert the raw key bytes into a SecretKey object of type algorithm.
      *
-     * @throws WSSecurityException if the raw key length does not match the 
key length required by
-     *         the algorithm or exceeds maximum allowed size
+     * A mismatch between the length of the key and the length required by the 
algorithm is logged,
+     * but not rejected, as the given algorithm is not necessarily the 
algorithm that the key is
+     * going to be used with. This is the case for example when a key is 
created only to be wrapped
+     * in an EncryptedKey structure and handed over to a third party, as done 
by a STS. Use
+     * {@link #prepareSecretKey(String, byte[], boolean)} with 
"strictKeyLengthCheck" set to true
+     * when the key is about to be used with the given algorithm.
+     *
+     * @throws WSSecurityException if the raw key is null or exceeds the 
maximum allowed size
      */
     public static SecretKey prepareSecretKey(String algorithm, byte[] rawKey) 
throws WSSecurityException {
+        return prepareSecretKey(algorithm, rawKey, false);
+    }
+
+    /**
+     * Convert the raw key bytes into a SecretKey object of type algorithm.
+     *
+     * @param algorithm the URI of the algorithm the key is associated with
+     * @param rawKey the raw key bytes
+     * @param strictKeyLengthCheck whether to reject a key whose length does 
not match the length
+     *        required by the algorithm. This must be set to true when the key 
is going to be used
+     *        with the given algorithm, so that key material is never 
truncated or re-used across
+     *        algorithms.
+     * @throws WSSecurityException if the raw key is null, exceeds the maximum 
allowed size, or (if
+     *         strictKeyLengthCheck is true) does not match the key length 
required by the algorithm
+     */
+    public static SecretKey prepareSecretKey(String algorithm, byte[] rawKey, 
boolean strictKeyLengthCheck)
+        throws WSSecurityException {
         if (rawKey == null) {
             throw new 
WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
         }
@@ -113,12 +136,14 @@ public final class KeyUtils {
         String keyAlgorithm = JCEMapper.getJCEKeyAlgorithmFromURI(algorithm);
 
         // For fixed-length symmetric ciphers (e.g. AES-CBC, AES-GCM, 3DES, 
AES KeyWrap),
-        // strictly verify that the provided key length matches the declared 
algorithm's key length.
-        // Refuse to truncate or mismatch key material to prevent 
cross-algorithm key-reuse attacks.
+        // verify that the provided key length matches the declared 
algorithm's key length.
+        // Never truncate the key material, as this enables cross-algorithm 
key-reuse attacks.
         if (size > 0 && (algorithm == null || !algorithm.contains("hmac-")) && 
rawKey.length != size) {
             LOG.warn("The provided key has a length of {} bytes, which does 
not match the length of"
                 + " {} bytes required by {}", rawKey.length, size, algorithm);
-            throw new 
WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
+            if (strictKeyLengthCheck) {
+                throw new 
WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
+            }
         }
 
         return new SecretKeySpec(rawKey, keyAlgorithm);
diff --git 
a/ws-security-common/src/test/java/org/apache/wss4j/common/util/KeyUtilsTest.java
 
b/ws-security-common/src/test/java/org/apache/wss4j/common/util/KeyUtilsTest.java
index b938f2c7a..6ff938625 100644
--- 
a/ws-security-common/src/test/java/org/apache/wss4j/common/util/KeyUtilsTest.java
+++ 
b/ws-security-common/src/test/java/org/apache/wss4j/common/util/KeyUtilsTest.java
@@ -41,7 +41,7 @@ class KeyUtilsTest {
         byte[] rawKey = new byte[32];
 
         WSSecurityException exception = 
Assertions.assertThrows(WSSecurityException.class,
-            () -> KeyUtils.prepareSecretKey(WSS4JConstants.AES_128, rawKey));
+            () -> KeyUtils.prepareSecretKey(WSS4JConstants.AES_128, rawKey, 
true));
 
         
Assertions.assertEquals(WSSecurityException.ErrorCode.INVALID_SECURITY, 
exception.getErrorCode());
     }
@@ -51,7 +51,7 @@ class KeyUtilsTest {
         byte[] rawKey = new byte[8];
 
         WSSecurityException exception = 
Assertions.assertThrows(WSSecurityException.class,
-            () -> KeyUtils.prepareSecretKey(WSS4JConstants.AES_128, rawKey));
+            () -> KeyUtils.prepareSecretKey(WSS4JConstants.AES_128, rawKey, 
true));
 
         
Assertions.assertEquals(WSSecurityException.ErrorCode.INVALID_SECURITY, 
exception.getErrorCode());
     }
@@ -70,7 +70,7 @@ class KeyUtilsTest {
         byte[] rawKey = new byte[32];
 
         WSSecurityException exception = 
Assertions.assertThrows(WSSecurityException.class,
-            () -> KeyUtils.prepareSecretKey(WSS4JConstants.AES_128_GCM, 
rawKey));
+            () -> KeyUtils.prepareSecretKey(WSS4JConstants.AES_128_GCM, 
rawKey, true));
 
         
Assertions.assertEquals(WSSecurityException.ErrorCode.INVALID_SECURITY, 
exception.getErrorCode());
     }
@@ -80,7 +80,7 @@ class KeyUtilsTest {
         byte[] rawKey = new byte[8];
 
         WSSecurityException exception = 
Assertions.assertThrows(WSSecurityException.class,
-            () -> KeyUtils.prepareSecretKey(WSS4JConstants.AES_128_GCM, 
rawKey));
+            () -> KeyUtils.prepareSecretKey(WSS4JConstants.AES_128_GCM, 
rawKey, true));
 
         
Assertions.assertEquals(WSSecurityException.ErrorCode.INVALID_SECURITY, 
exception.getErrorCode());
     }
@@ -123,4 +123,33 @@ class KeyUtilsTest {
 
         
Assertions.assertEquals(WSSecurityException.ErrorCode.INVALID_SECURITY, 
exception.getErrorCode());
     }
-}
\ No newline at end of file
+
+    @Test
+    void allowsMismatchedKeyLengthWhenNotStrict() throws Exception {
+        byte[] rawKey = new byte[16];
+
+        SecretKey secretKey = 
KeyUtils.prepareSecretKey(WSS4JConstants.AES_256, rawKey);
+
+        // The key is not truncated or padded, just wrapped as it is
+        Assertions.assertArrayEquals(rawKey, secretKey.getEncoded());
+    }
+
+    @Test
+    void doesNotTruncateOversizedKeyWhenNotStrict() throws Exception {
+        byte[] rawKey = new byte[32];
+
+        SecretKey secretKey = 
KeyUtils.prepareSecretKey(WSS4JConstants.AES_128, rawKey);
+
+        Assertions.assertArrayEquals(rawKey, secretKey.getEncoded());
+    }
+
+    @Test
+    void rejectsOversizedKeyWhenNotStrict() {
+        byte[] rawKey = new byte[1025];
+
+        WSSecurityException exception = 
Assertions.assertThrows(WSSecurityException.class,
+            () -> KeyUtils.prepareSecretKey(WSS4JConstants.AES_128, rawKey));
+
+        
Assertions.assertEquals(WSSecurityException.ErrorCode.INVALID_SECURITY, 
exception.getErrorCode());
+    }
+}
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 7bc498714..00875af2a 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
@@ -128,7 +128,7 @@ public class EncryptionAction implements Action {
 
         SecretKey symmetricKey;
         if (ephemeralKey != null) {
-            symmetricKey = 
KeyUtils.prepareSecretKey(wsEncrypt.getSymmetricEncAlgorithm(), ephemeralKey);
+            symmetricKey = 
KeyUtils.prepareSecretKey(wsEncrypt.getSymmetricEncAlgorithm(), ephemeralKey, 
true);
         } else {
             KeyGenerator keyGen = 
KeyUtils.getKeyGenerator(wsEncrypt.getSymmetricEncAlgorithm());
             symmetricKey = keyGen.generateKey();
diff --git 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecDerivedKeyBase.java
 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecDerivedKeyBase.java
index 10334dc20..ebf8bfa66 100644
--- 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecDerivedKeyBase.java
+++ 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecDerivedKeyBase.java
@@ -372,7 +372,7 @@ public abstract class WSSecDerivedKeyBase extends 
WSSecSignatureBase {
     }
 
     protected SecretKey getDerivedKey(String algorithm) throws 
WSSecurityException {
-        return KeyUtils.prepareSecretKey(algorithm, derivedKeyBytes);
+        return KeyUtils.prepareSecretKey(algorithm, derivedKeyBytes, true);
     }
 
     @Override
diff --git 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java
 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java
index e7384610d..11d38cb9d 100644
--- 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java
+++ 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java
@@ -576,7 +576,7 @@ public class WSSecSignature extends WSSecSignatureBase {
             if (secretKey == null) {
                 key = crypto.getPrivateKey(user, password);
             } else {
-                key = KeyUtils.prepareSecretKey(sigAlgo, secretKey);
+                key = KeyUtils.prepareSecretKey(sigAlgo, secretKey, true);
             }
             SignatureMethod signatureMethod =
                 signatureFactory.newSignatureMethod(sigAlgo, null);
diff --git 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/EncryptedDataProcessor.java
 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/EncryptedDataProcessor.java
index 3f6d6e317..aa41e7401 100644
--- 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/EncryptedDataProcessor.java
+++ 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/EncryptedDataProcessor.java
@@ -117,7 +117,7 @@ public class EncryptedDataProcessor implements Processor {
             principal = parserResult.getPrincipal();
             key = parserResult.isSecretKeyFromEncryptedKey()
                 ? 
EncryptedKeyProcessor.prepareSecretKeyFromEncryptedKey(symEncAlgo, secretKey)
-                : KeyUtils.prepareSecretKey(symEncAlgo, secretKey);
+                : KeyUtils.prepareSecretKey(symEncAlgo, secretKey, true);
             encrKeyResults = new ArrayList<>();
         } else if (encryptedKeyElement != null && data.getWssConfig() != null) 
{
             WSSConfig wssConfig = data.getWssConfig();
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 3fba62952..1e0a89a69 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
@@ -538,10 +538,10 @@ public class EncryptedKeyProcessor implements Processor {
     static SecretKey prepareSecretKeyFromEncryptedKey(String symEncAlgo, 
byte[] secretKey)
         throws WSSecurityException {
         try {
-            return KeyUtils.prepareSecretKey(symEncAlgo, secretKey);
+            return KeyUtils.prepareSecretKey(symEncAlgo, secretKey, true);
         } catch (WSSecurityException ex) {
             LOG.debug("The key recovered from the EncryptedKey does not match 
{}", symEncAlgo);
-            return KeyUtils.prepareSecretKey(symEncAlgo, 
getRandomKey(symEncAlgo));
+            return KeyUtils.prepareSecretKey(symEncAlgo, 
getRandomKey(symEncAlgo), true);
         }
     }
 
diff --git 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/ReferenceListProcessor.java
 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/ReferenceListProcessor.java
index a431205bb..d17cc1955 100644
--- 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/ReferenceListProcessor.java
+++ 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/ReferenceListProcessor.java
@@ -154,7 +154,7 @@ public class ReferenceListProcessor implements Processor {
         if (secRefToken == null) {
             byte[] decryptedData =
                 X509Util.getSecretKey(keyInfoElement, symEncAlgo, 
data.getCallbackHandler());
-            symmetricKey = KeyUtils.prepareSecretKey(symEncAlgo, 
decryptedData);
+            symmetricKey = KeyUtils.prepareSecretKey(symEncAlgo, 
decryptedData, true);
         } else {
             STRParserParameters parameters = new STRParserParameters();
             parameters.setData(data);
@@ -169,7 +169,7 @@ public class ReferenceListProcessor implements Processor {
             principal = parserResult.getPrincipal();
             symmetricKey = parserResult.isSecretKeyFromEncryptedKey()
                 ? 
EncryptedKeyProcessor.prepareSecretKeyFromEncryptedKey(symEncAlgo, secretKey)
-                : KeyUtils.prepareSecretKey(symEncAlgo, secretKey);
+                : KeyUtils.prepareSecretKey(symEncAlgo, secretKey, true);
         }
 
         // Check for compliance against the defined AlgorithmSuite
diff --git 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/SignatureProcessor.java
 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/SignatureProcessor.java
index 8db6097e7..33d82ae09 100644
--- 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/SignatureProcessor.java
+++ 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/SignatureProcessor.java
@@ -362,7 +362,7 @@ public class SignatureProcessor implements Processor {
         } else if (publicKey != null) {
             key = publicKey;
         } else {
-            key = KeyUtils.prepareSecretKey(signatureMethod, secretKey);
+            key = KeyUtils.prepareSecretKey(signatureMethod, secretKey, true);
         }
 
         if (data.isExpandXopInclude()) {
diff --git 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/WSSecSignatureSAML.java
 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/WSSecSignatureSAML.java
index b1e14bf89..21ea606f5 100644
--- 
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/WSSecSignatureSAML.java
+++ 
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/WSSecSignatureSAML.java
@@ -491,7 +491,7 @@ public class WSSecSignatureSAML extends WSSecSignature {
             if (senderVouches) {
                 key = issuerCrypto.getPrivateKey(issuerKeyName, issuerKeyPW);
             } else if (secretKey != null) {
-                key = KeyUtils.prepareSecretKey(getSignatureAlgorithm(), 
secretKey);
+                key = KeyUtils.prepareSecretKey(getSignatureAlgorithm(), 
secretKey, true);
             } else {
                 key = userCrypto.getPrivateKey(user, password);
             }
diff --git 
a/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/securityToken/KerberosClientSecurityToken.java
 
b/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/securityToken/KerberosClientSecurityToken.java
index 053533387..e44475100 100644
--- 
a/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/securityToken/KerberosClientSecurityToken.java
+++ 
b/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/securityToken/KerberosClientSecurityToken.java
@@ -152,7 +152,7 @@ public class KerberosClientSecurityToken extends 
GenericOutboundSecurityToken {
 
         byte[] sk = this.secretKey.getEncoded();
 
-        key = KeyUtils.prepareSecretKey(algorithmURI, sk);
+        key = KeyUtils.prepareSecretKey(algorithmURI, sk, true);
         setSecretKey(algorithmURI, key);
         return key;
     }
diff --git 
a/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/securityToken/KerberosServiceSecurityTokenImpl.java
 
b/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/securityToken/KerberosServiceSecurityTokenImpl.java
index 0794111f0..b01e5da6e 100644
--- 
a/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/securityToken/KerberosServiceSecurityTokenImpl.java
+++ 
b/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/securityToken/KerberosServiceSecurityTokenImpl.java
@@ -160,7 +160,7 @@ public class KerberosServiceSecurityTokenImpl extends 
AbstractInboundSecurityTok
 
         byte[] sk = getTGTSessionKey();
 
-        key = KeyUtils.prepareSecretKey(algorithmURI, sk);
+        key = KeyUtils.prepareSecretKey(algorithmURI, sk, true);
         setSecretKey(algorithmURI, key);
         return key;
     }

Reply via email to