You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ds...@apache.org on 2026/03/29 12:23:49 UTC

(tomcat) branch 11.0.x updated (301bc6efbf -> 541192875d)

This is an automated email from the ASF dual-hosted git repository.

dsoumis pushed a change to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


    from 301bc6efbf Reduce log verbosity for Kubernetes
     new 1ad0a9c0e8 Fix naming convention of performance tests in BUILDING.txt
     new ebef81c638 Add TestSSLAuthenticator
     new 49e613545a Add TestValidateClientSessionId
     new 196e3a2acd Add generateKeystore method in TesterSupport which utilizes BouncyCastle to generate a temporary JKS keystore containing a self-signed RSA certificate.
     new c2bd7fa98d Add TestLargeClientHello and add a debug log in SecureNioChannel when BUFFER_UNDERFLOW occurs during handshake
     new d69712fe91 Move generateKeystore() from TesterSupport to independent TesterKeystoreGenerator class
     new 541192875d Add support for NIO2 connector in TestLargeClientHello

The 7 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 BUILDING.txt                                       |   2 +-
 .../apache/tomcat/util/net/LocalStrings.properties |   1 +
 .../apache/tomcat/util/net/SecureNio2Channel.java  |   3 +
 .../apache/tomcat/util/net/SecureNioChannel.java   |   3 +
 .../authenticator/TestSSLAuthenticator.java}       |  21 +++--
 .../connector/TestValidateClientSessionId.java     |  89 ++++++++++++++++++
 .../tomcat/util/net/TestLargeClientHello.java      |  91 ++++++++++++++++++
 .../tomcat/util/net/TesterKeystoreGenerator.java   | 103 +++++++++++++++++++++
 8 files changed, 303 insertions(+), 10 deletions(-)
 copy test/org/apache/{jasper/compiler/TestNodeIntegration.java => catalina/authenticator/TestSSLAuthenticator.java} (62%)
 create mode 100644 test/org/apache/catalina/connector/TestValidateClientSessionId.java
 create mode 100644 test/org/apache/tomcat/util/net/TestLargeClientHello.java
 create mode 100644 test/org/apache/tomcat/util/net/TesterKeystoreGenerator.java


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


(tomcat) 03/07: Add TestValidateClientSessionId

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsoumis pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 49e613545af3d4c5f30db29a2b32b97b77b99cea
Author: Dimitris Soumis <ji...@gmail.com>
AuthorDate: Tue Mar 24 14:53:23 2026 +0200

    Add TestValidateClientSessionId
---
 .../connector/TestValidateClientSessionId.java     | 89 ++++++++++++++++++++++
 1 file changed, 89 insertions(+)

