This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push: new 4aae5a86ad Switch strong ETag generation from SHA-1 to SHA-256 4aae5a86ad is described below commit 4aae5a86adb9c5f1c61375de40736b1080d9e9fd Author: Mark Thomas <ma...@apache.org> AuthorDate: Tue Sep 2 17:00:43 2025 +0100 Switch strong ETag generation from SHA-1 to SHA-256 --- java/org/apache/catalina/webresources/AbstractResource.java | 4 ++-- java/org/apache/catalina/webresources/CachedResource.java | 2 +- .../apache/tomcat/util/security/ConcurrentMessageDigest.java | 10 ++++++++++ .../catalina/servlets/TestDefaultServletIfMatchRequests.java | 4 ++-- .../catalina/servlets/TestDefaultServletRangeRequests.java | 4 ++-- test/org/apache/catalina/servlets/TestWebdavServlet.java | 8 ++++++-- webapps/docs/changelog.xml | 10 ++++++++++ 7 files changed, 33 insertions(+), 9 deletions(-) diff --git a/java/org/apache/catalina/webresources/AbstractResource.java b/java/org/apache/catalina/webresources/AbstractResource.java index 83109274ad..bfc543a52e 100644 --- a/java/org/apache/catalina/webresources/AbstractResource.java +++ b/java/org/apache/catalina/webresources/AbstractResource.java @@ -90,7 +90,7 @@ public abstract class AbstractResource implements WebResource { if (contentLength <= 16 * 1024) { byte[] buf = getContent(); if (buf != null) { - buf = ConcurrentMessageDigest.digest("SHA-1", buf); + buf = ConcurrentMessageDigest.digestSHA256(buf); strongETag = "\"" + HexUtils.toHexString(buf) + "\""; } else { strongETag = getETag(); @@ -98,7 +98,7 @@ public abstract class AbstractResource implements WebResource { } else { byte[] buf = new byte[4096]; try (InputStream is = getInputStream()) { - MessageDigest digest = MessageDigest.getInstance("SHA-1"); + MessageDigest digest = MessageDigest.getInstance("SHA-256"); while (true) { int n = is.read(buf); if (n <= 0) { diff --git a/java/org/apache/catalina/webresources/CachedResource.java b/java/org/apache/catalina/webresources/CachedResource.java index 72bd60e967..872bfc1db8 100644 --- a/java/org/apache/catalina/webresources/CachedResource.java +++ b/java/org/apache/catalina/webresources/CachedResource.java @@ -295,7 +295,7 @@ public class CachedResource implements WebResource { if (cachedStrongETag == null) { byte[] buf = getContent(); if (buf != null) { - buf = ConcurrentMessageDigest.digest("SHA-1", buf); + buf = ConcurrentMessageDigest.digestSHA256(buf); cachedStrongETag = "\"" + HexUtils.toHexString(buf) + "\""; } else { cachedStrongETag = webResource.getStrongETag(); diff --git a/java/org/apache/tomcat/util/security/ConcurrentMessageDigest.java b/java/org/apache/tomcat/util/security/ConcurrentMessageDigest.java index 9404f55044..b4d9cf3f67 100644 --- a/java/org/apache/tomcat/util/security/ConcurrentMessageDigest.java +++ b/java/org/apache/tomcat/util/security/ConcurrentMessageDigest.java @@ -38,6 +38,7 @@ public class ConcurrentMessageDigest { private static final String MD5 = "MD5"; private static final String SHA1 = "SHA-1"; + private static final String SHA256 = "SHA-256"; private static final Map<String,Queue<MessageDigest>> queues = new ConcurrentHashMap<>(); @@ -58,6 +59,11 @@ public class ConcurrentMessageDigest { } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException(sm.getString("concurrentMessageDigest.noDigest"), e); } + try { + init(SHA256); + } catch (NoSuchAlgorithmException e) { + throw new IllegalArgumentException(sm.getString("concurrentMessageDigest.noDigest"), e); + } } public static byte[] digestMD5(byte[]... input) { @@ -68,6 +74,10 @@ public class ConcurrentMessageDigest { return digest(SHA1, input); } + public static byte[] digestSHA256(byte[]... input) { + return digest(SHA256, input); + } + public static byte[] digest(String algorithm, byte[]... input) { return digest(algorithm, 1, input); } diff --git a/test/org/apache/catalina/servlets/TestDefaultServletIfMatchRequests.java b/test/org/apache/catalina/servlets/TestDefaultServletIfMatchRequests.java index 1df310e6b0..bcfa359388 100644 --- a/test/org/apache/catalina/servlets/TestDefaultServletIfMatchRequests.java +++ b/test/org/apache/catalina/servlets/TestDefaultServletIfMatchRequests.java @@ -19,7 +19,6 @@ package org.apache.catalina.servlets; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; -import java.security.MessageDigest; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -40,6 +39,7 @@ import org.apache.catalina.startup.TomcatBaseTest; import org.apache.catalina.util.IOTools; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.buf.HexUtils; +import org.apache.tomcat.util.security.ConcurrentMessageDigest; @RunWith(Parameterized.class) public class TestDefaultServletIfMatchRequests extends TomcatBaseTest { @@ -62,7 +62,7 @@ public class TestDefaultServletIfMatchRequests extends TomcatBaseTest { try (FileInputStream is = new FileInputStream(index)) { ByteArrayOutputStream os = new ByteArrayOutputStream(); IOTools.flow(is, os); - resourceETagStrong = "\"" + HexUtils.toHexString(MessageDigest.getInstance("SHA-1").digest(os.toByteArray())) + "\""; + resourceETagStrong = "\"" + HexUtils.toHexString(ConcurrentMessageDigest.digestSHA256(os.toByteArray())) + "\""; } catch (Exception e) { } resourceETagWeak = "W/" + "\"" + index.length() + "-" + index.lastModified() + "\""; diff --git a/test/org/apache/catalina/servlets/TestDefaultServletRangeRequests.java b/test/org/apache/catalina/servlets/TestDefaultServletRangeRequests.java index e59534b5f9..60b1aec9bf 100644 --- a/test/org/apache/catalina/servlets/TestDefaultServletRangeRequests.java +++ b/test/org/apache/catalina/servlets/TestDefaultServletRangeRequests.java @@ -19,7 +19,6 @@ package org.apache.catalina.servlets; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; -import java.security.MessageDigest; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -40,6 +39,7 @@ import org.apache.catalina.util.IOTools; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.buf.HexUtils; import org.apache.tomcat.util.http.FastHttpDateFormat; +import org.apache.tomcat.util.security.ConcurrentMessageDigest; @RunWith(Parameterized.class) public class TestDefaultServletRangeRequests extends TomcatBaseTest { @@ -58,7 +58,7 @@ public class TestDefaultServletRangeRequests extends TomcatBaseTest { try (FileInputStream is = new FileInputStream(index)) { ByteArrayOutputStream os = new ByteArrayOutputStream(); IOTools.flow(is, os); - strongETag = "\"" + HexUtils.toHexString(MessageDigest.getInstance("SHA-1").digest(os.toByteArray())) + "\""; + strongETag = "\"" + HexUtils.toHexString(ConcurrentMessageDigest.digestSHA256(os.toByteArray())) + "\""; } catch (Exception e) { } diff --git a/test/org/apache/catalina/servlets/TestWebdavServlet.java b/test/org/apache/catalina/servlets/TestWebdavServlet.java index 0cd5e51ffb..233b4f8538 100644 --- a/test/org/apache/catalina/servlets/TestWebdavServlet.java +++ b/test/org/apache/catalina/servlets/TestWebdavServlet.java @@ -851,8 +851,12 @@ public class TestWebdavServlet extends TomcatBaseTest { Assert.assertFalse(client.getResponseBody().contains("/myfolder/file4.txt")); Assert.assertTrue(client.getResponseBody().contains("/file7.txt")); Assert.assertTrue(client.getResponseBody().contains("Second-")); - Assert.assertTrue(client.getResponseBody().contains("d1dc021f456864e84f9a37b7a6f51c51301128a0")); - Assert.assertTrue(client.getResponseBody().contains("f3390fe2e5546dac3d1968970df1a222a3a39c00")); + // SHA-256 hash for "FOOBAR...FOOBAR" (repeats 3000 times) + Assert.assertTrue(client.getResponseBody().contains( + "bb94e8d310800b24310036b168aa5a946e27f9572b3d99f956f3a3ed2e7d3045")); + // SHA-256 hash for "FOOBAR" + Assert.assertTrue(client.getResponseBody().contains( + "24c422e681f1c1bd08286c7aaf5d23a5f088dcdb0b219806b3a9e579244f00c5")); String timeoutValue = client.getResponseBody().substring(client.getResponseBody().indexOf("Second-")); timeoutValue = timeoutValue.substring("Second-".length(), timeoutValue.indexOf('<')); Assert.assertTrue(Integer.valueOf(timeoutValue).intValue() <= 20); diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index ebf80f71dc..a98091bf09 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -105,6 +105,16 @@ issues do not "pop up" wrt. others). --> <section name="Tomcat 9.0.110 (remm)" rtext="in development"> + <subsection name="Catlaina"> + <changelog> + <update> + Change the digest used to calculate strong ETags (if enabled) for the + default Servlet from SHA-1 to SHA-256 to align with the recommendation + in RFC 9110 that hash functions used to generate strong ETags should be + collision resistant. (markt) + </update> + </changelog> + </subsection> <subsection name="Coyote"> <changelog> <update> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org