This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch coheigea/minimum-derived-keylength in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
commit 886483fcf75eb43503ffd84e3f325dc372b21ddc Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Thu Sep 3 09:59:01 2026 +0100 Put a lower bound of 16 bytes on minimum derived key length --- .../wss4j/common/derivedKey/DerivedKeyUtils.java | 16 +++++ .../common/derivedKey/DerivedKeyUtilsTest.java | 70 ++++++++++++++++++++++ .../output/DerivedKeyTokenOutputProcessor.java | 6 +- 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/derivedKey/DerivedKeyUtils.java b/ws-security-common/src/main/java/org/apache/wss4j/common/derivedKey/DerivedKeyUtils.java index 4d1c88929..08fe625ee 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/derivedKey/DerivedKeyUtils.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/derivedKey/DerivedKeyUtils.java @@ -24,6 +24,15 @@ import org.apache.wss4j.common.ext.WSSecurityException; public final class DerivedKeyUtils { + /** + * The minimum length in bytes of a derived key (128 bits). The wsc:Length value is + * attacker-controlled message content: a shorter derived key (e.g. Length=1) reduces + * an HMAC signature key to a trivially brute-forceable keyspace. No standard + * WS-SecurityPolicy algorithm suite derives keys shorter than 128 bits, so this is + * enforced as a hard engine-level floor, independent of any configured AlgorithmSuite. + */ + public static final int MINIMUM_DERIVED_KEY_LENGTH = 16; + private DerivedKeyUtils() { // complete } @@ -55,6 +64,13 @@ public final class DerivedKeyUtils { if (keyLength <= 0) { keyLength = 32L; } + if (keyLength < MINIMUM_DERIVED_KEY_LENGTH) { + throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, + "unsupportedKeyId", + new Object[] {"Requested derived key length of " + keyLength + + " byte(s) is less than the minimum allowed (" + + MINIMUM_DERIVED_KEY_LENGTH + " bytes)"}); + } return algo.createKey(secret, seed, offset, keyLength); } } diff --git a/ws-security-common/src/test/java/org/apache/wss4j/common/derivedKey/DerivedKeyUtilsTest.java b/ws-security-common/src/test/java/org/apache/wss4j/common/derivedKey/DerivedKeyUtilsTest.java new file mode 100644 index 000000000..bf50941bd --- /dev/null +++ b/ws-security-common/src/test/java/org/apache/wss4j/common/derivedKey/DerivedKeyUtilsTest.java @@ -0,0 +1,70 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.wss4j.common.derivedKey; + +import org.apache.wss4j.common.ext.WSSecurityException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class DerivedKeyUtilsTest { + + private static final byte[] SECRET = "0123456789abcdef".getBytes(java.nio.charset.StandardCharsets.UTF_8); + private static final byte[] NONCE = "abcdefghijklmnop".getBytes(java.nio.charset.StandardCharsets.UTF_8); + + @Test + void rejectsPositiveDerivedKeyLengthsBelowMinimum() { + WSSecurityException exception = Assertions.assertThrows(WSSecurityException.class, + () -> DerivedKeyUtils.deriveKey( + ConversationConstants.DerivationAlgorithm.P_SHA_1, + null, + DerivedKeyUtils.MINIMUM_DERIVED_KEY_LENGTH - 1, + SECRET, + NONCE, + 0)); + + Assertions.assertEquals(WSSecurityException.ErrorCode.INVALID_SECURITY, exception.getErrorCode()); + } + + @Test + void acceptsMinimumDerivedKeyLength() throws Exception { + byte[] key = DerivedKeyUtils.deriveKey( + ConversationConstants.DerivationAlgorithm.P_SHA_1, + null, + DerivedKeyUtils.MINIMUM_DERIVED_KEY_LENGTH, + SECRET, + NONCE, + 0); + + Assertions.assertEquals(DerivedKeyUtils.MINIMUM_DERIVED_KEY_LENGTH, key.length); + } + + @Test + void preservesDefaultDerivedKeyLength() throws Exception { + byte[] key = DerivedKeyUtils.deriveKey( + ConversationConstants.DerivationAlgorithm.P_SHA_1, + null, + 0, + SECRET, + NONCE, + 0); + + Assertions.assertEquals(32, key.length); + } +} diff --git a/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/output/DerivedKeyTokenOutputProcessor.java b/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/output/DerivedKeyTokenOutputProcessor.java index a0fe52dff..929809569 100644 --- a/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/output/DerivedKeyTokenOutputProcessor.java +++ b/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/output/DerivedKeyTokenOutputProcessor.java @@ -88,7 +88,8 @@ public class DerivedKeyTokenOutputProcessor extends AbstractOutputProcessor { } else { length = JCEAlgorithmMapper.getKeyLengthFromURI(getSecurityProperties().getSignatureAlgorithm()) / 8; if (length == 0) { - length = KeyUtils.getKeyLength(getSecurityProperties().getSignatureAlgorithm()) / 8; + // KeyUtils.getKeyLength already returns a length in bytes + length = KeyUtils.getKeyLength(getSecurityProperties().getSignatureAlgorithm()); } } } else if (WSSConstants.ENCRYPTION_WITH_DERIVED_KEY.equals(action)) { @@ -97,7 +98,8 @@ public class DerivedKeyTokenOutputProcessor extends AbstractOutputProcessor { } else { length = JCEAlgorithmMapper.getKeyLengthFromURI(getSecurityProperties().getEncryptionSymAlgorithm()) / 8; if (length == 0) { - length = KeyUtils.getKeyLength(getSecurityProperties().getEncryptionSymAlgorithm()) / 8; + // KeyUtils.getKeyLength already returns a length in bytes + length = KeyUtils.getKeyLength(getSecurityProperties().getEncryptionSymAlgorithm()); } } }