diff --git a/test/org/apache/catalina/connector/TestValidateClientSessionId.java b/test/org/apache/catalina/connector/TestValidateClientSessionId.java
new file mode 100644
index 0000000000..dc94fc3fc3
--- /dev/null
+++ b/test/org/apache/catalina/connector/TestValidateClientSessionId.java
@@ -0,0 +1,89 @@
+/*
+ * 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.catalina.connector;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+public class TestValidateClientSessionId extends TomcatBaseTest {
+
+    @Test
+    public void testMaliciousSessionIdRejected() throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        Context ctx = getProgrammaticRootContext();
+        Tomcat.addServlet(ctx, "snoop", new SnoopServlet());
+        ctx.addServletMappingDecoded("/", "snoop");
+
+        tomcat.start();
+
+        Map<String, List<String>> reqHead = new HashMap<>();
+        reqHead.put("Cookie", List.of("JSESSIONID=DUMMY_SESSION_ID"));
+
+        ByteChunk res = new ByteChunk();
+        getUrl("http://localhost:" + getPort() + "/?createSession=true", res, reqHead, null);
+
+        RequestDescriptor requestDesc = SnoopResult.parse(res.toString());
+
+        String actualSessionId = requestDesc.getRequestInfo("SESSION-ID");
+        Assert.assertNotEquals("DUMMY_SESSION_ID", actualSessionId);
+    }
+
+    @Test
+    public void testValidSessionIdAcceptedAcrossContexts() throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        Context ctx1 = tomcat.addContext("/app1", null);
+        ctx1.setSessionCookiePath("/");
+        Tomcat.addServlet(ctx1, "snoop", new SnoopServlet());
+        ctx1.addServletMappingDecoded("/", "snoop");
+
+        Context ctx2 = tomcat.addContext("/app2", null);
+        ctx2.setSessionCookiePath("/");
+        Tomcat.addServlet(ctx2, "snoop", new SnoopServlet());
+        ctx2.addServletMappingDecoded("/", "snoop");
+
+        tomcat.start();
+
+        ByteChunk res = new ByteChunk();
+        Map<String, List<String>> resHead = new HashMap<>();
+        getUrl("http://localhost:" + getPort() + "/app1/?createSession=true", res, null, resHead);
+
+        RequestDescriptor requestDesc = SnoopResult.parse(res.toString());
+        String sessionId1 = requestDesc.getRequestInfo("SESSION-ID");
+
+        Map<String, List<String>> reqHead = new HashMap<>();
+        reqHead.put("Cookie", List.of("JSESSIONID=" + sessionId1));
+
+        getUrl("http://localhost:" + getPort() + "/app2/?createSession=true", res, reqHead, null);
+
+        requestDesc = SnoopResult.parse(res.toString());
+        String sessionId2 = requestDesc.getRequestInfo("SESSION-ID");
+        Assert.assertEquals(sessionId1, sessionId2);
+    }
+
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


(tomcat) 02/07: Add TestSSLAuthenticator

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsoumis pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit ebef81c638c19a105efa37bfd0923380a448764f
Author: Dimitris Soumis <ji...@gmail.com>
AuthorDate: Tue Mar 24 14:21:59 2026 +0200

    Add TestSSLAuthenticator
---
 .../authenticator/TestSSLAuthenticator.java        | 40 ++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/test/org/apache/catalina/authenticator/TestSSLAuthenticator.java b/test/org/apache/catalina/authenticator/TestSSLAuthenticator.java
new file mode 100644
index 0000000000..346d06add8
--- /dev/null
+++ b/test/org/apache/catalina/authenticator/TestSSLAuthenticator.java
@@ -0,0 +1,40 @@
+/*
+ * 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.catalina.authenticator;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.net.TesterSupport;
+
+public class TestSSLAuthenticator extends TomcatBaseTest {
+
+    // https://bz.apache.org/bugzilla/show_bug.cgi?id=65991
+    @Test
+    public void testBindOnInitFalseNoNPE() throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        TesterSupport.configureClientCertContext(tomcat);
+        Assert.assertTrue(tomcat.getConnector().setProperty("bindOnInit", "false"));
+
+        tomcat.start();
+        tomcat.stop();
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


(tomcat) 04/07: Add generateKeystore method in TesterSupport which utilizes BouncyCastle to generate a temporary JKS keystore containing a self-signed RSA certificate.

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsoumis pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 196e3a2acdfd39c4797630c6e7010dc2789cca85
Author: Dimitris Soumis <ji...@gmail.com>
AuthorDate: Fri Mar 27 14:38:54 2026 +0200

    Add generateKeystore method in TesterSupport which utilizes BouncyCastle to generate a temporary JKS keystore containing a self-signed RSA certificate.
---
 test/org/apache/tomcat/util/net/TesterSupport.java | 76 ++++++++++++++++++++++
 1 file changed, 76 insertions(+)

diff --git a/test/org/apache/tomcat/util/net/TesterSupport.java b/test/org/apache/tomcat/util/net/TesterSupport.java
index d38b233e32..1330895f0e 100644
--- a/test/org/apache/tomcat/util/net/TesterSupport.java
+++ b/test/org/apache/tomcat/util/net/TesterSupport.java
@@ -18,11 +18,15 @@ package org.apache.tomcat.util.net;
 
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.math.BigInteger;
 import java.net.InetAddress;
 import java.net.Socket;
 import java.net.UnknownHostException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
@@ -36,6 +40,7 @@ import java.security.cert.PKIXRevocationChecker;
 import java.security.cert.TrustAnchor;
 import java.security.cert.X509CertSelector;
 import java.security.cert.X509Certificate;
+import java.util.Date;
 import java.util.EnumSet;
 import java.util.Enumeration;
 import java.util.HashSet;
@@ -79,6 +84,15 @@ import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type;
 import org.apache.tomcat.util.net.jsse.JSSEImplementation;
 import org.apache.tomcat.util.net.openssl.OpenSSLImplementation;
 import org.apache.tomcat.util.net.openssl.OpenSSLStatus;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 
 public final class TesterSupport {
 
@@ -410,6 +424,68 @@ public final class TesterSupport {
                     new X500Principal(lastRequestedIssuers[0].getName()));
     }
 
+    @FunctionalInterface
+    public interface CertificateExtensionsCustomizer {
+        void customize(KeyPair keyPair, X509v3CertificateBuilder certBuilder)
+            throws Exception;
+    }
+
+    /**
+     * Generate a temporary JKS keystore containing a self-signed RSA certificate.
+     *
+     * @param cn       the Common Name for the certificate subject
+     * @param alias    the keystore alias for the key entry
+     * @param sanNames DNS Subject Alternative Names to include, or {@code null} for none
+     * @param customizer callback to add extensions to the certificate, or {@code null} for none.
+     *
+     *  @return a temporary keystore file with password {@link #JKS_PASS}
+     *
+     *  @throws Exception if certificate generation or keystore creation fails
+     */
+    public static File generateKeystore(String cn, String alias, String[] sanNames,
+                                        CertificateExtensionsCustomizer customizer) throws Exception {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+        kpg.initialize(4096);
+        KeyPair keyPair = kpg.generateKeyPair();
+
+        X500Name subject = new X500Name("CN=" + cn);
+        BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
+        long oneDay = 86400000L;
+        Date notBefore = new Date(System.currentTimeMillis() - oneDay);
+        Date notAfter = new Date(System.currentTimeMillis() + 365L * oneDay);
+
+        X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(subject, serial, notBefore, notAfter,
+                subject, keyPair.getPublic());
+
+        if (sanNames != null && sanNames.length > 0) {
+            GeneralName[] generalNames = new GeneralName[sanNames.length];
+            for (int i = 0; i < sanNames.length; i++) {
+                generalNames[i] = new GeneralName(GeneralName.dNSName, sanNames[i]);
+            }
+            certBuilder.addExtension(Extension.subjectAlternativeName, false, new GeneralNames(generalNames));
+        }
+
+        if (customizer != null) {
+            customizer.customize(keyPair, certBuilder);
+        }
+
+        ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate());
+        X509Certificate certificate = new JcaX509CertificateConverter().getCertificate(certBuilder.build(signer));
+
+        KeyStore ks = KeyStore.getInstance("JKS");
+        ks.load(null, null);
+        ks.setKeyEntry(alias, keyPair.getPrivate(), JKS_PASS.toCharArray(), new X509Certificate[] { certificate });
+
+        File keystoreFile = File.createTempFile("test-cert-", ".jks");
+        keystoreFile.deleteOnExit();
+        try (FileOutputStream fos = new FileOutputStream(keystoreFile)) {
+            ks.store(fos, JKS_PASS.toCharArray());
+        }
+
+        return keystoreFile;
+    }
+
+
     public static final byte DATA = (byte)33;
 
     public static class SimpleServlet extends HttpServlet {


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


(tomcat) 07/07: Add support for NIO2 connector in TestLargeClientHello

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsoumis pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 541192875d3c33eed5d051091ced896ef73b162a
Author: Dimitris Soumis <ji...@gmail.com>
AuthorDate: Sun Mar 29 15:22:25 2026 +0300

    Add support for NIO2 connector in TestLargeClientHello
---
 java/org/apache/tomcat/util/net/SecureNio2Channel.java    | 3 +++
 test/org/apache/tomcat/util/net/TestLargeClientHello.java | 9 +++++++--
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/java/org/apache/tomcat/util/net/SecureNio2Channel.java b/java/org/apache/tomcat/util/net/SecureNio2Channel.java
index 0a75377b9c..a04a4e1774 100644
--- a/java/org/apache/tomcat/util/net/SecureNio2Channel.java
+++ b/java/org/apache/tomcat/util/net/SecureNio2Channel.java
@@ -590,6 +590,9 @@ public class SecureNio2Channel extends Nio2Channel {
             // call unwrap
             getBufHandler().configureReadBufferForWrite();
             result = sslEngine.unwrap(netInBuffer, getBufHandler().getReadBuffer());
+            if (log.isDebugEnabled() && result.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
+                log.debug(sm.getString("channel.nio.ssl.handshakeUnwrapBufferUnderflow"));
+            }
             /*
              * ByteBuffer.compact() is an optional method but netInBuffer is created from either ByteBuffer.allocate()
              * or ByteBuffer.allocateDirect() and the ByteBuffers returned by those methods do implement compact(). The
diff --git a/test/org/apache/tomcat/util/net/TestLargeClientHello.java b/test/org/apache/tomcat/util/net/TestLargeClientHello.java
index 43370f9c54..61e8de5eb2 100644
--- a/test/org/apache/tomcat/util/net/TestLargeClientHello.java
+++ b/test/org/apache/tomcat/util/net/TestLargeClientHello.java
@@ -65,7 +65,10 @@ public class TestLargeClientHello extends TomcatBaseTest {
 
         TesterSupport.initSsl(tomcat, keystoreFile.getAbsolutePath(), false);
 
-        try (LogCapture logCapture = attachLogCapture(Level.FINE, "org.apache.tomcat.util.net.SecureNioChannel")) {
+        try (LogCapture nioCapture = attachLogCapture(Level.FINE,
+            "org.apache.tomcat.util.net.SecureNioChannel");
+             LogCapture nio2Capture = attachLogCapture(Level.FINE,
+                 "org.apache.tomcat.util.net.SecureNio2Channel")) {
 
             tomcat.start();
 
@@ -77,8 +80,10 @@ public class TestLargeClientHello extends TomcatBaseTest {
             Assert.assertTrue(getUrl(url).toString().contains("Hello World"));
             Assert.assertTrue(getUrl(url).toString().contains("Hello World"));
 
-            Assert.assertTrue(logCapture.containsText(
+            Assert.assertTrue(nioCapture.containsText(
                     TomcatBaseTest.getKeyFromPropertiesFile("org.apache.tomcat.util.net",
+                    "channel.nio.ssl.handshakeUnwrapBufferUnderflow")) || nio2Capture.containsText(
+                TomcatBaseTest.getKeyFromPropertiesFile("org.apache.tomcat.util.net",
                     "channel.nio.ssl.handshakeUnwrapBufferUnderflow")));
         }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


(tomcat) 06/07: Move generateKeystore() from TesterSupport to independent TesterKeystoreGenerator class

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsoumis pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit d69712fe910ebc3137729f1271acdc62010dec4c
Author: Dimitris Soumis <ji...@gmail.com>
AuthorDate: Fri Mar 27 20:46:19 2026 +0200

    Move generateKeystore() from TesterSupport to independent TesterKeystoreGenerator class
---
 .../tomcat/util/net/TestLargeClientHello.java      |   2 +-
 .../tomcat/util/net/TesterKeystoreGenerator.java   | 103 +++++++++++++++++++++
 test/org/apache/tomcat/util/net/TesterSupport.java |  76 ---------------
 3 files changed, 104 insertions(+), 77 deletions(-)

diff --git a/test/org/apache/tomcat/util/net/TestLargeClientHello.java b/test/org/apache/tomcat/util/net/TestLargeClientHello.java
index c476fe0a24..43370f9c54 100644
--- a/test/org/apache/tomcat/util/net/TestLargeClientHello.java
+++ b/test/org/apache/tomcat/util/net/TestLargeClientHello.java
@@ -41,7 +41,7 @@ public class TestLargeClientHello extends TomcatBaseTest {
     // https://bz.apache.org/bugzilla/show_bug.cgi?id=67938
     @Test
     public void testLargeClientHelloWithSessionResumption() throws Exception {
-        File keystoreFile = TesterSupport.generateKeystore("localhost", "tomcat",
+        File keystoreFile = TesterKeystoreGenerator.generateKeystore("localhost", "tomcat",
             new String[]{"localhost", "*.localhost"},
                 (keyPair, certBuilder) -> {
                 JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
diff --git a/test/org/apache/tomcat/util/net/TesterKeystoreGenerator.java b/test/org/apache/tomcat/util/net/TesterKeystoreGenerator.java
new file mode 100644
index 0000000000..9fd4affde6
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/TesterKeystoreGenerator.java
@@ -0,0 +1,103 @@
+/*
+ * 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.tomcat.util.net;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+
+public final class TesterKeystoreGenerator {
+
+    private TesterKeystoreGenerator() {}
+
+    @FunctionalInterface
+    public interface CertificateExtensionsCustomizer {
+        void customize(KeyPair keyPair, X509v3CertificateBuilder certBuilder)
+            throws Exception;
+    }
+
+    /**
+     * Generate a temporary JKS keystore containing a self-signed RSA certificate.
+     *
+     * @param cn       the Common Name for the certificate subject
+     * @param alias    the keystore alias for the key entry
+     * @param sanNames DNS Subject Alternative Names to include, or {@code null} for none
+     * @param customizer callback to add extensions to the certificate, or {@code null} for none.
+     *
+     *  @return a temporary keystore file with password {@link  TesterSupport#JKS_PASS}
+     *
+     *  @throws Exception if certificate generation or keystore creation fails
+     */
+    public static File generateKeystore(String cn, String alias, String[] sanNames,
+                                        CertificateExtensionsCustomizer customizer) throws Exception {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+        kpg.initialize(4096);
+        KeyPair keyPair = kpg.generateKeyPair();
+
+        X500Name subject = new X500Name("CN=" + cn);
+        BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
+        long oneDay = 86400000L;
+        Date notBefore = new Date(System.currentTimeMillis() - oneDay);
+        Date notAfter = new Date(System.currentTimeMillis() + 365L * oneDay);
+
+        X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(subject, serial, notBefore, notAfter,
+                subject, keyPair.getPublic());
+
+        if (sanNames != null && sanNames.length > 0) {
+            GeneralName[] generalNames = new GeneralName[sanNames.length];
+            for (int i = 0; i < sanNames.length; i++) {
+                generalNames[i] = new GeneralName(GeneralName.dNSName, sanNames[i]);
+            }
+            certBuilder.addExtension(Extension.subjectAlternativeName, false, new GeneralNames(generalNames));
+        }
+
+        if (customizer != null) {
+            customizer.customize(keyPair, certBuilder);
+        }
+
+        ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate());
+        X509Certificate certificate = new JcaX509CertificateConverter().getCertificate(certBuilder.build(signer));
+
+        KeyStore ks = KeyStore.getInstance("JKS");
+        ks.load(null, null);
+        ks.setKeyEntry(alias, keyPair.getPrivate(), TesterSupport.JKS_PASS.toCharArray(), new X509Certificate[] { certificate });
+
+        File keystoreFile = File.createTempFile("test-cert-", ".jks");
+        keystoreFile.deleteOnExit();
+        try (FileOutputStream fos = new FileOutputStream(keystoreFile)) {
+            ks.store(fos, TesterSupport.JKS_PASS.toCharArray());
+        }
+
+        return keystoreFile;
+    }
+}
diff --git a/test/org/apache/tomcat/util/net/TesterSupport.java b/test/org/apache/tomcat/util/net/TesterSupport.java
index 1330895f0e..d38b233e32 100644
--- a/test/org/apache/tomcat/util/net/TesterSupport.java
+++ b/test/org/apache/tomcat/util/net/TesterSupport.java
@@ -18,15 +18,11 @@ package org.apache.tomcat.util.net;
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.math.BigInteger;
 import java.net.InetAddress;
 import java.net.Socket;
 import java.net.UnknownHostException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
