This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch 2_4_x-fixes
in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
The following commit(s) were added to refs/heads/2_4_x-fixes by this push:
new 1c2127ed2 Fix regression with KeyUtils
1c2127ed2 is described below
commit 1c2127ed2517bf7793e9aa183b8ba7dbd01faa76
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 c99c67849..bda4493c4 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 2e61dd080..c3bdb5e4a 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
@@ -114,7 +114,7 @@ public class EncryptionAction implements Action {
SecretKey symmetricKey = null;
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 a2ec47df0..350fed80b 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
@@ -370,7 +370,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 b296ff901..75530c825 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
@@ -574,7 +574,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 223a90617..0adebae9e 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
@@ -403,10 +403,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 e818e4e07..cad277135 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 d286fce88..3afbc4162 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 d110fcc05..aa75a2617 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
@@ -492,7 +492,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;
}