This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch 3.6.x-fixes in repository https://gitbox.apache.org/repos/asf/cxf.git
commit 7cec2100b13ae53098c7e21321426410e560bcc9 Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Tue Sep 1 08:27:49 2026 +0100 Use a properly random source to generate digest client nonces (#3423) (cherry picked from commit b0196449b3561b29924b219ed331a8e85f237f73) --- .../apache/cxf/transport/http/auth/DigestAuthSupplier.java | 12 +++++++++++- .../cxf/transport/http/auth/DigestAuthSupplierTest.java | 14 ++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/DigestAuthSupplier.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/DigestAuthSupplier.java index b076ae3fd7b..6ab3931f90d 100644 --- a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/DigestAuthSupplier.java +++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/DigestAuthSupplier.java @@ -22,6 +22,7 @@ package org.apache.cxf.transport.http.auth; import java.net.URI; import java.nio.ByteBuffer; import java.security.MessageDigest; +import java.security.SecureRandom; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -36,6 +37,7 @@ import static java.nio.charset.StandardCharsets.US_ASCII; * */ public class DigestAuthSupplier implements HttpAuthSupplier { + private static final SecureRandom CNONCE_GENERATOR = new SecureRandom(); Map<URI, DigestInfo> authInfo = new ConcurrentHashMap<>(); @@ -107,8 +109,16 @@ public class DigestAuthSupplier implements HttpAuthSupplier { return authURI; } + /** + * Creates the client nonce. RFC 7616 relies on the cnonce being unpredictable so + * that a hostile or spoofed server controlling the challenge nonce cannot steer + * the client into computing a digest over fully attacker-chosen input; a + * timestamp is guessable and must not be used here. + */ public String createCnonce() { - return Long.toString(System.currentTimeMillis()); + byte[] bytes = new byte[16]; + CNONCE_GENERATOR.nextBytes(bytes); + return StringUtils.toHexString(bytes); } class DigestInfo { diff --git a/rt/transports/http/src/test/java/org/apache/cxf/transport/http/auth/DigestAuthSupplierTest.java b/rt/transports/http/src/test/java/org/apache/cxf/transport/http/auth/DigestAuthSupplierTest.java index 127b88b8856..81e20e4c878 100644 --- a/rt/transports/http/src/test/java/org/apache/cxf/transport/http/auth/DigestAuthSupplierTest.java +++ b/rt/transports/http/src/test/java/org/apache/cxf/transport/http/auth/DigestAuthSupplierTest.java @@ -29,6 +29,7 @@ import org.apache.cxf.message.MessageImpl; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; public class DigestAuthSupplierTest { @@ -92,6 +93,19 @@ public class DigestAuthSupplierTest { assertEquals(expectedParams, params); } + @Test + public void testCnonceIsUnpredictable() throws Exception { + DigestAuthSupplier authSupplier = new DigestAuthSupplier(); + String cnonce1 = authSupplier.createCnonce(); + String cnonce2 = authSupplier.createCnonce(); + // 16 random bytes, hex encoded + assertEquals(32, cnonce1.length()); + assertTrue(cnonce1.matches("[0-9a-fA-F]+")); + assertNotEquals(cnonce1, cnonce2); + // must not be an epoch-millis timestamp + assertNotEquals(Long.toString(System.currentTimeMillis()).length(), cnonce1.length()); + } + @Test public void testUrlEncodedUri() throws Exception { AuthorizationPolicy authPolicy = new AuthorizationPolicy();