@@ -40,7 +36,6 @@ import java.security.cert.PKIXRevocationChecker;
 import java.security.cert.TrustAnchor;
 import java.security.cert.X509CertSelector;
 import java.security.cert.X509Certificate;
-import java.util.Date;
 import java.util.EnumSet;
 import java.util.Enumeration;
 import java.util.HashSet;
@@ -84,15 +79,6 @@ import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type;
 import org.apache.tomcat.util.net.jsse.JSSEImplementation;
 import org.apache.tomcat.util.net.openssl.OpenSSLImplementation;
 import org.apache.tomcat.util.net.openssl.OpenSSLStatus;
-import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.asn1.x509.Extension;
-import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.GeneralNames;
-import org.bouncycastle.cert.X509v3CertificateBuilder;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
-import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
-import org.bouncycastle.operator.ContentSigner;
-import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 
 public final class TesterSupport {
 
@@ -424,68 +410,6 @@ public final class TesterSupport {
                     new X500Principal(lastRequestedIssuers[0].getName()));
     }
 
-    @FunctionalInterface
-    public interface CertificateExtensionsCustomizer {
-        void customize(KeyPair keyPair, X509v3CertificateBuilder certBuilder)
-            throws Exception;
-    }
-
-    /**
-     * Generate a temporary JKS keystore containing a self-signed RSA certificate.
-     *
-     * @param cn       the Common Name for the certificate subject
-     * @param alias    the keystore alias for the key entry
-     * @param sanNames DNS Subject Alternative Names to include, or {@code null} for none
-     * @param customizer callback to add extensions to the certificate, or {@code null} for none.
-     *
-     *  @return a temporary keystore file with password {@link #JKS_PASS}
-     *
-     *  @throws Exception if certificate generation or keystore creation fails
-     */
-    public static File generateKeystore(String cn, String alias, String[] sanNames,
-                                        CertificateExtensionsCustomizer customizer) throws Exception {
-        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
-        kpg.initialize(4096);
-        KeyPair keyPair = kpg.generateKeyPair();
-
-        X500Name subject = new X500Name("CN=" + cn);
-        BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
-        long oneDay = 86400000L;
-        Date notBefore = new Date(System.currentTimeMillis() - oneDay);
-        Date notAfter = new Date(System.currentTimeMillis() + 365L * oneDay);
-
-        X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(subject, serial, notBefore, notAfter,
-                subject, keyPair.getPublic());
-
-        if (sanNames != null && sanNames.length > 0) {
-            GeneralName[] generalNames = new GeneralName[sanNames.length];
-            for (int i = 0; i < sanNames.length; i++) {
-                generalNames[i] = new GeneralName(GeneralName.dNSName, sanNames[i]);
-            }
-            certBuilder.addExtension(Extension.subjectAlternativeName, false, new GeneralNames(generalNames));
-        }
-
-        if (customizer != null) {
-            customizer.customize(keyPair, certBuilder);
-        }
-
-        ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate());
-        X509Certificate certificate = new JcaX509CertificateConverter().getCertificate(certBuilder.build(signer));
-
-        KeyStore ks = KeyStore.getInstance("JKS");
-        ks.load(null, null);
-        ks.setKeyEntry(alias, keyPair.getPrivate(), JKS_PASS.toCharArray(), new X509Certificate[] { certificate });
-
-        File keystoreFile = File.createTempFile("test-cert-", ".jks");
-        keystoreFile.deleteOnExit();
-        try (FileOutputStream fos = new FileOutputStream(keystoreFile)) {
-            ks.store(fos, JKS_PASS.toCharArray());
-        }
-
-        return keystoreFile;
-    }
-
-
     public static final byte DATA = (byte)33;
 
     public static class SimpleServlet extends HttpServlet {


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


(tomcat) 05/07: Add TestLargeClientHello and add a debug log in SecureNioChannel when BUFFER_UNDERFLOW occurs during handshake

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsoumis pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit c2bd7fa98d3d712d42dcc35759f311a28de92784
Author: Dimitris Soumis <ji...@gmail.com>
AuthorDate: Fri Mar 27 14:38:58 2026 +0200

    Add TestLargeClientHello and add a debug log in SecureNioChannel when BUFFER_UNDERFLOW occurs during handshake
---
 .../apache/tomcat/util/net/LocalStrings.properties |  1 +
 .../apache/tomcat/util/net/SecureNioChannel.java   |  3 +
 .../tomcat/util/net/TestLargeClientHello.java      | 86 ++++++++++++++++++++++
 3 files changed, 90 insertions(+)

diff --git a/java/org/apache/tomcat/util/net/LocalStrings.properties b/java/org/apache/tomcat/util/net/LocalStrings.properties
index b023ad09a7..6a56e51816 100644
--- a/java/org/apache/tomcat/util/net/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/net/LocalStrings.properties
@@ -47,6 +47,7 @@ channel.nio.ssl.unexpectedStatusDuringWrap=Unexpected status [{0}] during handsh
 channel.nio.ssl.unwrapFail=Unable to unwrap data, invalid status [{0}]
 channel.nio.ssl.unwrapFailResize=Unable to unwrap data because buffer is too small, invalid status [{0}]
 channel.nio.ssl.wrapFail=Unable to wrap data, invalid status [{0}]
+channel.nio.ssl.handshakeUnwrapBufferUnderflow=BUFFER_UNDERFLOW during handshake unwrap, more data needed from the network
 
 endpoint.accept.fail=Socket accept failed
 endpoint.alpn.fail=Failed to configure endpoint for ALPN using [{0}]
diff --git a/java/org/apache/tomcat/util/net/SecureNioChannel.java b/java/org/apache/tomcat/util/net/SecureNioChannel.java
index bb43c5c098..28c090d5c8 100644
--- a/java/org/apache/tomcat/util/net/SecureNioChannel.java
+++ b/java/org/apache/tomcat/util/net/SecureNioChannel.java
@@ -492,6 +492,9 @@ public class SecureNioChannel extends NioChannel {
             // call unwrap
             getBufHandler().configureReadBufferForWrite();
             result = sslEngine.unwrap(netInBuffer, getBufHandler().getReadBuffer());
+            if (log.isDebugEnabled() && result.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
+                log.debug(sm.getString("channel.nio.ssl.handshakeUnwrapBufferUnderflow"));
+            }
             /*
              * ByteBuffer.compact() is an optional method but netInBuffer is created from either ByteBuffer.allocate()
              * or ByteBuffer.allocateDirect() and the ByteBuffers returned by those methods do implement compact(). The
diff --git a/test/org/apache/tomcat/util/net/TestLargeClientHello.java b/test/org/apache/tomcat/util/net/TestLargeClientHello.java
new file mode 100644
index 0000000000..c476fe0a24
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/TestLargeClientHello.java
@@ -0,0 +1,86 @@
+/*
+ * 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.tomcat.util.net;
+
+import java.io.File;
+import java.util.logging.Level;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERUTF8String;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
+
+public class TestLargeClientHello extends TomcatBaseTest {
+
+    // https://bz.apache.org/bugzilla/show_bug.cgi?id=67938
+    @Test
+    public void testLargeClientHelloWithSessionResumption() throws Exception {
+        File keystoreFile = TesterSupport.generateKeystore("localhost", "tomcat",
+            new String[]{"localhost", "*.localhost"},
+                (keyPair, certBuilder) -> {
+                JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
+                certBuilder.addExtension(Extension.subjectKeyIdentifier, false,
+                        extUtils.createSubjectKeyIdentifier(keyPair.getPublic()));
+                certBuilder.addExtension(Extension.authorityKeyIdentifier, false,
+                        extUtils.createAuthorityKeyIdentifier(keyPair.getPublic()));
+                certBuilder.addExtension(Extension.basicConstraints, true,
+                    new BasicConstraints(true));
+                certBuilder.addExtension(Extension.keyUsage, false,
+                    new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment));
+                certBuilder.addExtension(new ASN1ObjectIdentifier("2.999"), false,
+                    new DERUTF8String("x".repeat(16922)));
+            });
+
+        Tomcat tomcat = getTomcatInstance();
+
+        Context ctx = getProgrammaticRootContext();
+        Tomcat.addServlet(ctx, "hello", new HelloWorldServlet());
+        ctx.addServletMappingDecoded("/", "hello");
+
+        TesterSupport.initSsl(tomcat, keystoreFile.getAbsolutePath(), false);
+
+        try (LogCapture logCapture = attachLogCapture(Level.FINE, "org.apache.tomcat.util.net.SecureNioChannel")) {
+
+            tomcat.start();
+
+            SSLContext sc = SSLContext.getInstance(Constants.SSL_PROTO_TLSv1_3);
+            sc.init(null, new TrustManager[]{new TesterSupport.TrustAllCerts()}, null);
+            javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+
+            String url = "https://localhost:" + getPort() + "/";
+            Assert.assertTrue(getUrl(url).toString().contains("Hello World"));
+            Assert.assertTrue(getUrl(url).toString().contains("Hello World"));
+
+            Assert.assertTrue(logCapture.containsText(
+                    TomcatBaseTest.getKeyFromPropertiesFile("org.apache.tomcat.util.net",
+                    "channel.nio.ssl.handshakeUnwrapBufferUnderflow")));
+        }
+
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


(tomcat) 01/07: Fix naming convention of performance tests in BUILDING.txt

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsoumis pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 1ad0a9c0e8418346b866f122d5206a62c8d9b37c
Author: Dimitris Soumis <ji...@gmail.com>
AuthorDate: Tue Mar 24 13:26:29 2026 +0200

    Fix naming convention of performance tests in BUILDING.txt
---
 BUILDING.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/BUILDING.txt b/BUILDING.txt
index 5043c0a469..775c7efb28 100644
--- a/BUILDING.txt
+++ b/BUILDING.txt
@@ -580,7 +580,7 @@ NOTE: Cobertura is licensed under GPL v2 with parts of it being under
 
     Where there is no benefit in running an absolute performance test as part
     of a standard test run, the test will be excluded by naming it
-    Tester*Performance.java.
+    Test*Performance.java.
 
     The relative tests are included as part of a standard test run however,
     where the assumptions made about host capabilities are  not true (e.g. on


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org