From 13fac161c7fe9800e128b242bacbf896b99190cb Mon Sep 17 00:00:00 2001 From: pmeloni Date: Tue, 21 Apr 2026 16:53:25 +0200 Subject: [PATCH] SOLVE RHEL-150714: add patch and edit spec files --- rhel-150714.patch | 3954 +++++++++++++++++++++++++++++++++++++++++++++ rhel-168081.patch | 22 +- tomcat.spec | 8 +- 3 files changed, 3961 insertions(+), 23 deletions(-) create mode 100644 rhel-150714.patch diff --git a/rhel-150714.patch b/rhel-150714.patch new file mode 100644 index 0000000..ec1eabb --- /dev/null +++ b/rhel-150714.patch @@ -0,0 +1,3954 @@ +diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLStatus.java b/java/org/apache/tomcat/util/net/openssl/OpenSSLStatus.java +index 1ad7d3dc84..749813bc38 100644 +--- a/java/org/apache/tomcat/util/net/openssl/OpenSSLStatus.java ++++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLStatus.java +@@ -38,9 +38,10 @@ public class OpenSSLStatus { + private static volatile boolean useOpenSSL = true; + private static volatile boolean instanceCreated = false; + private static volatile long version = 0; ++ private static volatile int majorVersion = 0; ++ private static volatile int minorVersion = 0; + private static volatile Name name = Name.UNKNOWN; + +- + public static boolean isLibraryInitialized() { + return libraryInitialized; + } +@@ -95,6 +96,34 @@ public class OpenSSLStatus { + OpenSSLStatus.version = version; + } + ++ /** ++ * @return the majorVersion ++ */ ++ public static int getMajorVersion() { ++ return majorVersion; ++ } ++ ++ /** ++ * @param majorVersion the majorVersion to set ++ */ ++ public static void setMajorVersion(int majorVersion) { ++ OpenSSLStatus.majorVersion = majorVersion; ++ } ++ ++ /** ++ * @return the minorVersion ++ */ ++ public static int getMinorVersion() { ++ return minorVersion; ++ } ++ ++ /** ++ * @param minorVersion the minorVersion to set ++ */ ++ public static void setMinorVersion(int minorVersion) { ++ OpenSSLStatus.minorVersion = minorVersion; ++ } ++ + /** + * @return the library name + */ +@@ -116,4 +145,18 @@ public class OpenSSLStatus { + return Name.OPENSSL3.equals(name); + } + ++ /** ++ * @return true if running with BoringSSL ++ */ ++ public static boolean isBoringSSL() { ++ return Name.BORINGSSL.equals(name); ++ } ++ ++ /** ++ * @return true if running with LibreSSL < 3.5 ++ */ ++ public static boolean isLibreSSLPre35() { ++ return Name.LIBRESSL.equals(name) && ((majorVersion == 3 && minorVersion < 5) || majorVersion < 3); ++ } ++ + } +diff --git a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java +index bfb9b6f20a..960fa0459f 100644 +--- a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java ++++ b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java +@@ -59,6 +59,7 @@ import org.apache.tomcat.util.buf.Asn1Parser; + import org.apache.tomcat.util.http.Method; + import org.apache.tomcat.util.net.Constants; + import org.apache.tomcat.util.net.SSLUtil; ++import org.apache.tomcat.util.net.openssl.OpenSSLStatus; + import org.apache.tomcat.util.net.openssl.ciphers.OpenSSLCipherConfigurationParser; + import org.apache.tomcat.util.openssl.SSL_CTX_set_verify$callback; + import org.apache.tomcat.util.openssl.SSL_set_info_callback$cb; +@@ -68,7 +69,7 @@ import org.apache.tomcat.util.openssl.openssl_h_Compatibility; + import org.apache.tomcat.util.res.StringManager; + + /** +- * Implements a {@link SSLEngine} using OpenSSL BIO ++ * Implements a {@link SSLEngine} using OpenSSL BIO + * abstractions. + */ + public final class OpenSSLEngine extends SSLEngine implements SSLUtil.ProtocolInfo { +@@ -137,7 +138,7 @@ public final class OpenSSLEngine extends SSLEngine implements SSLUtil.ProtocolIn + private volatile boolean destroyed; + + // Use an invalid cipherSuite until the handshake is completed +- // See https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/SSLEngine.html#getSession() ++ // See https://docs.oracle.com/en/java/javase/21/docs/api/java.base/javax/net/ssl/SSLEngine.html#getSession() + private volatile String version; + private volatile String cipher; + private volatile String applicationProtocol; +@@ -1076,7 +1077,7 @@ public final class OpenSSLEngine extends SSLEngine implements SSLUtil.ProtocolIn + case NONE -> SSL_VERIFY_NONE(); + case REQUIRE -> SSL_VERIFY_FAIL_IF_NO_PEER_CERT(); + case OPTIONAL -> +- certificateVerificationOptionalNoCA ? OpenSSLContext.OPTIONAL_NO_CA : SSL_VERIFY_PEER(); ++ certificateVerificationOptionalNoCA ? OpenSSLContext.OPTIONAL_NO_CA : SSL_VERIFY_PEER(); + }; + // Set int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) callback + int value = switch (mode) { +@@ -1138,6 +1139,9 @@ public final class OpenSSLEngine extends SSLEngine implements SSLUtil.ProtocolIn + ok = 1; + openssl_h_Compatibility.SSL_set_verify_result(state.ssl, X509_V_OK()); + } ++ if (ok == 0 && errnum == X509_V_ERR_UNABLE_TO_GET_CRL()) { ++ ok = 1; ++ } + /* + * Expired certificates vs. "expired" CRLs: by default, OpenSSL turns X509_V_ERR_CRL_HAS_EXPIRED into a + * "certificate_expired(45)" SSL alert, but that's not really the message we should convey to the peer (at +@@ -1168,9 +1172,10 @@ public final class OpenSSLEngine extends SSLEngine implements SSLUtil.ProtocolIn + if (ocspResponse == V_OCSP_CERTSTATUS_REVOKED()) { + ok = 0; + errnum = X509_STORE_CTX_get_error(x509ctx); ++ X509_STORE_CTX_set_error(x509ctx, X509_V_ERR_CERT_REVOKED()); + } else if (ocspResponse == V_OCSP_CERTSTATUS_UNKNOWN()) { + errnum = X509_STORE_CTX_get_error(x509ctx); +- if (errnum <= 0) { ++ if (errnum != X509_V_ERR_UNABLE_TO_GET_CRL() && (errnum == X509_V_ERR_APPLICATION_VERIFICATION() || errnum != 0)) { + ok = 0; + } + } +@@ -1196,46 +1201,53 @@ public final class OpenSSLEngine extends SSLEngine implements SSLUtil.ProtocolIn + // don't do OCSP checking for valid self-issued certs + X509_STORE_CTX_set_error(x509ctx, X509_V_OK()); + } else { +- // If we can't get the issuer, we cannot perform OCSP verification +- MemorySegment issuer = X509_STORE_CTX_get0_current_issuer(x509ctx); +- if (!MemorySegment.NULL.equals(issuer)) { +- // sslutils.c ssl_ocsp_request(x509, issuer, x509ctx); +- int nid = X509_get_ext_by_NID(x509, NID_info_access(), -1); +- if (nid >= 0) { +- try (var localArenal = Arena.ofConfined()) { +- MemorySegment ext = X509_get_ext(x509, nid); +- MemorySegment os = X509_EXTENSION_get_data(ext); +- int length = ASN1_STRING_length(os); +- MemorySegment data = ASN1_STRING_get0_data(os); +- // ocsp_urls = decode_OCSP_url(os); +- byte[] asn1String = +- data.reinterpret(length, localArenal, null).toArray(ValueLayout.JAVA_BYTE); +- Asn1Parser parser = new Asn1Parser(asn1String); +- // Parse the byte sequence +- ArrayList urls = new ArrayList<>(); +- try { +- parseOCSPURLs(parser, urls); +- } catch (Exception e) { +- log.error(sm.getString("engine.ocspParseError"), e); +- } +- if (!urls.isEmpty()) { +- // Use OpenSSL to build OCSP request +- for (String urlString : urls) { +- try { +- URL url = (new URI(urlString)).toURL(); +- ocspResponse = processOCSPRequest(url, issuer, x509, x509ctx, localArenal); +- if (log.isDebugEnabled()) { +- log.debug(sm.getString("engine.ocspResponse", urlString, +- Integer.toString(ocspResponse))); ++ try (var localArena = Arena.ofConfined()) { ++ // If we can't get the issuer, we cannot perform OCSP verification ++ MemorySegment x509IssuerPointer = localArena.allocateFrom(ValueLayout.ADDRESS, MemorySegment.NULL); ++ int res = X509_STORE_CTX_get1_issuer(x509IssuerPointer, x509ctx, x509); ++ if (res > 0) { ++ MemorySegment issuer = MemorySegment.NULL; ++ try { ++ issuer = x509IssuerPointer.get(ValueLayout.ADDRESS, 0); ++ // sslutils.c ssl_ocsp_request(x509, issuer, x509ctx); ++ int nid = X509_get_ext_by_NID(x509, NID_info_access(), -1); ++ if (nid >= 0) { ++ MemorySegment ext = X509_get_ext(x509, nid); ++ MemorySegment os = X509_EXTENSION_get_data(ext); ++ int length = ASN1_STRING_length(os); ++ MemorySegment data = ASN1_STRING_get0_data(os); ++ // ocsp_urls = decode_OCSP_url(os); ++ byte[] asn1String = ++ data.reinterpret(length, localArena, null).toArray(ValueLayout.JAVA_BYTE); ++ Asn1Parser parser = new Asn1Parser(asn1String); ++ // Parse the byte sequence ++ ArrayList urls = new ArrayList<>(); ++ try { ++ parseOCSPURLs(parser, urls); ++ } catch (Exception e) { ++ log.error(sm.getString("engine.ocspParseError"), e); ++ } ++ if (!urls.isEmpty()) { ++ // Use OpenSSL to build OCSP request ++ for (String urlString : urls) { ++ try { ++ URL url = (new URI(urlString)).toURL(); ++ ocspResponse = processOCSPRequest(url, issuer, x509, x509ctx, localArena); ++ if (log.isDebugEnabled()) { ++ log.debug(sm.getString("engine.ocspResponse", urlString, ++ Integer.toString(ocspResponse))); ++ } ++ } catch (MalformedURLException | URISyntaxException e) { ++ log.warn(sm.getString("engine.invalidOCSPURL", urlString)); ++ } ++ if (ocspResponse != V_OCSP_CERTSTATUS_UNKNOWN()) { ++ break; + } +- } catch (MalformedURLException | URISyntaxException e) { +- log.warn(sm.getString("engine.invalidOCSPURL", urlString)); +- } +- if (ocspResponse != V_OCSP_CERTSTATUS_UNKNOWN()) { +- break; + } + } + } ++ } finally { ++ X509_free(issuer); + } + } + } +@@ -1275,6 +1287,9 @@ public final class OpenSSLEngine extends SSLEngine implements SSLUtil.ProtocolIn + + private static int processOCSPRequest(URL url, MemorySegment issuer, MemorySegment x509, + MemorySegment /* X509_STORE_CTX */ x509ctx, Arena localArena) { ++ if (OpenSSLStatus.Name.BORINGSSL.equals(OpenSSLStatus.getName())) { ++ return V_OCSP_CERTSTATUS_UNKNOWN(); ++ } + MemorySegment ocspRequest = MemorySegment.NULL; + MemorySegment ocspResponse = MemorySegment.NULL; + MemorySegment id; +@@ -1574,7 +1589,7 @@ public final class OpenSSLEngine extends SSLEngine implements SSLUtil.ProtocolIn + } + + private Principal principal(Certificate[] certs) { +- return ((java.security.cert.X509Certificate) certs[0]).getIssuerX500Principal(); ++ return ((java.security.cert.X509Certificate) certs[0]).getSubjectX500Principal(); + } + + @Override +diff --git a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLibrary.java b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLibrary.java +index 7272db9cf2..c1bb2cb664 100644 +--- a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLibrary.java ++++ b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLibrary.java +@@ -178,6 +178,8 @@ public class OpenSSLLibrary { + initLibrary(); + + OpenSSLStatus.setVersion(OpenSSL_version_num()); ++ OpenSSLStatus.setMajorVersion(openssl_h_Compatibility.MAJOR); ++ OpenSSLStatus.setMinorVersion(openssl_h_Compatibility.MINOR); + if (openssl_h_Compatibility.OPENSSL3) { + OpenSSLStatus.setName(OpenSSLStatus.Name.OPENSSL3); + } else if (openssl_h_Compatibility.OPENSSL) { +diff --git a/java/org/apache/tomcat/util/openssl/openssl_h.java b/java/org/apache/tomcat/util/openssl/openssl_h.java +index 0c2465b633..cbd8a5a0d4 100644 +--- a/java/org/apache/tomcat/util/openssl/openssl_h.java ++++ b/java/org/apache/tomcat/util/openssl/openssl_h.java +@@ -196,6 +196,16 @@ public class openssl_h { + return X509_V_OK; + } + ++ private static final int X509_V_ERR_UNABLE_TO_GET_CRL = (int) 3L; ++ ++ /** ++ * {@snippet lang = c : * #define X509_V_ERR_UNABLE_TO_GET_CRL 3 ++ * } ++ */ ++ public static int X509_V_ERR_UNABLE_TO_GET_CRL() { ++ return X509_V_ERR_UNABLE_TO_GET_CRL; ++ } ++ + private static final int X509_V_ERR_CRL_HAS_EXPIRED = (int) 12L; + + /** +@@ -246,6 +256,16 @@ public class openssl_h { + return X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE; + } + ++ private static final int X509_V_ERR_CERT_REVOKED = (int) 23L; ++ ++ /** ++ * {@snippet lang = c : * #define X509_V_ERR_CERT_REVOKED 23 ++ * } ++ */ ++ public static int X509_V_ERR_CERT_REVOKED() { ++ return X509_V_ERR_CERT_REVOKED; ++ } ++ + private static final int X509_V_ERR_CERT_UNTRUSTED = (int) 27L; + + /** +@@ -266,6 +286,46 @@ public class openssl_h { + return X509_V_ERR_APPLICATION_VERIFICATION; + } + ++ private static final int X509_V_ERR_OCSP_RESP_INVALID = (int) 96L; ++ ++ /** ++ * {@snippet lang = c : * #define X509_V_ERR_OCSP_RESP_INVALID 96 ++ * } ++ */ ++ public static int X509_V_ERR_OCSP_RESP_INVALID() { ++ return X509_V_ERR_OCSP_RESP_INVALID; ++ } ++ ++ private static final int X509_V_ERR_OCSP_SIGNATURE_FAILURE = (int) 97L; ++ ++ /** ++ * {@snippet lang = c : * #define X509_V_ERR_OCSP_SIGNATURE_FAILURE 97 ++ * } ++ */ ++ public static int X509_V_ERR_OCSP_SIGNATURE_FAILURE() { ++ return X509_V_ERR_OCSP_SIGNATURE_FAILURE; ++ } ++ ++ private static final int X509_V_ERR_OCSP_NOT_YET_VALID = (int) 98L; ++ ++ /** ++ * {@snippet lang = c : * #define X509_V_ERR_OCSP_NOT_YET_VALID 98 ++ * } ++ */ ++ public static int X509_V_ERR_OCSP_NOT_YET_VALID() { ++ return X509_V_ERR_OCSP_NOT_YET_VALID; ++ } ++ ++ private static final int X509_V_ERR_OCSP_HAS_EXPIRED = (int) 99L; ++ ++ /** ++ * {@snippet lang = c : * #define X509_V_ERR_OCSP_HAS_EXPIRED 99 ++ * } ++ */ ++ public static int X509_V_ERR_OCSP_HAS_EXPIRED() { ++ return X509_V_ERR_OCSP_HAS_EXPIRED; ++ } ++ + private static final int X509_V_FLAG_CRL_CHECK = (int) 4L; + + /** +@@ -3071,6 +3131,66 @@ public class openssl_h { + } + } + ++ private static class X509_STORE_CTX_get0_store { ++ public static final FunctionDescriptor DESC = FunctionDescriptor.of( ++ openssl_h.C_POINTER, ++ openssl_h.C_POINTER ++ ); ++ ++ public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_STORE_CTX_get0_store"); ++ ++ public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); ++ } ++ ++ /** ++ * Function descriptor for: ++ * {@snippet lang=c : ++ * X509_STORE *X509_STORE_CTX_get0_store(const X509_STORE_CTX *ctx) ++ * } ++ */ ++ public static FunctionDescriptor X509_STORE_CTX_get0_store$descriptor() { ++ return X509_STORE_CTX_get0_store.DESC; ++ } ++ ++ /** ++ * Downcall method handle for: ++ * {@snippet lang=c : ++ * X509_STORE *X509_STORE_CTX_get0_store(const X509_STORE_CTX *ctx) ++ * } ++ */ ++ public static MethodHandle X509_STORE_CTX_get0_store$handle() { ++ return X509_STORE_CTX_get0_store.HANDLE; ++ } ++ ++ /** ++ * Address for: ++ * {@snippet lang=c : ++ * X509_STORE *X509_STORE_CTX_get0_store(const X509_STORE_CTX *ctx) ++ * } ++ */ ++ public static MemorySegment X509_STORE_CTX_get0_store$address() { ++ return X509_STORE_CTX_get0_store.ADDR; ++ } ++ ++ /** ++ * {@snippet lang=c : ++ * X509_STORE *X509_STORE_CTX_get0_store(const X509_STORE_CTX *ctx) ++ * } ++ */ ++ public static MemorySegment X509_STORE_CTX_get0_store(MemorySegment ctx) { ++ var mh$ = X509_STORE_CTX_get0_store.HANDLE; ++ try { ++ if (TRACE_DOWNCALLS) { ++ traceDowncall("X509_STORE_CTX_get0_store", ctx); ++ } ++ return (MemorySegment)mh$.invokeExact(ctx); ++ } catch (Error | RuntimeException ex) { ++ throw ex; ++ } catch (Throwable ex$) { ++ throw new AssertionError("should not reach here", ex$); ++ } ++ } ++ + private static class X509_STORE_CTX_get0_untrusted { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + +@@ -3584,52 +3704,52 @@ public class openssl_h { + } + } + +- private static class X509_STORE_CTX_get0_current_issuer { +- public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); ++ private static class X509_STORE_CTX_get1_issuer { ++ public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + +- public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_STORE_CTX_get0_current_issuer"); ++ public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_STORE_CTX_get1_issuer"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: +- * {@snippet lang = c : * X509 *X509_STORE_CTX_get0_current_issuer(const X509_STORE_CTX *ctx) ++ * {@snippet lang = c : * int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x) + * } + */ +- public static FunctionDescriptor X509_STORE_CTX_get0_current_issuer$descriptor() { +- return X509_STORE_CTX_get0_current_issuer.DESC; ++ public static FunctionDescriptor X509_STORE_CTX_get1_issuer$descriptor() { ++ return X509_STORE_CTX_get1_issuer.DESC; + } + + /** + * Downcall method handle for: +- * {@snippet lang = c : * X509 *X509_STORE_CTX_get0_current_issuer(const X509_STORE_CTX *ctx) ++ * {@snippet lang = c : * int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x) + * } + */ +- public static MethodHandle X509_STORE_CTX_get0_current_issuer$handle() { +- return X509_STORE_CTX_get0_current_issuer.HANDLE; ++ public static MethodHandle X509_STORE_CTX_get1_issuer$handle() { ++ return X509_STORE_CTX_get1_issuer.HANDLE; + } + + /** + * Address for: +- * {@snippet lang = c : * X509 *X509_STORE_CTX_get0_current_issuer(const X509_STORE_CTX *ctx) ++ * {@snippet lang = c : * int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x) + * } + */ +- public static MemorySegment X509_STORE_CTX_get0_current_issuer$address() { +- return X509_STORE_CTX_get0_current_issuer.ADDR; ++ public static MemorySegment X509_STORE_CTX_get1_issuer$address() { ++ return X509_STORE_CTX_get1_issuer.ADDR; + } + + /** +- * {@snippet lang = c : * X509 *X509_STORE_CTX_get0_current_issuer(const X509_STORE_CTX *ctx) ++ * {@snippet lang = c : * int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x) + * } + */ +- public static MemorySegment X509_STORE_CTX_get0_current_issuer(MemorySegment ctx) { +- var mh$ = X509_STORE_CTX_get0_current_issuer.HANDLE; ++ public static int X509_STORE_CTX_get1_issuer(MemorySegment issuer, MemorySegment ctx, MemorySegment x) { ++ var mh$ = X509_STORE_CTX_get1_issuer.HANDLE; + try { + if (TRACE_DOWNCALLS) { +- traceDowncall("X509_STORE_CTX_get0_current_issuer", ctx); ++ traceDowncall("X509_STORE_CTX_get1_issuer", issuer, ctx, x); + } +- return (MemorySegment) mh$.invokeExact(ctx); ++ return (int) mh$.invokeExact(issuer, ctx, x); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } +@@ -8771,6 +8891,315 @@ public class openssl_h { + } + } + ++ private static class OCSP_resp_get0_certs { ++ public static final FunctionDescriptor DESC = FunctionDescriptor.of( ++ openssl_h.C_POINTER, ++ openssl_h.C_POINTER ++ ); ++ ++ public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_resp_get0_certs"); ++ ++ public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); ++ } ++ ++ /** ++ * Function descriptor for: ++ * {@snippet lang=c : ++ * const struct stack_st_X509 *OCSP_resp_get0_certs(const OCSP_BASICRESP *bs) ++ * } ++ */ ++ public static FunctionDescriptor OCSP_resp_get0_certs$descriptor() { ++ return OCSP_resp_get0_certs.DESC; ++ } ++ ++ /** ++ * Downcall method handle for: ++ * {@snippet lang=c : ++ * const struct stack_st_X509 *OCSP_resp_get0_certs(const OCSP_BASICRESP *bs) ++ * } ++ */ ++ public static MethodHandle OCSP_resp_get0_certs$handle() { ++ return OCSP_resp_get0_certs.HANDLE; ++ } ++ ++ /** ++ * Address for: ++ * {@snippet lang=c : ++ * const struct stack_st_X509 *OCSP_resp_get0_certs(const OCSP_BASICRESP *bs) ++ * } ++ */ ++ public static MemorySegment OCSP_resp_get0_certs$address() { ++ return OCSP_resp_get0_certs.ADDR; ++ } ++ ++ /** ++ * {@snippet lang=c : ++ * const struct stack_st_X509 *OCSP_resp_get0_certs(const OCSP_BASICRESP *bs) ++ * } ++ */ ++ public static MemorySegment OCSP_resp_get0_certs(MemorySegment bs) { ++ var mh$ = OCSP_resp_get0_certs.HANDLE; ++ try { ++ if (TRACE_DOWNCALLS) { ++ traceDowncall("OCSP_resp_get0_certs", bs); ++ } ++ return (MemorySegment)mh$.invokeExact(bs); ++ } catch (Error | RuntimeException ex) { ++ throw ex; ++ } catch (Throwable ex$) { ++ throw new AssertionError("should not reach here", ex$); ++ } ++ } ++ ++ private static class OCSP_basic_verify { ++ public static final FunctionDescriptor DESC = FunctionDescriptor.of( ++ openssl_h.C_INT, ++ openssl_h.C_POINTER, ++ openssl_h.C_POINTER, ++ openssl_h.C_POINTER, ++ openssl_h.C_LONG ++ ); ++ ++ public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_basic_verify"); ++ ++ public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); ++ } ++ ++ /** ++ * Function descriptor for: ++ * {@snippet lang=c : ++ * int OCSP_basic_verify(OCSP_BASICRESP *bs, struct stack_st_X509 *certs, X509_STORE *st, unsigned long flags) ++ * } ++ */ ++ public static FunctionDescriptor OCSP_basic_verify$descriptor() { ++ return OCSP_basic_verify.DESC; ++ } ++ ++ /** ++ * Downcall method handle for: ++ * {@snippet lang=c : ++ * int OCSP_basic_verify(OCSP_BASICRESP *bs, struct stack_st_X509 *certs, X509_STORE *st, unsigned long flags) ++ * } ++ */ ++ public static MethodHandle OCSP_basic_verify$handle() { ++ return OCSP_basic_verify.HANDLE; ++ } ++ ++ /** ++ * Address for: ++ * {@snippet lang=c : ++ * int OCSP_basic_verify(OCSP_BASICRESP *bs, struct stack_st_X509 *certs, X509_STORE *st, unsigned long flags) ++ * } ++ */ ++ public static MemorySegment OCSP_basic_verify$address() { ++ return OCSP_basic_verify.ADDR; ++ } ++ ++ /** ++ * {@snippet lang=c : ++ * int OCSP_basic_verify(OCSP_BASICRESP *bs, struct stack_st_X509 *certs, X509_STORE *st, unsigned long flags) ++ * } ++ */ ++ public static int OCSP_basic_verify(MemorySegment bs, MemorySegment certs, MemorySegment st, long flags) { ++ var mh$ = OCSP_basic_verify.HANDLE; ++ try { ++ if (TRACE_DOWNCALLS) { ++ traceDowncall("OCSP_basic_verify", bs, certs, st, flags); ++ } ++ return (int)mh$.invokeExact(bs, certs, st, flags); ++ } catch (Error | RuntimeException ex) { ++ throw ex; ++ } catch (Throwable ex$) { ++ throw new AssertionError("should not reach here", ex$); ++ } ++ } ++ ++ private static class OCSP_check_validity { ++ public static final FunctionDescriptor DESC = FunctionDescriptor.of( ++ openssl_h.C_INT, ++ openssl_h.C_POINTER, ++ openssl_h.C_POINTER, ++ openssl_h.C_LONG, ++ openssl_h.C_LONG ++ ); ++ ++ public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_check_validity"); ++ ++ public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); ++ } ++ ++ /** ++ * Function descriptor for: ++ * {@snippet lang=c : ++ * int OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long sec, long maxsec) ++ * } ++ */ ++ public static FunctionDescriptor OCSP_check_validity$descriptor() { ++ return OCSP_check_validity.DESC; ++ } ++ ++ /** ++ * Downcall method handle for: ++ * {@snippet lang=c : ++ * int OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long sec, long maxsec) ++ * } ++ */ ++ public static MethodHandle OCSP_check_validity$handle() { ++ return OCSP_check_validity.HANDLE; ++ } ++ ++ /** ++ * Address for: ++ * {@snippet lang=c : ++ * int OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long sec, long maxsec) ++ * } ++ */ ++ public static MemorySegment OCSP_check_validity$address() { ++ return OCSP_check_validity.ADDR; ++ } ++ ++ /** ++ * {@snippet lang=c : ++ * int OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long sec, long maxsec) ++ * } ++ */ ++ public static int OCSP_check_validity(MemorySegment thisupd, MemorySegment nextupd, long sec, long maxsec) { ++ var mh$ = OCSP_check_validity.HANDLE; ++ try { ++ if (TRACE_DOWNCALLS) { ++ traceDowncall("OCSP_check_validity", thisupd, nextupd, sec, maxsec); ++ } ++ return (int)mh$.invokeExact(thisupd, nextupd, sec, maxsec); ++ } catch (Error | RuntimeException ex) { ++ throw ex; ++ } catch (Throwable ex$) { ++ throw new AssertionError("should not reach here", ex$); ++ } ++ } ++ ++ private static class OCSP_request_add1_nonce { ++ public static final FunctionDescriptor DESC = FunctionDescriptor.of( ++ openssl_h.C_INT, ++ openssl_h.C_POINTER, ++ ValueLayout.JAVA_CHAR, ++ openssl_h.C_INT ++ ); ++ ++ public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_request_add1_nonce"); ++ ++ public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); ++ } ++ ++ /** ++ * Function descriptor for: ++ * {@snippet lang=c : ++ * int OCSP_request_add1_nonce(OCSP_REQUEST *req, unsigned char *val, int len) ++ * } ++ */ ++ public static FunctionDescriptor OCSP_request_add1_nonce$descriptor() { ++ return OCSP_request_add1_nonce.DESC; ++ } ++ ++ /** ++ * Downcall method handle for: ++ * {@snippet lang=c : ++ * int OCSP_request_add1_nonce(OCSP_REQUEST *req, unsigned char *val, int len) ++ * } ++ */ ++ public static MethodHandle OCSP_request_add1_nonce$handle() { ++ return OCSP_request_add1_nonce.HANDLE; ++ } ++ ++ /** ++ * Address for: ++ * {@snippet lang=c : ++ * int OCSP_request_add1_nonce(OCSP_REQUEST *req, unsigned char *val, int len) ++ * } ++ */ ++ public static MemorySegment OCSP_request_add1_nonce$address() { ++ return OCSP_request_add1_nonce.ADDR; ++ } ++ ++ /** ++ * {@snippet lang=c : ++ * int OCSP_request_add1_nonce(OCSP_REQUEST *req, unsigned char *val, int len) ++ * } ++ */ ++ public static int OCSP_request_add1_nonce(MemorySegment req, char val, int len) { ++ var mh$ = OCSP_request_add1_nonce.HANDLE; ++ try { ++ if (TRACE_DOWNCALLS) { ++ traceDowncall("OCSP_request_add1_nonce", req, val, len); ++ } ++ return (int)mh$.invokeExact(req, val, len); ++ } catch (Error | RuntimeException ex) { ++ throw ex; ++ } catch (Throwable ex$) { ++ throw new AssertionError("should not reach here", ex$); ++ } ++ } ++ ++ private static class OCSP_check_nonce { ++ public static final FunctionDescriptor DESC = FunctionDescriptor.of( ++ openssl_h.C_INT, ++ openssl_h.C_POINTER, ++ openssl_h.C_POINTER ++ ); ++ ++ public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_check_nonce"); ++ ++ public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); ++ } ++ ++ /** ++ * Function descriptor for: ++ * {@snippet lang=c : ++ * int OCSP_check_nonce(OCSP_REQUEST *req, OCSP_BASICRESP *bs) ++ * } ++ */ ++ public static FunctionDescriptor OCSP_check_nonce$descriptor() { ++ return OCSP_check_nonce.DESC; ++ } ++ ++ /** ++ * Downcall method handle for: ++ * {@snippet lang=c : ++ * int OCSP_check_nonce(OCSP_REQUEST *req, OCSP_BASICRESP *bs) ++ * } ++ */ ++ public static MethodHandle OCSP_check_nonce$handle() { ++ return OCSP_check_nonce.HANDLE; ++ } ++ ++ /** ++ * Address for: ++ * {@snippet lang=c : ++ * int OCSP_check_nonce(OCSP_REQUEST *req, OCSP_BASICRESP *bs) ++ * } ++ */ ++ public static MemorySegment OCSP_check_nonce$address() { ++ return OCSP_check_nonce.ADDR; ++ } ++ ++ /** ++ * {@snippet lang=c : ++ * int OCSP_check_nonce(OCSP_REQUEST *req, OCSP_BASICRESP *bs) ++ * } ++ */ ++ public static int OCSP_check_nonce(MemorySegment req, MemorySegment bs) { ++ var mh$ = OCSP_check_nonce.HANDLE; ++ try { ++ if (TRACE_DOWNCALLS) { ++ traceDowncall("OCSP_check_nonce", req, bs); ++ } ++ return (int)mh$.invokeExact(req, bs); ++ } catch (Error | RuntimeException ex) { ++ throw ex; ++ } catch (Throwable ex$) { ++ throw new AssertionError("should not reach here", ex$); ++ } ++ } ++ + private static class OCSP_cert_to_id { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, + openssl_h.C_POINTER, openssl_h.C_POINTER); +diff --git a/java/org/apache/tomcat/util/openssl/openssl_h_Compatibility.java b/java/org/apache/tomcat/util/openssl/openssl_h_Compatibility.java +index 5f513182d1..e75528da40 100644 +--- a/java/org/apache/tomcat/util/openssl/openssl_h_Compatibility.java ++++ b/java/org/apache/tomcat/util/openssl/openssl_h_Compatibility.java +@@ -33,16 +33,45 @@ public class openssl_h_Compatibility { + public static final boolean OPENSSL3; + public static final boolean BORINGSSL; + public static final boolean LIBRESSL; ++ ++ public static final int MAJOR; ++ public static final int MINOR; ++ + static { + String versionString = OpenSSL_version(0).getString(0); + OPENSSL = versionString.contains("OpenSSL"); + OPENSSL3 = OPENSSL && OpenSSL_version_num() >= 0x3000000fL; + BORINGSSL = versionString.contains("BoringSSL"); + LIBRESSL = versionString.contains("LibreSSL"); ++ int majorVersion = 0; ++ int minorVersion = 0; ++ try { ++ String[] blocks = versionString.split("\\s"); ++ if (blocks.length >= 2) { ++ versionString = blocks[1]; ++ } ++ String[] versionNumberStrings = versionString.split("\\."); ++ if (versionNumberStrings.length >= 2) { ++ majorVersion = Integer.parseInt(versionNumberStrings[0]); ++ minorVersion = Integer.parseInt(versionNumberStrings[1]); ++ } ++ } catch (Exception e) { ++ // Ignore, default to 0 ++ } finally { ++ MAJOR = majorVersion; ++ MINOR = minorVersion; ++ } ++ } ++ ++ public static boolean isLibreSSLPre35() { ++ return LIBRESSL && ((MAJOR == 3 && MINOR < 5) || MAJOR < 3); + } + + // OpenSSL 1.1 FIPS_mode + public static int FIPS_mode() { ++ if (isLibreSSLPre35()) { ++ return 0; ++ } + class Holder { + static final String NAME = "FIPS_mode"; + static final FunctionDescriptor DESC = FunctionDescriptor.of(JAVA_INT); +@@ -61,6 +90,9 @@ public class openssl_h_Compatibility { + + // OpenSSL 1.1 FIPS_mode_set + public static int FIPS_mode_set(int r) { ++ if (isLibreSSLPre35()) { ++ return 0; ++ } + class Holder { + static final String NAME = "FIPS_mode_set"; + static final FunctionDescriptor DESC = FunctionDescriptor.of(JAVA_INT, JAVA_INT); +diff --git a/res/openssl/openssl-tomcat.conf b/res/openssl/openssl-tomcat.conf +index 892f3b84af..337e7a831e 100644 +--- a/res/openssl/openssl-tomcat.conf ++++ b/res/openssl/openssl-tomcat.conf +@@ -16,7 +16,7 @@ + -t org.apache.tomcat.util.openssl + -lssl + # Configure include path +--I /usr/lib/gcc/x86_64-redhat-linux/14/include ++-I /usr/lib/gcc/x86_64-redhat-linux/15/include + --output ../../java + + #### Extracted from: /usr/include/openssl/asn1.h +@@ -108,12 +108,17 @@ + --include-function OCSP_REQUEST_free # header: /usr/include/openssl/ocsp.h + --include-function OCSP_REQUEST_new # header: /usr/include/openssl/ocsp.h + --include-function OCSP_RESPONSE_free # header: /usr/include/openssl/ocsp.h ++--include-function OCSP_basic_verify # header: /usr/include/openssl/ocsp.h + --include-function OCSP_cert_to_id # header: /usr/include/openssl/ocsp.h ++--include-function OCSP_check_validity # header: /usr/include/openssl/ocsp.h ++--include-function OCSP_check_nonce # header: /usr/include/openssl/ocsp.h + --include-function OCSP_request_add0_id # header: /usr/include/openssl/ocsp.h ++--include-function OCSP_request_add1_nonce # header: /usr/include/openssl/ocsp.h + --include-function OCSP_response_get1_basic # header: /usr/include/openssl/ocsp.h + --include-function OCSP_response_status # header: /usr/include/openssl/ocsp.h + --include-function OCSP_resp_find # header: /usr/include/openssl/ocsp.h + --include-function OCSP_resp_get0 # header: /usr/include/openssl/ocsp.h ++--include-function OCSP_resp_get0_certs # header: /usr/include/openssl/ocsp.h + --include-function OCSP_single_get0_status # header: /usr/include/openssl/ocsp.h + --include-function d2i_OCSP_RESPONSE # header: /usr/include/openssl/ocsp.h + --include-function i2d_OCSP_REQUEST # header: /usr/include/openssl/ocsp.h +@@ -342,20 +347,26 @@ + --include-function X509_STORE_CTX_get_error # header: /usr/include/openssl/x509_vfy.h + --include-function X509_STORE_CTX_get_error_depth # header: /usr/include/openssl/x509_vfy.h + --include-function X509_STORE_CTX_get_ex_data # header: /usr/include/openssl/x509_vfy.h +---include-function X509_STORE_CTX_get0_current_issuer # header: /usr/include/openssl/x509_vfy.h ++--include-function X509_STORE_CTX_get0_store # header: /usr/include/openssl/x509_vfy.h + --include-function X509_STORE_CTX_get0_untrusted # header: /usr/include/openssl/x509_vfy.h ++--include-function X509_STORE_CTX_get1_issuer # header: /usr/include/openssl/x509_vfy.h + --include-function X509_STORE_CTX_set_error # header: /usr/include/openssl/x509_vfy.h + --include-function X509_STORE_set_flags # header: /usr/include/openssl/x509_vfy.h + --include-constant X509_L_ADD_DIR # header: /usr/include/openssl/x509_vfy.h + --include-constant X509_L_FILE_LOAD # header: /usr/include/openssl/x509_vfy.h +---include-constant X509_V_ERR_APPLICATION_VERIFICATION # header: /usr/include/openssl/x509_vfy.h +---include-constant X509_V_ERR_CERT_UNTRUSTED # header: /usr/include/openssl/x509_vfy.h ++--include-constant X509_V_OK # header: /usr/include/openssl/x509_vfy.h ++--include-constant X509_V_ERR_UNABLE_TO_GET_CRL # header: /usr/include/openssl/x509_vfy.h + --include-constant X509_V_ERR_CRL_HAS_EXPIRED # header: /usr/include/openssl/x509_vfy.h + --include-constant X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT # header: /usr/include/openssl/x509_vfy.h + --include-constant X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN # header: /usr/include/openssl/x509_vfy.h + --include-constant X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY # header: /usr/include/openssl/x509_vfy.h + --include-constant X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE # header: /usr/include/openssl/x509_vfy.h ++--include-constant X509_V_ERR_CERT_REVOKED # header: /usr/include/openssl/x509_vfy.h ++--include-constant X509_V_ERR_CERT_UNTRUSTED # header: /usr/include/openssl/x509_vfy.h ++--include-constant X509_V_ERR_APPLICATION_VERIFICATION # header: /usr/include/openssl/x509_vfy.h ++--include-constant X509_V_ERR_OCSP_RESP_INVALID # header: /usr/include/openssl/x509_vfy.h ++--include-constant X509_V_ERR_OCSP_SIGNATURE_FAILURE # header: /usr/include/openssl/x509_vfy.h ++--include-constant X509_V_ERR_OCSP_NOT_YET_VALID # header: /usr/include/openssl/x509_vfy.h ++--include-constant X509_V_ERR_OCSP_HAS_EXPIRED # header: /usr/include/openssl/x509_vfy.h + --include-constant X509_V_FLAG_CRL_CHECK # header: /usr/include/openssl/x509_vfy.h + --include-constant X509_V_FLAG_CRL_CHECK_ALL # header: /usr/include/openssl/x509_vfy.h +---include-constant X509_V_OK # header: /usr/include/openssl/x509_vfy.h +- +diff --git a/test/org/apache/tomcat/security/TestSecurity2017Ocsp.java b/test/org/apache/tomcat/security/TestSecurity2017Ocsp.java +new file mode 100644 +index 0000000000..2187396985 +--- /dev/null ++++ b/test/org/apache/tomcat/security/TestSecurity2017Ocsp.java +@@ -0,0 +1,107 @@ ++/* ++ * 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.security; ++ ++import java.io.IOException; ++ ++import javax.net.ssl.SSLHandshakeException; ++import javax.servlet.http.HttpServletResponse; ++ ++import org.junit.AfterClass; ++import org.junit.Assert; ++import org.junit.Assume; ++import org.junit.BeforeClass; ++import org.junit.Test; ++import org.junit.runner.RunWith; ++import org.junit.runners.Parameterized; ++ ++import org.apache.catalina.Context; ++import org.apache.catalina.startup.Tomcat; ++import org.apache.tomcat.util.buf.ByteChunk; ++import org.apache.tomcat.util.net.SSLHostConfig; ++import org.apache.tomcat.util.net.TesterSupport; ++import org.apache.tomcat.util.net.TesterSupport.SimpleServlet; ++import org.apache.tomcat.util.net.ocsp.OcspBaseTest; ++import org.apache.tomcat.util.net.ocsp.TesterOcspResponder; ++import org.apache.tomcat.util.net.openssl.OpenSSLStatus; ++ ++@RunWith(Parameterized.class) ++public class TestSecurity2017Ocsp extends OcspBaseTest { ++ ++ private static TesterOcspResponder ocspResponder; ++ ++ @BeforeClass ++ public static void startOcspResponder() { ++ ocspResponder = new TesterOcspResponder(); ++ try { ++ ocspResponder.start(); ++ } catch (IOException ioe) { ++ ocspResponder = null; ++ } ++ } ++ ++ ++ @AfterClass ++ public static void stopOcspResponder() { ++ if (ocspResponder != null) { ++ ocspResponder.stop(); ++ ocspResponder = null; ++ } ++ } ++ ++ ++ /* ++ * In addition to testing Tomcat Native (where the CVE occurred), this also tests JSSE and OpenSSl via FFM. ++ */ ++ @Test(expected=SSLHandshakeException.class) ++ public void testCVE_2017_15698() throws Exception { ++ if ("OpenSSL-FFM".equals(connectorName)) { ++ Assume.assumeFalse(OpenSSLStatus.isBoringSSL() || OpenSSLStatus.isLibreSSLPre35()); ++ } ++ Assume.assumeNotNull(ocspResponder); ++ ++ Tomcat tomcat = getTomcatInstance(); ++ ++ // No file system docBase required ++ Context ctx = tomcat.addContext("", null); ++ ++ Tomcat.addServlet(ctx, "simple", new SimpleServlet()); ++ ctx.addServletMappingDecoded("/simple", "simple"); ++ ++ // User a valid (non-revoked) server certificate ++ TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_RSA_JKS, useOpenSSLTrust); ++ ++ // Require client certificates and enable verification ++ SSLHostConfig sslHostConfig = tomcat.getConnector().findSslHostConfigs()[0]; ++ sslHostConfig.setOcspEnabled(true); ++ sslHostConfig.setCertificateVerification("required"); ++ ++ // Configure a revoked client certificate with a long AIA ++ // Don't verify the server certificate ++ TesterSupport.configureClientSsl(false, TesterSupport.CLIENT_CRL_LONG_JKS); ++ ++ // Disable soft-fail ++ sslHostConfig.setOcspSoftFail(false); ++ ++ tomcat.start(); ++ ++ int rc = getUrl("https://localhost:" + getPort() + "/simple", new ByteChunk(), false); ++ ++ // If the TLS handshake fails, the test won't get this far. ++ Assert.assertEquals(HttpServletResponse.SC_OK, rc); ++ } ++} +diff --git a/test/org/apache/tomcat/util/net/TestSSLHostConfigCipher.java b/test/org/apache/tomcat/util/net/TestSSLHostConfigCipher.java +new file mode 100644 +index 0000000000..75c8e90d00 +--- /dev/null ++++ b/test/org/apache/tomcat/util/net/TestSSLHostConfigCipher.java +@@ -0,0 +1,161 @@ ++/* ++ * 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.util.ArrayList; ++import java.util.Collection; ++import java.util.List; ++ ++import javax.net.ssl.SSLHandshakeException; ++ ++import org.junit.Assert; ++import org.junit.Assume; ++import org.junit.Test; ++import org.junit.runner.RunWith; ++import org.junit.runners.Parameterized; ++import org.junit.runners.Parameterized.Parameter; ++ ++import org.apache.catalina.Context; ++import org.apache.catalina.connector.Connector; ++import org.apache.catalina.startup.TesterServlet; ++import org.apache.catalina.startup.Tomcat; ++import org.apache.catalina.startup.TomcatBaseTest; ++import org.apache.tomcat.util.buf.ByteChunk; ++import org.apache.tomcat.util.net.openssl.OpenSSLStatus; ++ ++@RunWith(Parameterized.class) ++public class TestSSLHostConfigCipher extends TomcatBaseTest { ++ ++ private static final String CIPHER_12_AVAILABLE = "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"; ++ private static final String CIPHER_12_NOT_AVAILABLE = "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"; ++ private static final String CIPHER_13_AVAILABLE = "TLS_AES_128_GCM_SHA256"; ++ private static final String CIPHER_13_NOT_AVAILABLE = "TLS_AES_256_GCM_SHA384"; ++ ++ @Parameterized.Parameters(name = "{0}") ++ public static Collection parameters() { ++ List parameterSets = new ArrayList<>(); ++ parameterSets.add(new Object[] { ++ "JSSE", Boolean.FALSE, "org.apache.tomcat.util.net.jsse.JSSEImplementation"}); ++ parameterSets.add(new Object[] { ++ "OpenSSL", Boolean.TRUE, "org.apache.tomcat.util.net.openssl.OpenSSLImplementation"}); ++ parameterSets.add(new Object[] { ++ "OpenSSL-FFM", Boolean.TRUE, "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation"}); ++ ++ return parameterSets; ++ } ++ ++ @Parameter(0) ++ public String connectorName; ++ ++ @Parameter(1) ++ public boolean useOpenSSL; ++ ++ @Parameter(2) ++ public String sslImplementationName; ++ ++ ++ @Override ++ public void setUp() throws Exception { ++ super.setUp(); ++ ++ Tomcat tomcat = getTomcatInstance(); ++ ++ // Server-side TLS configuration ++ TesterSupport.initSsl(tomcat); ++ TesterSupport.configureSSLImplementation(tomcat, sslImplementationName, useOpenSSL); ++ ++ // Test specific, server-side cipher & protocol configuration ++ SSLHostConfig sslHostConfig = getSSLHostConfig(); ++ sslHostConfig.setProtocols("+TLSv1.2+TLSv1.3"); ++ sslHostConfig.setCiphers(CIPHER_12_AVAILABLE); ++ sslHostConfig.setCipherSuites(CIPHER_13_AVAILABLE); ++ ++ // Simple webapp ++ Context ctxt = getProgrammaticRootContext(); ++ Tomcat.addServlet(ctxt, "TesterServlet", new TesterServlet()); ++ ctxt.addServletMappingDecoded("/*", "TesterServlet"); ++ } ++ ++ ++ @Test ++ public void testTls12CipherAvailable() throws Exception { ++ if ("OpenSSL-FFM".equals(connectorName)) { ++ // The functionality works, but the two ciphers used are not available ++ Assume.assumeFalse(OpenSSLStatus.isBoringSSL()); ++ } ++ // Client-side TLS configuration ++ TesterSupport.configureClientSsl(true, new String[] { CIPHER_12_AVAILABLE } ); ++ ++ doTest(); ++ } ++ ++ ++ @Test(expected=SSLHandshakeException.class) ++ public void testTls12CipherNotAvailable() throws Exception { ++ if ("OpenSSL-FFM".equals(connectorName)) { ++ Assume.assumeFalse(OpenSSLStatus.isBoringSSL()); ++ } ++ // Client-side TLS configuration ++ TesterSupport.configureClientSsl(true, new String[] { CIPHER_12_NOT_AVAILABLE } ); ++ ++ doTest(); ++ } ++ ++ ++ @Test ++ public void testTls13CipherAvailable() throws Exception { ++ if ("OpenSSL-FFM".equals(connectorName)) { ++ Assume.assumeFalse(OpenSSLStatus.isBoringSSL()); ++ } ++ // Client-side TLS configuration ++ TesterSupport.configureClientSsl(new String[] { CIPHER_13_AVAILABLE } ); ++ ++ doTest(); ++ } ++ ++ ++ @Test(expected=SSLHandshakeException.class) ++ public void testTls13CipherNotAvailable() throws Exception { ++ if ("OpenSSL-FFM".equals(connectorName)) { ++ // The TLS 1.3 call might not be present ++ Assume.assumeFalse(OpenSSLStatus.isLibreSSLPre35()); ++ Assume.assumeFalse(OpenSSLStatus.isBoringSSL()); ++ } ++ // Client-side TLS configuration ++ TesterSupport.configureClientSsl(new String[] { CIPHER_13_NOT_AVAILABLE } ); ++ ++ doTest(); ++ } ++ ++ ++ private void doTest() throws Exception { ++ Tomcat tomcat = getTomcatInstance(); ++ ++ tomcat.start(); ++ ++ // Check a request can be made ++ ByteChunk res = getUrl("https://localhost:" + getPort() + "/"); ++ Assert.assertEquals("OK", res.toString()); ++ } ++ ++ ++ private SSLHostConfig getSSLHostConfig() { ++ Tomcat tomcat = getTomcatInstance(); ++ Connector connector = tomcat.getConnector(); ++ return connector.findSslHostConfigs()[0]; ++ } ++} +diff --git a/test/org/apache/tomcat/util/net/TestSsl.java b/test/org/apache/tomcat/util/net/TestSsl.java +index 5aeada0951..e21b61da93 100644 +--- a/test/org/apache/tomcat/util/net/TestSsl.java ++++ b/test/org/apache/tomcat/util/net/TestSsl.java +@@ -251,7 +251,7 @@ public class TestSsl extends TomcatBaseTest { + Context ctxt = tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); + ctxt.addApplicationListener(WsContextListener.class.getName()); + +- TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_KEYPASS_JKS, ++ TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_KEYPASS_JKS, false, + TesterSupport.JKS_PASS, null, TesterSupport.JKS_KEY_PASS, null); + + TesterSupport.configureSSLImplementation(tomcat, sslImplementationName, useOpenSSL); +@@ -274,7 +274,7 @@ public class TestSsl extends TomcatBaseTest { + Context ctxt = tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); + ctxt.addApplicationListener(WsContextListener.class.getName()); + +- TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_KEYPASS_JKS, ++ TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_KEYPASS_JKS, false, + null, TesterSupport.JKS_PASS_FILE, null, TesterSupport.JKS_KEY_PASS_FILE); + + TesterSupport.configureSSLImplementation(tomcat, sslImplementationName, useOpenSSL); +diff --git a/test/org/apache/tomcat/util/net/TesterSupport.java b/test/org/apache/tomcat/util/net/TesterSupport.java +index 711cbadfc8..f9ff77499e 100644 +--- a/test/org/apache/tomcat/util/net/TesterSupport.java ++++ b/test/org/apache/tomcat/util/net/TesterSupport.java +@@ -55,11 +55,11 @@ import org.apache.catalina.LifecycleListener; + import org.apache.catalina.authenticator.SSLAuthenticator; + import org.apache.catalina.connector.Connector; + import org.apache.catalina.core.AprLifecycleListener; +-import org.apache.catalina.core.AprStatus; + import org.apache.catalina.core.OpenSSLLifecycleListener; + import org.apache.catalina.core.StandardServer; + import org.apache.catalina.startup.TesterMapRealm; + import org.apache.catalina.startup.Tomcat; ++import org.apache.tomcat.jni.AprStatus; + import org.apache.tomcat.util.descriptor.web.LoginConfig; + import org.apache.tomcat.util.descriptor.web.SecurityCollection; + import org.apache.tomcat.util.descriptor.web.SecurityConstraint; +@@ -87,6 +87,9 @@ public final class TesterSupport { + public static final String LOCALHOST_EC_KEY_PEM = SSL_DIR + "localhost-ec-key.pem"; + public static final String LOCALHOST_RSA_CERT_PEM = SSL_DIR + "localhost-rsa-cert.pem"; + public static final String LOCALHOST_RSA_KEY_PEM = SSL_DIR + "localhost-rsa-key.pem"; ++ public static final String DB_INDEX = SSL_DIR + "index.db"; ++ public static final String OCSP_RESPONDER_RSA_CERT = SSL_DIR + "oscp-responder-rsa-cert.pem"; ++ public static final String OCSP_RESPONDER_RSA_KEY = SSL_DIR + "oscp-responder-rsa-key.pem"; + public static final boolean TLSV13_AVAILABLE; + + public static final String ROLE = "testrole"; +diff --git a/test/org/apache/tomcat/util/net/ca-cert.pem b/test/org/apache/tomcat/util/net/ca-cert.pem +index a5171578c1..e8e344f6b1 100644 +--- a/test/org/apache/tomcat/util/net/ca-cert.pem ++++ b/test/org/apache/tomcat/util/net/ca-cert.pem +@@ -1,39 +1,39 @@ + -----BEGIN CERTIFICATE----- +-MIIGvzCCBKegAwIBAgIUd8TEpsZJz7k3Ddw/oRfCrdlmRlcwDQYJKoZIhvcNAQEL +-BQAwgZMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJNQTESMBAGA1UEBxMJV2FrZWZp +-ZWxkMScwJQYDVQQKEx5UaGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xGjAY +-BgNVBAsTEUFwYWNoZSBUb21jYXQgUE1DMR4wHAYDVQQDExVBcGFjaGUgVG9tY2F0 +-IFRlc3QgQ0EwHhcNMjUwODE3MDczNDAwWhcNMzUwODE1MDczNDAwWjCBkzELMAkG +-A1UEBhMCVVMxCzAJBgNVBAgTAk1BMRIwEAYDVQQHEwlXYWtlZmllbGQxJzAlBgNV +-BAoTHlRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbjEaMBgGA1UECxMRQXBh +-Y2hlIFRvbWNhdCBQTUMxHjAcBgNVBAMTFUFwYWNoZSBUb21jYXQgVGVzdCBDQTCC +-AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK6iRe7N1DwdOhFVwvTsB3Ed +-b1aZoylw9Zv+/JB1y6Fr4lcn1a753t+nGml67GeMOYMJk2jSSKYjudggQMx3dZV6 +-fvlqgxmdDv6838ZlBfHNI/Pa/NNnIQRMCqzsOwbA7kIrbLmxYqXnv5ALP837EzsT +-A7I2Sa37vEjV5D7PaxQ0tEwrtpuibyeb/BV+0YBciKuJuZOq6SjGOvx2kQODvtUL +-L16p7qRyKXoW+euZismqavDMaV1SXDiOoeXUgmcnnPbzW/PNDRTlQt6K4GkMZdw4 +-ehofzNjGKUFWk+5ENEE5xfXt7k2i5m0NMV6aQmrmezc8O35aMm+5g1IQssFuc/5L +-VXqa34xT0+wXpN2VVlnHEUP2E9ioDPKbAkbNMpsTyO5mu5CzAviJ+3IQuYs+Mix6 +-1W1eJ+xQ1rLoJ8Ovmw3qv4ouuElZSqGKLaYfWPHhwuaV/fTxM5zzROV2+8j1x+tv +-n3r3nhwQT7t2VpS+dFsEB5U0c0eXl7rx+/CkqC29CG2DWtnbDV+hKQDLI1GRitRk +-p7jO1TH0TbD2oibc6RnIxw9MpiQFVqFHorVsKjr25pSvFjg1piU98p0mYZ+Kj8nZ +-YZ2our2driIdHJilvnMq9iYf1w2maflXjbKGZfu2MwnNTJbDwrFekT+oRUHbCXXX +-A2C4QvwJpnL7e7SJoKptAgMBAAGjggEHMIIBAzAdBgNVHQ4EFgQUAPKYTSEsADxA +-m4T03irwJu4yDp8wgdMGA1UdIwSByzCByIAUAPKYTSEsADxAm4T03irwJu4yDp+h +-gZmkgZYwgZMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJNQTESMBAGA1UEBxMJV2Fr +-ZWZpZWxkMScwJQYDVQQKEx5UaGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24x +-GjAYBgNVBAsTEUFwYWNoZSBUb21jYXQgUE1DMR4wHAYDVQQDExVBcGFjaGUgVG9t +-Y2F0IFRlc3QgQ0GCFHfExKbGSc+5Nw3cP6EXwq3ZZkZXMAwGA1UdEwQFMAMBAf8w +-DQYJKoZIhvcNAQELBQADggIBAD839dBKpesTMCsiSrx1GXP6hbjXfNtKcskzlce3 +-zGYDdIUFYit1gnuftvcl4u2hDCqodCn30LUE6wRQd3E2hiItOzlWufp4Rb6KZNie +-xp/A5ifu90qbJgEBlZScLDydcvkts1JTF4WuzQdKGY5MEh1OgNPj1M3cwg92UmGJ +-fGU57jnvpaJxS9IoJXIeIpmCr5dM1nk5NMY3Vt2Fg+vssHLS9t7glKQBlSa4+Ts6 +-26xWINXQvJOomWWEDrs96pzZ2AHAJ+L/eExofAa6FyCXB1Oxd5M+U8A9DgehHVqi +-9r5/FMi3nS+v8UR0/4KpAUPjKAMCyZZbLFza0o0sHYWKehG/NdAUio4jKJKFCh/Z +-bQxGtPNW92N0FOQaWv3TUCbfv5bDtX6RC8b9D8u/bvUlYDYnT+c/2Z1RQHwxuoyF +-+/514VUZztpr/DBidlUlHBpqsAbRAHsoRVtn1xjTXraDRHewnvdA/AV1f+PuIKJf +-4DAnUCn7OKExUAFe87EZizdoaKu5lr0kNUhXdKKVgqGrSolHD67etEviHjAqEJdn +-mPO1aIeG3ukFGh3vp8/SFZk9J34qZ5jEl+QldIhAEFWHEX5wmZ74GatAUnOSmiHq +-xSQMB2ULGTOC1HMBUQMgelY4Qiwsa92pO1qySsZIL5npmBM9ok+o/jshCpoTCCin +-nzrk ++MIIGwjCCBKqgAwIBAgIUb23jgHGhJDQJaz0iHUfKwRV5z5kwDQYJKoZIhvcNAQEL ++BQAwgZQxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJERTETMBEGA1UEBxMKV2lsbWlu ++Z3RvbjEnMCUGA1UEChMeVGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMRow ++GAYDVQQLExFBcGFjaGUgVG9tY2F0IFBNQzEeMBwGA1UEAxMVQXBhY2hlIFRvbWNh ++dCBUZXN0IENBMB4XDTI1MTIwNTE5MTIzOFoXDTM1MTIwMzE5MTIzOFowgZQxCzAJ ++BgNVBAYTAlVTMQswCQYDVQQIEwJERTETMBEGA1UEBxMKV2lsbWluZ3RvbjEnMCUG ++A1UEChMeVGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFB ++cGFjaGUgVG9tY2F0IFBNQzEeMBwGA1UEAxMVQXBhY2hlIFRvbWNhdCBUZXN0IENB ++MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqeIoIFXsR468dJCUYnNX ++E4DLI9J2jLIz/T1Jiq/tA9XCzA+XFqf3tlZdXLsfQhDLg0lf+wHqQfSvbQ0Gcatn ++Iw8BIvA7yq6PDKsO4cjQ2EdrkTn2JD7hJW5uUB9Je95TUUbKuNxOatihwS4DNU2J ++aoI2R2iUGasXsawVR+4qj1UJByawlsGHxFCqhVoaUKM/fKXK7LllbyHsRxuZTgab ++Spmvj2jcMxyeDN2ZjlBdsrSg+8FpYvnPKzHpdFiA6d4pxU6QJbeDeWPQ6B64yWi1 ++eImFvilqZQoQ3RR/1gDjoFuLCdWK7mAvfaSXFzax1YK3D/MiCowTZ8M0iWmGqMg1 ++QF6luhPL+5p8Q2/KooE3CZol0IpCUGr3cQs3FC1deL5ySNMYniXQMgxGqVE3x0i1 ++Wlf5bItGY0Ha9vc+s4vmnRZxW4O+fXiuG9OXS5XjfWGAjzkJ6wXgFRJKbWDsSPkR ++Sw9j9SAsLE6cycY66eMl94QdOl6KRXVyeaLHJbRMBux7WvDXYS/eWKFHjmPYNKq6 ++btIB7ELTqoMuwTAWj83bdkQgMac5fsTozKduM+C63sSg3mhmyy6yVgCX1eECqPNk ++N4K4oWdhQvKYvCp1n1EySAupZqrxudBLp/LDHRfGh7gfAZgP8hxRaoBqh2u0H8Fh ++RwSAML8zEeRH/si3G7LfXzECAwEAAaOCAQgwggEEMB0GA1UdDgQWBBSsh9yLPYrb ++iUh+bzscxnCSCRdv+TCB1AYDVR0jBIHMMIHJgBSsh9yLPYrbiUh+bzscxnCSCRdv +++aGBmqSBlzCBlDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkRFMRMwEQYDVQQHEwpX ++aWxtaW5ndG9uMScwJQYDVQQKEx5UaGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRp ++b24xGjAYBgNVBAsTEUFwYWNoZSBUb21jYXQgUE1DMR4wHAYDVQQDExVBcGFjaGUg ++VG9tY2F0IFRlc3QgQ0GCFG9t44BxoSQ0CWs9Ih1HysEVec+ZMAwGA1UdEwQFMAMB ++Af8wDQYJKoZIhvcNAQELBQADggIBAA/DyqPhxRAluY1e1S9L2SkkrcYePjb033Wl ++ZmMVdXvf6LZtpjquNz81ThNoX5yfgV6CawDRw7COI4dAU3HOLVtOeKybm11Jq1BH ++AWmlZVQIOplgnitoEoZi8brxEpSgeyRGLKHYaW1YxshPRC5tVDKsbyYBtvmTaBtK ++dMirc36cEDkSfEe2dq+MkHgzHyyvySqTSlI/MH7CKQYBjGg2D+Gcle/BNrK02wef ++Rol9M3R3skDfV/P0NFmrfFi4B/cXt/EFxm1thMsQ6ofd0GUMGgv6fJKN1AVDihx6 ++sQvbn712+54ilkcWMa3gW5L/BIxcDS+ZZiO/7T2tZ2d3tcz86gYbFkiQbWelnTmn ++FBSHno9PDgxRWZzqauKqlsczAsz5qLf8zF+GDubEcxvOgEf0wQ/vPwtSb2R4egb+ ++3Vn+InqDy9/78ug497KV842jxQy2G6wZLND0q81H04t8NYQopbX4DI0hS5BJnKuV ++TM3rh4C/hNXs8kUpjusqnnV8nbBpab2R1YzEOefhgtTFdk/dHI7a5+6BKNSK55yn ++hDqgemD5p+5A6FLu3M0RDgbTZ1WdrtYDvQZrAMB10mrV0YDoDgnEzGYEc/PQwkYF ++gOeqb97mOdjf9YUJli4S4XLkZ5zn/IitEQRbnKen2VE60qIgW/UMYKTiwuSZtRaW ++gxBAeoi3 + -----END CERTIFICATE----- +diff --git a/test/org/apache/tomcat/util/net/ca.jks b/test/org/apache/tomcat/util/net/ca.jks +index 615060a41f..d740b45124 100644 +Binary files a/test/org/apache/tomcat/util/net/ca.jks and b/test/org/apache/tomcat/util/net/ca.jks differ +diff --git a/test/org/apache/tomcat/util/net/index.db b/test/org/apache/tomcat/util/net/index.db +new file mode 100644 +index 0000000000..eb521a4242 +--- /dev/null ++++ b/test/org/apache/tomcat/util/net/index.db +@@ -0,0 +1,6 @@ ++V 271205191323Z 1000 unknown /C=US/ST=DE/L=Wilmington/O=The Apache Software Foundation/OU=Apache Tomcat PMC/CN=localhost ++R 271205191327Z 251205192921Z 1001 unknown /C=US/ST=DE/L=Wilmington/O=The Apache Software Foundation/OU=Apache Tomcat PMC/CN=localhost ++V 271205191331Z 1002 unknown /C=US/ST=DE/L=Wilmington/O=The Apache Software Foundation/OU=Apache Tomcat PMC/CN=user1 ++R 271205191335Z 251205192924Z 1003 unknown /C=US/ST=DE/L=Wilmington/O=The Apache Software Foundation/OU=Apache Tomcat PMC/CN=user2 ++V 271205191540Z 1004 unknown /C=US/ST=DE/L=Wilmington/O=The Apache Software Foundation/OU=Apache Tomcat PMC/CN=localhost ++V 271205193355Z 1005 unknown /C=US/ST=DE/L=Wilmington/O=The Apache Software Foundation/OU=Apache Tomcat PMC/CN=OCSP Responder +diff --git a/test/org/apache/tomcat/util/net/index.db.attr b/test/org/apache/tomcat/util/net/index.db.attr +new file mode 100644 +index 0000000000..3a7e39e6ee +--- /dev/null ++++ b/test/org/apache/tomcat/util/net/index.db.attr +@@ -0,0 +1 @@ ++unique_subject = no +diff --git a/test/org/apache/tomcat/util/net/localhost-crl-rsa-cert.pem b/test/org/apache/tomcat/util/net/localhost-crl-rsa-cert.pem +new file mode 100644 +index 0000000000..7cfe515a25 +--- /dev/null ++++ b/test/org/apache/tomcat/util/net/localhost-crl-rsa-cert.pem +@@ -0,0 +1,108 @@ ++Certificate: ++ Data: ++ Version: 3 (0x2) ++ Serial Number: 4097 (0x1001) ++ Signature Algorithm: sha256WithRSAEncryption ++ Issuer: C=US, ST=DE, L=Wilmington, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=Apache Tomcat Test CA ++ Validity ++ Not Before: Dec 5 19:13:27 2025 GMT ++ Not After : Dec 5 19:13:27 2027 GMT ++ Subject: C=US, ST=DE, L=Wilmington, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=localhost ++ Subject Public Key Info: ++ Public Key Algorithm: rsaEncryption ++ Public-Key: (2048 bit) ++ Modulus: ++ 00:aa:f2:ee:01:dd:21:fd:4b:f6:1e:9f:a1:a6:65: ++ 42:71:1c:db:cf:d7:47:6d:20:81:63:01:c7:6c:a2: ++ 5a:2e:17:0d:5d:d9:60:78:e5:d9:2a:9a:c0:2d:1c: ++ 2d:24:9d:b3:f6:26:a9:93:80:86:ad:0e:b0:06:4e: ++ 64:dc:11:da:70:a0:eb:47:aa:9d:39:3e:10:1c:b5: ++ fe:bc:ca:b2:5b:b8:3f:0c:a8:d4:b3:cb:9e:dd:f5: ++ 19:0e:dc:83:34:54:1d:17:b9:0d:2e:5d:4c:31:d7: ++ 9b:64:e9:40:89:de:77:75:ad:6e:86:3e:46:15:88: ++ ed:48:88:bb:a2:77:a5:05:4a:33:33:3e:bb:cb:7b: ++ f7:00:9b:eb:4e:2b:f5:4a:59:07:ff:18:5a:a1:49: ++ c2:25:48:21:03:55:64:da:d7:75:0f:e8:55:a8:61: ++ 39:ca:af:09:64:c2:c1:67:4b:ec:95:df:66:50:2d: ++ 39:45:d6:4c:8d:07:a8:3d:d1:fc:3c:46:76:92:2f: ++ 1a:b0:27:f0:0c:e8:19:2b:c6:b4:ad:62:c4:c6:21: ++ 7c:23:17:c4:13:a4:0e:7d:d3:05:3b:0b:43:ab:43: ++ 32:88:12:67:91:1b:01:e4:98:90:fc:67:88:d7:8b: ++ 8e:6b:f0:2a:59:62:d1:dc:24:38:28:8c:b7:e9:1b: ++ 7b:27 ++ Exponent: 65537 (0x10001) ++ X509v3 extensions: ++ X509v3 Basic Constraints: ++ CA:FALSE ++ Netscape Comment: ++ OpenSSL Generated Certificate ++ X509v3 Subject Key Identifier: ++ CB:A6:D2:18:07:CE:1A:E1:A7:F1:DC:FB:4C:A0:2C:85:AE:A5:40:A2 ++ X509v3 Authority Key Identifier: ++ AC:87:DC:8B:3D:8A:DB:89:48:7E:6F:3B:1C:C6:70:92:09:17:6F:F9 ++ Authority Information Access: ++ OCSP - URI:http://127.0.0.1:8888 ++ X509v3 Subject Alternative Name: ++ DNS:localhost, IP Address:127.0.0.1 ++ Signature Algorithm: sha256WithRSAEncryption ++ Signature Value: ++ 11:8b:03:aa:50:38:53:88:71:66:01:2f:d5:ab:82:49:ba:4d: ++ 45:5a:20:b6:f7:6e:1f:ca:77:ce:f7:93:85:ec:4d:da:ba:17: ++ 71:dc:30:7b:7f:55:35:07:88:a2:4a:47:06:9c:8c:1b:a0:33: ++ 5f:46:dd:c9:c6:f5:0e:27:48:6e:1f:0b:a0:e8:17:e4:16:3b: ++ d0:e3:0b:56:4f:5c:5f:b7:ae:fb:da:46:13:0b:fc:e8:3d:72: ++ 7e:ab:2d:19:4d:6c:09:8b:32:51:9a:7a:b8:49:70:7e:ce:ce: ++ 78:7f:95:9c:3a:8e:05:cc:29:af:0b:de:42:b8:cd:5e:56:a8: ++ 2f:78:ea:19:67:ed:a7:59:c0:ce:0a:4c:50:46:67:9a:6e:72: ++ 6b:fd:ae:00:a6:2f:9a:93:97:39:58:06:b4:05:a3:78:a9:b6: ++ df:66:22:0d:ae:5b:fd:e8:37:78:25:ac:2e:5a:a0:65:56:3e: ++ 83:04:bf:98:96:30:e1:11:f9:da:3b:6a:97:c4:77:28:d6:d7: ++ 46:27:a4:05:67:2e:f5:b8:d4:2e:c7:00:39:49:08:9b:a7:58: ++ 21:48:4c:fb:8f:dd:99:69:ea:59:bc:41:96:fd:1a:29:1a:fe: ++ 53:16:6a:2b:ac:92:51:18:ac:85:d9:7d:d5:77:ad:60:63:98: ++ 47:2c:52:ec:85:ef:ad:ff:b0:58:d7:12:7b:6f:70:2b:93:2a: ++ 92:f8:99:3c:15:7a:26:d4:79:16:05:93:9a:f4:01:bc:e6:9b: ++ a0:31:c3:d7:12:23:47:12:81:30:02:06:0c:13:ca:38:4c:63: ++ 96:2d:17:41:53:06:8b:c5:a3:8c:c3:96:b7:e2:b5:85:49:f7: ++ cd:46:01:21:ff:4f:d1:65:be:37:1b:84:7c:79:a6:e1:a8:a8: ++ d7:8c:63:34:35:45:d5:42:10:03:0f:60:fa:52:c5:39:03:26: ++ b1:41:6c:b9:53:3c:cf:8d:5d:fa:02:ac:0a:7a:04:27:28:df: ++ 33:b2:01:69:d8:3e:72:38:57:60:d3:a8:47:5f:98:55:8c:55: ++ 15:36:58:6e:c7:fc:65:a5:f8:c7:7f:1a:8c:de:b7:79:21:50: ++ 98:76:69:8a:b3:48:b0:1d:b2:91:fd:25:8f:bd:59:17:81:22: ++ bc:67:32:d5:fb:e6:c3:cd:3c:b3:79:6f:cd:1c:4a:b0:04:13: ++ 3c:af:80:53:7c:f0:c6:28:f3:37:3e:36:4d:65:ef:38:dc:00: ++ 5f:88:7e:04:34:75:66:fa:ba:6c:1f:76:b2:55:bb:21:60:fe: ++ b8:48:f8:ac:cb:6e:16:2f:75:87:44:d0:c3:6d:d5:6a:e1:e6: ++ b9:32:7e:39:a6:e6:d0:7f ++-----BEGIN CERTIFICATE----- ++MIIFZjCCA06gAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwgZQxCzAJBgNVBAYTAlVT ++MQswCQYDVQQIEwJERTETMBEGA1UEBxMKV2lsbWluZ3RvbjEnMCUGA1UEChMeVGhl ++IEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFBcGFjaGUgVG9t ++Y2F0IFBNQzEeMBwGA1UEAxMVQXBhY2hlIFRvbWNhdCBUZXN0IENBMB4XDTI1MTIw ++NTE5MTMyN1oXDTI3MTIwNTE5MTMyN1owgYgxCzAJBgNVBAYTAlVTMQswCQYDVQQI ++EwJERTETMBEGA1UEBxMKV2lsbWluZ3RvbjEnMCUGA1UEChMeVGhlIEFwYWNoZSBT ++b2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFBcGFjaGUgVG9tY2F0IFBNQzES ++MBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC ++AQEAqvLuAd0h/Uv2Hp+hpmVCcRzbz9dHbSCBYwHHbKJaLhcNXdlgeOXZKprALRwt ++JJ2z9iapk4CGrQ6wBk5k3BHacKDrR6qdOT4QHLX+vMqyW7g/DKjUs8ue3fUZDtyD ++NFQdF7kNLl1MMdebZOlAid53da1uhj5GFYjtSIi7onelBUozMz67y3v3AJvrTiv1 ++SlkH/xhaoUnCJUghA1Vk2td1D+hVqGE5yq8JZMLBZ0vsld9mUC05RdZMjQeoPdH8 ++PEZ2ki8asCfwDOgZK8a0rWLExiF8IxfEE6QOfdMFOwtDq0MyiBJnkRsB5JiQ/GeI ++14uOa/AqWWLR3CQ4KIy36Rt7JwIDAQABo4HLMIHIMAkGA1UdEwQCMAAwLAYJYIZI ++AYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQW ++BBTLptIYB84a4afx3PtMoCyFrqVAojAfBgNVHSMEGDAWgBSsh9yLPYrbiUh+bzsc ++xnCSCRdv+TAxBggrBgEFBQcBAQQlMCMwIQYIKwYBBQUHMAGGFWh0dHA6Ly8xMjcu ++MC4wLjE6ODg4ODAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcN ++AQELBQADggIBABGLA6pQOFOIcWYBL9Wrgkm6TUVaILb3bh/Kd873k4XsTdq6F3Hc ++MHt/VTUHiKJKRwacjBugM19G3cnG9Q4nSG4fC6DoF+QWO9DjC1ZPXF+3rvvaRhML ++/Og9cn6rLRlNbAmLMlGaerhJcH7Oznh/lZw6jgXMKa8L3kK4zV5WqC946hln7adZ ++wM4KTFBGZ5pucmv9rgCmL5qTlzlYBrQFo3iptt9mIg2uW/3oN3glrC5aoGVWPoME ++v5iWMOER+do7apfEdyjW10YnpAVnLvW41C7HADlJCJunWCFITPuP3Zlp6lm8QZb9 ++Gika/lMWaiusklEYrIXZfdV3rWBjmEcsUuyF763/sFjXEntvcCuTKpL4mTwVeibU ++eRYFk5r0Abzmm6Axw9cSI0cSgTACBgwTyjhMY5YtF0FTBovFo4zDlrfitYVJ981G ++ASH/T9FlvjcbhHx5puGoqNeMYzQ1RdVCEAMPYPpSxTkDJrFBbLlTPM+NXfoCrAp6 ++BCco3zOyAWnYPnI4V2DTqEdfmFWMVRU2WG7H/GWl+Md/Gozet3khUJh2aYqzSLAd ++spH9JY+9WReBIrxnMtX75sPNPLN5b80cSrAEEzyvgFN88MYo8zc+Nk1l7zjcAF+I ++fgQ0dWb6umwfdrJVuyFg/rhI+KzLbhYvdYdE0MNt1Wrh5rkyfjmm5tB/ ++-----END CERTIFICATE----- +diff --git a/test/org/apache/tomcat/util/net/localhost-crl-rsa-key.pem b/test/org/apache/tomcat/util/net/localhost-crl-rsa-key.pem +new file mode 100644 +index 0000000000..f8de8c251b +--- /dev/null ++++ b/test/org/apache/tomcat/util/net/localhost-crl-rsa-key.pem +@@ -0,0 +1,28 @@ ++-----BEGIN PRIVATE KEY----- ++MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCq8u4B3SH9S/Ye ++n6GmZUJxHNvP10dtIIFjAcdsolouFw1d2WB45dkqmsAtHC0knbP2JqmTgIatDrAG ++TmTcEdpwoOtHqp05PhActf68yrJbuD8MqNSzy57d9RkO3IM0VB0XuQ0uXUwx15tk ++6UCJ3nd1rW6GPkYViO1IiLuid6UFSjMzPrvLe/cAm+tOK/VKWQf/GFqhScIlSCED ++VWTa13UP6FWoYTnKrwlkwsFnS+yV32ZQLTlF1kyNB6g90fw8RnaSLxqwJ/AM6Bkr ++xrStYsTGIXwjF8QTpA590wU7C0OrQzKIEmeRGwHkmJD8Z4jXi45r8CpZYtHcJDgo ++jLfpG3snAgMBAAECggEAGwmoPlA454SjrU1HNneujhBn7dQZXne1LzEhVxvIkqCf ++wxe2C/kio3vtaFUcCL4OsCCGUqeR5X48tgqhJjOGrqis/d1M17hquPfKDhcrJZmb ++E0gCuFs8ydNRvsl+0QB6x31DyfEEs59r2waPaB7xGwIuyHnCAPbyvLWvo68zwQWs ++q3YF8Z03wPoSVxx9IrIsi01vaZGYREXmsNEX5krvd+Ku2JsQJ9NreduMg/gtegbB ++snN9MpjbydRWnwql4YzOCPaaGlQltrUFixKw6InZJRkKaSri49gEseaiSqwx31mY ++RvnWtyZfq1qRkcQm+NC9WO3cyCH38Um1egQmVpmguQKBgQDUlfAuAotMDN9aFnQ5 ++DUviMyVy+ea1SDvJ3TnoaplZ4vp9pmwX5B2RPz8MzHKVLhll9h6GjAIXZuLT2Fed ++OyGUhM0Cd//+5esp0dHMKQFdzVMrhn8eGPPPBReEZtILbi3ulRJI6nsagAmKnLLN ++bik5fbcsc1KhtIUzGFbrw9ThWQKBgQDN3DTFrxP50XLc1hfs3OjP3OqZ7mqMyVXq ++JFJ4JNiFCFk/Jhqz83IuxrzoWGVmtnDjj9sdN/TsClG337xD9L3OgpS1yjjbzhi5 ++wYG4cCjNxpqis097LFHqZDBBSZ9d3SWsdENqSWPbWOVLZQKrwR239m3mjCipSewI ++JRnX+1cwfwKBgQCrySoYFAg6gWBvrRtoCv2aIZiOi7DKJz/hRPsJfDLFtyySIszQ ++lY7rE3/AuOmS8XimszdBpJwACy0a4YUTUng1Swdbilr8wRDCb4Ioh65J/aTK1Fme ++ma1TShsR7ACqKfPGCwKGl6y44mRTdYLrjKyVf6horBxG/dhxTKbYyBwbqQKBgQDE ++vQPuPXFg2ivlI/L/muwg46eN704DONOUDpyGV+hZ022/rUHt4uaoD6UwhHJ8ZDWl ++NbGZzgWTpBUPHpMFexv/BcrrpdULNH9q36WCyCYm6vyUK7v1Ipky4gdADgVxpk0/ ++8GkRZgw58E5K7MFNtiUZ0DieEis2BwC9k/+L65gbLwKBgG87Ag/C0un6LYMHI8gT ++DyqrsjARdlwVeyL864p3GHQbfGeQ2VTxSriEPHwSc2H1FtRsFo06P98DBjVW1xDD ++MPY8PD9MZ347rnNi1lcG42moxY6ywYesRwKaDPC98PgvaXzmm81wSyjOerwDQubi ++UQ6bYhVLe3BdVfFI8N8kSRzu ++-----END PRIVATE KEY----- +diff --git a/test/org/apache/tomcat/util/net/localhost-crl-rsa.jks b/test/org/apache/tomcat/util/net/localhost-crl-rsa.jks +new file mode 100644 +index 0000000000..1519eab143 +Binary files /dev/null and b/test/org/apache/tomcat/util/net/localhost-crl-rsa.jks differ +diff --git a/test/org/apache/tomcat/util/net/localhost-ec-cert.pem b/test/org/apache/tomcat/util/net/localhost-ec-cert.pem +index 341e6f72da..520b300a8b 100644 +--- a/test/org/apache/tomcat/util/net/localhost-ec-cert.pem ++++ b/test/org/apache/tomcat/util/net/localhost-ec-cert.pem +@@ -1,22 +1,22 @@ + Certificate: + Data: + Version: 3 (0x2) +- Serial Number: 4113 (0x1011) ++ Serial Number: 4100 (0x1004) + Signature Algorithm: sha256WithRSAEncryption +- Issuer: C=US, ST=MA, L=Wakefield, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=Apache Tomcat Test CA ++ Issuer: C=US, ST=DE, L=Wilmington, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=Apache Tomcat Test CA + Validity +- Not Before: Feb 16 17:59:33 2025 GMT +- Not After : Feb 16 17:59:33 2027 GMT ++ Not Before: Dec 5 19:15:40 2025 GMT ++ Not After : Dec 5 19:15:40 2027 GMT + Subject: C=US, ST=DE, L=Wilmington, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=localhost + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: +- 04:0a:0a:c9:36:e4:18:27:f0:ef:ac:f3:8a:8b:a8: +- 9c:15:f5:71:95:94:10:bc:74:4a:c5:1e:e7:c5:fd: +- b0:b3:35:c0:5e:fe:11:2b:ae:ba:a7:a4:27:09:bd: +- 37:c5:de:bc:e7:6d:f7:de:dd:ab:ed:9d:cf:02:59: +- 1e:ba:b2:6a:b0 ++ 04:4a:67:a8:52:9a:a4:a8:54:e0:0a:26:01:7b:c8: ++ 20:fc:99:99:09:b9:ce:a4:11:bd:99:0f:9d:73:b3: ++ 82:ef:9f:a9:73:31:bf:e5:f2:10:b2:b4:91:82:e1: ++ d5:9e:91:4b:e4:f2:ce:87:e1:a6:ee:6b:b5:68:39: ++ c0:c0:9a:e7:5d + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: +@@ -25,62 +25,66 @@ Certificate: + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: +- B2:76:F0:C0:C9:97:89:B7:00:A2:9C:AA:5B:49:D8:62:C7:2B:81:AD ++ AD:35:5C:21:09:CA:97:51:8F:CC:F2:33:B0:86:80:DF:73:4F:FD:73 + X509v3 Authority Key Identifier: +- 00:F2:98:4D:21:2C:00:3C:40:9B:84:F4:DE:2A:F0:26:EE:32:0E:9F ++ AC:87:DC:8B:3D:8A:DB:89:48:7E:6F:3B:1C:C6:70:92:09:17:6F:F9 ++ Authority Information Access: ++ OCSP - URI:http://127.0.0.1:8888 + Signature Algorithm: sha256WithRSAEncryption + Signature Value: +- 55:68:78:de:91:87:35:36:63:ca:bf:b1:bb:17:89:ef:01:7d: +- be:40:d6:e3:cb:45:ae:3b:7f:97:0f:5f:f4:7c:eb:2b:5d:0f: +- 2f:23:17:2d:2d:25:76:b1:1b:b8:59:24:4c:8b:0a:c7:cc:07: +- 37:f5:55:b2:20:b4:51:c9:ee:3b:0b:2f:b0:b2:d0:7f:76:30: +- 0e:46:65:63:45:67:cd:bd:42:ba:55:72:16:cd:6d:37:07:9b: +- 9c:9b:ee:cc:63:e1:32:15:c2:3c:76:05:a6:25:51:c2:a7:99: +- dd:03:36:d4:d4:75:47:50:a9:bc:e6:d6:a7:95:49:6a:54:12: +- 26:27:94:a2:67:48:8a:42:38:fd:e4:58:8b:3c:c1:67:a2:dc: +- ba:4e:72:72:f7:60:d8:ce:18:c7:56:2d:61:bf:b5:5b:f7:d8: +- b9:d5:f0:3e:62:20:2f:f1:b2:6d:0e:b0:e2:9a:60:a0:3f:8c: +- 7c:4a:ec:4f:fa:90:29:e5:b5:01:6e:20:f5:e5:d8:cd:ed:29: +- 59:e3:93:29:08:a9:a3:6b:1f:9f:6b:91:48:78:ad:03:1f:64: +- 0d:36:c7:c4:d3:40:a0:fc:cf:72:09:f5:8e:71:7b:cb:6c:fd: +- 1a:d3:02:5e:37:96:d0:41:ef:b2:2b:a9:28:18:fc:de:68:7e: +- 53:80:c4:8c:2b:90:42:de:61:ff:56:88:ea:f7:80:42:3f:cd: +- 13:46:9d:6d:70:b3:83:b9:69:35:94:a0:56:d9:3e:39:fc:b1: +- bb:f5:6d:d4:3c:22:80:86:b3:32:2a:eb:2a:c9:5d:a8:4b:b9: +- 48:70:68:d1:2c:2b:b7:bd:9c:2c:79:70:b4:e7:69:ba:32:bc: +- 97:ff:07:2a:49:78:d7:a1:6f:d0:24:96:73:1e:8c:3f:20:a2: +- 3c:fb:8f:11:e0:88:d3:86:06:6c:02:b8:a5:25:f2:10:1a:b3: +- bf:9a:ec:57:81:d4:d4:91:e1:6d:70:21:0b:6f:c0:a2:5d:49: +- 29:9c:fa:bd:78:7f:a7:3b:45:65:96:79:68:33:71:f8:a9:ea: +- 5c:69:69:b4:bf:8a:45:66:49:0d:e1:13:65:8b:27:72:cc:e6: +- 99:42:80:6c:b8:cc:6b:d8:f0:d2:71:bb:a6:f1:fa:5a:70:8a: +- 7b:df:65:4c:2f:bf:18:2c:b9:de:db:7f:e1:73:96:66:5d:73: +- aa:ef:f3:87:9e:e6:79:14:50:99:ae:bd:38:04:f2:78:54:db: +- ef:03:8a:57:9c:99:a8:57:87:c7:e7:84:19:d1:d9:4d:cf:a2: +- 8d:66:29:1a:78:a8:28:85:3e:0b:44:9e:60:d6:f0:14:92:11: +- 80:48:b5:5c:1b:81:8f:ba ++ 9a:74:81:f5:9d:87:bc:47:ea:33:a9:ef:d4:6b:6a:de:77:4e: ++ fc:41:c4:44:e7:ca:02:63:8b:a6:4f:b3:ed:14:44:8d:3f:29: ++ 3b:ee:2f:30:2b:31:bc:2f:88:64:ce:e7:4e:77:d7:d2:94:c6: ++ 31:98:ea:b8:c3:0c:23:e9:cd:00:99:2f:bd:41:bb:49:5d:ae: ++ f0:65:c7:b8:1b:c3:07:58:ea:ed:0e:ff:22:be:7b:0a:77:15: ++ 62:49:e0:7c:39:6a:9d:02:7d:b3:6d:94:eb:14:e6:75:1f:8a: ++ 40:cd:88:13:bd:42:03:49:18:ae:11:43:d5:f1:98:c5:a0:87: ++ 18:33:ae:84:c6:65:8f:61:89:be:54:d9:52:02:80:59:30:42: ++ df:02:d0:e5:44:9c:f5:36:28:3b:26:5c:a3:28:89:d2:b9:a3: ++ c7:2a:99:d3:86:b5:fd:13:6d:d7:f9:5d:7e:88:35:af:19:9e: ++ 4e:9c:8b:48:92:15:62:47:c7:c4:1e:25:4e:d5:8b:96:1f:7e: ++ cc:55:10:5f:82:1e:3f:b7:c7:ba:00:38:59:55:53:59:3c:f9: ++ 0b:2a:ab:d4:bf:0e:84:2b:71:61:be:14:4d:12:c1:8d:ab:8b: ++ 14:d7:af:b2:b4:6a:7e:b6:23:64:a2:7b:11:6b:ec:6b:1a:64: ++ ea:49:f0:a7:9f:d0:c9:47:71:fa:01:8a:3b:be:b9:6d:4a:d9: ++ 58:25:1a:a8:f6:88:35:e5:d4:98:53:48:62:f9:24:db:87:21: ++ bb:67:5f:b7:d1:60:0f:34:f5:63:f9:52:fd:f1:33:cb:5f:79: ++ 16:c8:e5:e4:c1:79:0c:d9:76:b4:44:56:ff:e7:86:a5:9f:dd: ++ 72:c6:15:4f:ed:6e:2d:1a:54:61:b3:5c:c7:26:10:98:9f:7e: ++ 77:0b:80:4c:a9:d7:bd:28:4c:3c:3b:05:67:e1:af:6d:1b:26: ++ d5:24:96:5f:01:69:84:37:1e:f2:30:3e:9d:e9:8a:fd:0f:28: ++ 56:87:d1:28:8f:32:5d:c9:96:f0:ec:14:b1:34:b9:0d:63:16: ++ 1b:26:e5:76:33:12:fe:52:f5:49:17:2d:14:06:fc:3f:4e:81: ++ b2:02:7d:e7:16:ae:62:d2:0f:1c:df:a1:50:a3:ff:3e:69:d5: ++ 7f:e7:c4:e7:6a:b8:93:b2:d8:99:9b:72:30:e9:e0:62:d6:c3: ++ c1:39:a0:1b:30:37:63:6e:b0:0f:47:20:71:8a:20:56:2f:2d: ++ a6:54:35:ee:72:38:fc:e2:bb:67:60:4f:f7:b8:79:cb:57:6e: ++ f3:7c:b3:55:03:01:41:29:48:ca:f6:a9:3a:4a:6a:26:cb:f7: ++ 21:54:fa:70:31:3d:95:d0 + -----BEGIN CERTIFICATE----- +-MIIESTCCAjGgAwIBAgICEBEwDQYJKoZIhvcNAQELBQAwgZMxCzAJBgNVBAYTAlVT +-MQswCQYDVQQIEwJNQTESMBAGA1UEBxMJV2FrZWZpZWxkMScwJQYDVQQKEx5UaGUg +-QXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xGjAYBgNVBAsTEUFwYWNoZSBUb21j +-YXQgUE1DMR4wHAYDVQQDExVBcGFjaGUgVG9tY2F0IFRlc3QgQ0EwHhcNMjUwMjE2 +-MTc1OTMzWhcNMjcwMjE2MTc1OTMzWjCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgM +-AkRFMRMwEQYDVQQHDApXaWxtaW5ndG9uMScwJQYDVQQKDB5UaGUgQXBhY2hlIFNv +-ZnR3YXJlIEZvdW5kYXRpb24xGjAYBgNVBAsMEUFwYWNoZSBUb21jYXQgUE1DMRIw +-EAYDVQQDDAlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQKCsk2 +-5Bgn8O+s84qLqJwV9XGVlBC8dErFHufF/bCzNcBe/hErrrqnpCcJvTfF3rznbffe +-3avtnc8CWR66smqwo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVu +-U1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUsnbwwMmXibcAopyq +-W0nYYscrga0wHwYDVR0jBBgwFoAUAPKYTSEsADxAm4T03irwJu4yDp8wDQYJKoZI +-hvcNAQELBQADggIBAFVoeN6RhzU2Y8q/sbsXie8Bfb5A1uPLRa47f5cPX/R86ytd +-Dy8jFy0tJXaxG7hZJEyLCsfMBzf1VbIgtFHJ7jsLL7Cy0H92MA5GZWNFZ829QrpV +-chbNbTcHm5yb7sxj4TIVwjx2BaYlUcKnmd0DNtTUdUdQqbzm1qeVSWpUEiYnlKJn +-SIpCOP3kWIs8wWei3LpOcnL3YNjOGMdWLWG/tVv32LnV8D5iIC/xsm0OsOKaYKA/ +-jHxK7E/6kCnltQFuIPXl2M3tKVnjkykIqaNrH59rkUh4rQMfZA02x8TTQKD8z3IJ +-9Y5xe8ts/RrTAl43ltBB77IrqSgY/N5oflOAxIwrkELeYf9WiOr3gEI/zRNGnW1w +-s4O5aTWUoFbZPjn8sbv1bdQ8IoCGszIq6yrJXahLuUhwaNEsK7e9nCx5cLTnaboy +-vJf/BypJeNehb9AklnMejD8gojz7jxHgiNOGBmwCuKUl8hAas7+a7FeB1NSR4W1w +-IQtvwKJdSSmc+r14f6c7RWWWeWgzcfip6lxpabS/ikVmSQ3hE2WLJ3LM5plCgGy4 +-zGvY8NJxu6bx+lpwinvfZUwvvxgsud7bf+FzlmZdc6rv84ee5nkUUJmuvTgE8nhU +-2+8DilecmahXh8fnhBnR2U3Poo1mKRp4qCiFPgtEnmDW8BSSEYBItVwbgY+6 ++MIIEfzCCAmegAwIBAgICEAQwDQYJKoZIhvcNAQELBQAwgZQxCzAJBgNVBAYTAlVT ++MQswCQYDVQQIEwJERTETMBEGA1UEBxMKV2lsbWluZ3RvbjEnMCUGA1UEChMeVGhl ++IEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFBcGFjaGUgVG9t ++Y2F0IFBNQzEeMBwGA1UEAxMVQXBhY2hlIFRvbWNhdCBUZXN0IENBMB4XDTI1MTIw ++NTE5MTU0MFoXDTI3MTIwNTE5MTU0MFowgYgxCzAJBgNVBAYTAlVTMQswCQYDVQQI ++DAJERTETMBEGA1UEBwwKV2lsbWluZ3RvbjEnMCUGA1UECgweVGhlIEFwYWNoZSBT ++b2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLDBFBcGFjaGUgVG9tY2F0IFBNQzES ++MBAGA1UEAwwJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESmeo ++UpqkqFTgCiYBe8gg/JmZCbnOpBG9mQ+dc7OC75+pczG/5fIQsrSRguHVnpFL5PLO ++h+Gm7mu1aDnAwJrnXaOBrzCBrDAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1P ++cGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUrTVcIQnKl1GP ++zPIzsIaA33NP/XMwHwYDVR0jBBgwFoAUrIfciz2K24lIfm87HMZwkgkXb/kwMQYI ++KwYBBQUHAQEEJTAjMCEGCCsGAQUFBzABhhVodHRwOi8vMTI3LjAuMC4xOjg4ODgw ++DQYJKoZIhvcNAQELBQADggIBAJp0gfWdh7xH6jOp79Rrat53TvxBxETnygJji6ZP ++s+0URI0/KTvuLzArMbwviGTO505319KUxjGY6rjDDCPpzQCZL71Bu0ldrvBlx7gb ++wwdY6u0O/yK+ewp3FWJJ4Hw5ap0CfbNtlOsU5nUfikDNiBO9QgNJGK4RQ9XxmMWg ++hxgzroTGZY9hib5U2VICgFkwQt8C0OVEnPU2KDsmXKMoidK5o8cqmdOGtf0Tbdf5 ++XX6INa8Znk6ci0iSFWJHx8QeJU7Vi5YffsxVEF+CHj+3x7oAOFlVU1k8+Qsqq9S/ ++DoQrcWG+FE0SwY2rixTXr7K0an62I2SiexFr7GsaZOpJ8Kef0MlHcfoBiju+uW1K ++2VglGqj2iDXl1JhTSGL5JNuHIbtnX7fRYA809WP5Uv3xM8tfeRbI5eTBeQzZdrRE ++Vv/nhqWf3XLGFU/tbi0aVGGzXMcmEJiffncLgEyp170oTDw7BWfhr20bJtUkll8B ++aYQ3HvIwPp3piv0PKFaH0SiPMl3JlvDsFLE0uQ1jFhsm5XYzEv5S9UkXLRQG/D9O ++gbICfecWrmLSDxzfoVCj/z5p1X/nxOdquJOy2JmbcjDp4GLWw8E5oBswN2NusA9H ++IHGKIFYvLaZUNe5yOPziu2dgT/e4ectXbvN8s1UDAUEpSMr2qTpKaibL9yFU+nAx ++PZXQ + -----END CERTIFICATE----- +diff --git a/test/org/apache/tomcat/util/net/localhost-ec-key.pem b/test/org/apache/tomcat/util/net/localhost-ec-key.pem +index b652c24a6e..e6621274ca 100644 +--- a/test/org/apache/tomcat/util/net/localhost-ec-key.pem ++++ b/test/org/apache/tomcat/util/net/localhost-ec-key.pem +@@ -1,5 +1,8 @@ +------BEGIN PRIVATE KEY----- +-MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgIGiUWTenuT85M7WD +-zpFQvWMq5xsQgZoNqmgg1BfGuyahRANCAAQKCsk25Bgn8O+s84qLqJwV9XGVlBC8 +-dErFHufF/bCzNcBe/hErrrqnpCcJvTfF3rznbffe3avtnc8CWR66smqw +------END PRIVATE KEY----- ++-----BEGIN EC PARAMETERS----- ++BggqhkjOPQMBBw== ++-----END EC PARAMETERS----- ++-----BEGIN EC PRIVATE KEY----- ++MHcCAQEEIF9PqIbcIMlMOKkaAID2L1ULbJApzzOj3WlBYuyZQqaaoAoGCCqGSM49 ++AwEHoUQDQgAESmeoUpqkqFTgCiYBe8gg/JmZCbnOpBG9mQ+dc7OC75+pczG/5fIQ ++srSRguHVnpFL5PLOh+Gm7mu1aDnAwJrnXQ== ++-----END EC PRIVATE KEY----- +diff --git a/test/org/apache/tomcat/util/net/localhost-ec.jks b/test/org/apache/tomcat/util/net/localhost-ec.jks +index 0fc20baca4..e510be8894 100644 +Binary files a/test/org/apache/tomcat/util/net/localhost-ec.jks and b/test/org/apache/tomcat/util/net/localhost-ec.jks differ +diff --git a/test/org/apache/tomcat/util/net/localhost-rsa-cert.pem b/test/org/apache/tomcat/util/net/localhost-rsa-cert.pem +index 488f1fb3c6..9d016d3b17 100644 +--- a/test/org/apache/tomcat/util/net/localhost-rsa-cert.pem ++++ b/test/org/apache/tomcat/util/net/localhost-rsa-cert.pem +@@ -1,35 +1,35 @@ + Certificate: + Data: + Version: 3 (0x2) +- Serial Number: 4112 (0x1010) ++ Serial Number: 4096 (0x1000) + Signature Algorithm: sha256WithRSAEncryption +- Issuer: C=US, ST=MA, L=Wakefield, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=Apache Tomcat Test CA ++ Issuer: C=US, ST=DE, L=Wilmington, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=Apache Tomcat Test CA + Validity +- Not Before: Feb 16 17:53:36 2025 GMT +- Not After : Feb 16 17:53:36 2027 GMT ++ Not Before: Dec 5 19:13:23 2025 GMT ++ Not After : Dec 5 19:13:23 2027 GMT + Subject: C=US, ST=DE, L=Wilmington, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=localhost + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: +- 00:c0:40:4c:7d:58:4b:93:4a:0c:4d:ba:59:cc:50: +- a1:99:c1:ce:e3:3e:c8:a2:41:b0:e8:24:92:92:d4: +- f6:c1:31:d9:d6:41:c0:fa:de:d0:2e:59:09:75:a9: +- c3:3b:d4:56:d3:4f:79:a3:30:df:71:15:cd:d8:a7: +- 03:d6:be:fb:49:b0:cc:69:75:c4:b9:fb:a2:de:2e: +- da:30:db:72:27:1c:f9:96:e1:9f:bd:13:39:8f:da: +- a3:12:95:c7:a1:67:3e:4c:14:8c:98:03:6a:86:18: +- 8b:3a:d5:4e:09:77:7d:c1:be:6a:53:75:cc:c8:0f: +- 35:fc:4d:fd:40:63:a3:e4:10:0b:77:d9:b3:82:1b: +- 30:4c:35:02:9e:41:dc:8f:b5:70:a6:d7:be:75:1e: +- 57:a7:26:14:33:5a:e7:17:68:98:27:10:a6:9c:0c: +- 17:81:6b:d1:15:36:44:01:95:f8:91:38:9c:6d:d0: +- 97:45:0d:02:3c:c3:18:20:8d:a6:2a:e7:e0:df:8d: +- c1:81:81:71:bb:89:b2:b6:02:87:51:2c:7a:ca:9f: +- c9:ec:ab:e0:01:26:0c:dc:38:bd:17:be:2d:fe:48: +- 4c:7f:9c:42:1a:38:a0:39:60:5c:38:65:54:d4:71: +- ab:d3:a6:d0:c9:a2:a3:3c:0f:59:09:8d:8b:57:dc: +- 2b:05 ++ 00:b1:da:2e:50:5c:5b:22:db:84:61:5e:e4:9d:e1: ++ a4:24:6c:92:d3:96:5a:77:52:c8:f7:cf:16:b5:60: ++ 80:a7:15:0e:12:75:1a:bc:3f:0b:54:38:96:f6:2f: ++ 0a:38:6c:21:68:da:13:49:a2:7b:7c:80:c7:aa:69: ++ fe:e3:8c:b1:ce:73:e6:5b:80:e2:36:89:49:5b:03: ++ ce:76:47:bf:35:20:ee:c6:78:df:62:aa:d1:d7:bf: ++ 91:b0:fc:aa:fd:7f:59:3f:8a:51:9f:7d:59:0f:e1: ++ d6:dc:7b:16:80:62:3f:6c:c2:2c:1b:a5:05:1e:40: ++ 90:60:20:e1:c3:6d:3f:ff:1c:73:84:92:86:59:03: ++ e5:4a:f6:d6:dc:e6:6e:68:1f:05:5f:f7:21:a9:fc: ++ 5e:e6:fc:8b:c7:36:34:18:fc:d5:70:21:54:5a:f1: ++ 10:c9:24:c8:08:92:2c:91:13:54:b1:d9:59:70:80: ++ 39:9f:8c:90:ad:c9:d2:b8:bd:a3:28:20:3f:67:35: ++ a2:d9:20:e2:3f:2e:be:3d:88:d2:3b:7d:3b:47:44: ++ 79:53:e2:52:98:72:13:ae:a2:9d:fe:68:bd:36:8c: ++ 2e:43:b1:3b:cd:50:35:3d:b5:f7:2f:43:ea:1e:c9: ++ d1:10:ac:e8:24:c1:8c:ee:e6:d0:c2:06:de:14:2d: ++ 1a:89 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: +@@ -37,69 +37,72 @@ Certificate: + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: +- BE:46:EA:26:EA:2A:17:93:67:DD:17:0B:57:8D:ED:A0:30:14:00:B2 ++ 70:34:99:AD:29:E3:27:9D:BD:97:72:50:20:98:D2:97:B3:6C:AD:A1 + X509v3 Authority Key Identifier: +- 00:F2:98:4D:21:2C:00:3C:40:9B:84:F4:DE:2A:F0:26:EE:32:0E:9F ++ AC:87:DC:8B:3D:8A:DB:89:48:7E:6F:3B:1C:C6:70:92:09:17:6F:F9 ++ Authority Information Access: ++ OCSP - URI:http://127.0.0.1:8888 + X509v3 Subject Alternative Name: + DNS:localhost, IP Address:127.0.0.1 + Signature Algorithm: sha256WithRSAEncryption + Signature Value: +- 90:9b:b3:66:a4:42:07:c2:68:05:aa:f3:a2:c9:66:35:45:42: +- b4:be:40:c7:90:34:71:b4:cf:79:fd:74:f7:4b:46:3f:f3:da: +- 81:3b:55:c3:62:7f:79:12:12:7f:b2:8d:72:55:d8:85:97:cf: +- 54:44:0e:9f:22:02:57:98:e9:02:a8:c7:6d:45:3e:b7:f6:7c: +- 56:58:a1:1b:0b:fa:a7:4c:57:66:92:3a:92:bd:f4:27:c6:5c: +- af:06:3b:d8:be:e9:b7:9a:d3:85:ae:9f:da:5f:59:05:bf:12: +- c9:13:49:75:25:1e:76:2d:c9:b7:c1:55:76:25:30:db:48:f4: +- c9:c4:cb:a4:dc:b6:df:13:1c:c1:44:2b:67:14:15:da:53:56: +- 18:e8:86:5a:75:89:96:a5:88:72:47:36:b5:0b:9c:51:13:50: +- c4:93:1c:a1:2b:d0:d8:0c:c7:f6:c5:30:88:93:6c:02:ff:6e: +- fe:92:ce:60:4b:17:27:e7:67:9a:d0:27:ae:4e:94:86:2a:ad: +- 99:3b:2e:6f:99:a5:d0:a1:a8:e8:3f:d1:c2:b6:1c:1a:f8:dc: +- 96:49:3b:79:be:c0:f8:3b:6a:93:d9:08:1e:3a:a9:36:0b:a1: +- 64:df:eb:fa:14:34:9b:73:25:49:29:25:e0:39:aa:49:0f:2d: +- 4b:ec:40:19:f7:44:00:47:7b:41:72:e8:5f:06:31:31:74:ca: +- 7b:98:e7:63:f8:19:ac:7e:5d:1f:5a:b4:22:d8:73:88:27:70: +- f4:18:5d:10:a9:52:6c:e3:e1:35:b0:4e:e3:91:79:63:49:86: +- 86:19:98:c4:9a:46:a8:bd:dd:5f:6a:8e:14:75:9e:e1:35:6b: +- ad:ab:83:8c:31:b6:cb:c2:cc:a5:86:4f:d9:e7:9a:cb:d6:a6: +- f5:ee:a9:8e:85:a1:85:54:3a:f6:f3:0e:b9:9f:e2:bc:5d:e5: +- eb:42:75:f1:3f:e1:a6:50:09:cc:a6:d3:c5:34:40:55:84:66: +- 61:60:99:f0:ea:f7:56:0b:fe:c7:b2:d2:ed:68:dc:0b:35:2f: +- 79:bf:f6:0a:af:2f:eb:25:8a:f2:9a:c7:72:00:04:ad:99:21: +- 08:14:b6:e4:12:f8:d0:c4:a3:55:c8:c7:ec:4f:e4:fb:4a:3e: +- 14:e4:56:cf:f8:a5:e1:b2:b2:50:8b:83:ce:fc:13:68:45:55: +- f6:1c:bc:e5:fe:f2:f2:51:0d:b7:9e:49:f2:19:3e:10:59:a3: +- dd:e0:72:ab:75:25:a0:0e:3c:b1:b9:17:ad:7e:46:9c:37:f1: +- 2a:19:be:91:54:2d:ab:3e:13:54:4c:aa:d6:05:56:b9:f6:3e: +- 19:61:cb:b3:c8:95:25:ad ++ 77:af:4a:b3:09:d8:9b:43:5a:73:78:10:ba:31:ad:a5:06:50: ++ 49:5d:14:f7:7b:1c:38:f0:3a:9b:67:b3:cb:71:52:7a:df:67: ++ fa:e1:c1:8b:18:dc:4e:bf:21:6d:58:b1:5f:df:2d:f1:ee:62: ++ a1:b6:78:dd:e2:c5:f2:4f:0b:9d:3d:cc:88:c7:26:8d:f0:41: ++ 02:ad:bd:e0:29:21:f9:0c:0a:07:cf:38:a4:f1:db:87:f7:e5: ++ ed:55:59:b5:34:7d:b4:39:70:ca:81:7a:bb:20:fa:2e:85:47: ++ 72:9c:35:f8:f2:e1:57:f9:9e:5c:9c:76:92:50:6a:c9:52:e9: ++ a0:d3:4d:41:03:f7:4f:bb:fc:5c:b8:aa:2c:7d:10:ff:3f:6c: ++ 82:e0:60:b8:d5:58:66:de:e0:db:30:b7:bf:3c:09:b9:3c:5c: ++ 36:cc:c6:d3:aa:58:9f:90:fe:e7:eb:5b:62:e2:66:a5:88:ec: ++ dd:2b:64:d1:25:08:e6:0a:49:91:5f:ac:1f:35:8d:2c:a2:71: ++ de:01:18:b0:0e:13:3d:cd:83:92:67:62:63:8a:63:bb:eb:8d: ++ f9:b7:95:b0:b6:a8:9c:14:8c:0b:00:34:8b:65:af:b7:6c:b9: ++ 1b:94:e2:54:3f:ee:87:0f:81:a9:3f:61:10:5f:ee:4d:42:b5: ++ 67:77:30:2e:04:4f:ad:5d:4e:de:e3:20:b3:8f:ca:46:98:7d: ++ 97:91:1d:4c:04:d9:bb:ae:02:3d:6f:55:dd:39:64:f3:53:86: ++ b6:4a:6a:eb:4a:7a:46:4e:0f:3b:44:8e:c2:46:c6:4b:07:1e: ++ b1:4e:8a:1c:fd:6a:06:55:2e:88:c6:85:8c:9b:c2:ea:90:e1: ++ 0b:d9:b1:af:2d:80:7f:b6:a2:c7:7a:63:7d:0a:cd:60:f6:46: ++ ea:40:3d:7d:4e:89:3e:0b:5d:22:8f:b7:3a:3a:38:18:26:a3: ++ 76:e9:cf:18:bc:c1:3c:8b:06:74:b6:f2:a8:a9:b4:cf:e1:96: ++ 54:bd:87:21:1d:20:b6:25:d1:d9:c6:fa:94:25:c6:6e:2b:1e: ++ a1:51:e3:31:ff:43:77:bd:e0:4c:6a:c2:24:97:43:99:f6:cb: ++ dd:3f:63:31:88:af:07:ef:e0:2b:3b:8e:3b:80:26:27:a6:c3: ++ 58:95:53:fa:39:14:59:42:3d:3a:e5:02:ff:0d:5b:e0:63:ad: ++ 93:72:8e:1d:55:c2:f9:49:7d:5c:cb:35:d2:11:b4:86:2d:26: ++ 61:74:1e:4d:e6:c7:97:e7:99:8b:fc:91:41:c8:9f:b8:ec:3e: ++ a3:f9:18:b5:2a:2e:1d:ee:2d:b6:cd:8f:14:be:b8:1c:6d:fd: ++ c3:59:61:66:87:e8:31:82 + -----BEGIN CERTIFICATE----- +-MIIFMjCCAxqgAwIBAgICEBAwDQYJKoZIhvcNAQELBQAwgZMxCzAJBgNVBAYTAlVT +-MQswCQYDVQQIEwJNQTESMBAGA1UEBxMJV2FrZWZpZWxkMScwJQYDVQQKEx5UaGUg +-QXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xGjAYBgNVBAsTEUFwYWNoZSBUb21j +-YXQgUE1DMR4wHAYDVQQDExVBcGFjaGUgVG9tY2F0IFRlc3QgQ0EwHhcNMjUwMjE2 +-MTc1MzM2WhcNMjcwMjE2MTc1MzM2WjCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgT +-AkRFMRMwEQYDVQQHEwpXaWxtaW5ndG9uMScwJQYDVQQKEx5UaGUgQXBhY2hlIFNv +-ZnR3YXJlIEZvdW5kYXRpb24xGjAYBgNVBAsTEUFwYWNoZSBUb21jYXQgUE1DMRIw +-EAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +-AQDAQEx9WEuTSgxNulnMUKGZwc7jPsiiQbDoJJKS1PbBMdnWQcD63tAuWQl1qcM7 +-1FbTT3mjMN9xFc3YpwPWvvtJsMxpdcS5+6LeLtow23InHPmW4Z+9EzmP2qMSlceh +-Zz5MFIyYA2qGGIs61U4Jd33BvmpTdczIDzX8Tf1AY6PkEAt32bOCGzBMNQKeQdyP +-tXCm1751HlenJhQzWucXaJgnEKacDBeBa9EVNkQBlfiROJxt0JdFDQI8wxggjaYq +-5+DfjcGBgXG7ibK2AodRLHrKn8nsq+ABJgzcOL0Xvi3+SEx/nEIaOKA5YFw4ZVTU +-cavTptDJoqM8D1kJjYtX3CsFAgMBAAGjgZgwgZUwCQYDVR0TBAIwADAsBglghkgB +-hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE +-FL5G6ibqKheTZ90XC1eN7aAwFACyMB8GA1UdIwQYMBaAFADymE0hLAA8QJuE9N4q +-8CbuMg6fMBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsF +-AAOCAgEAkJuzZqRCB8JoBarzoslmNUVCtL5Ax5A0cbTPef1090tGP/PagTtVw2J/ +-eRISf7KNclXYhZfPVEQOnyICV5jpAqjHbUU+t/Z8VlihGwv6p0xXZpI6kr30J8Zc +-rwY72L7pt5rTha6f2l9ZBb8SyRNJdSUedi3Jt8FVdiUw20j0ycTLpNy23xMcwUQr +-ZxQV2lNWGOiGWnWJlqWIckc2tQucURNQxJMcoSvQ2AzH9sUwiJNsAv9u/pLOYEsX +-J+dnmtAnrk6UhiqtmTsub5ml0KGo6D/RwrYcGvjclkk7eb7A+Dtqk9kIHjqpNguh +-ZN/r+hQ0m3MlSSkl4DmqSQ8tS+xAGfdEAEd7QXLoXwYxMXTKe5jnY/gZrH5dH1q0 +-IthziCdw9BhdEKlSbOPhNbBO45F5Y0mGhhmYxJpGqL3dX2qOFHWe4TVrrauDjDG2 +-y8LMpYZP2eeay9am9e6pjoWhhVQ69vMOuZ/ivF3l60J18T/hplAJzKbTxTRAVYRm +-YWCZ8Or3Vgv+x7LS7WjcCzUveb/2Cq8v6yWK8prHcgAErZkhCBS25BL40MSjVcjH +-7E/k+0o+FORWz/il4bKyUIuDzvwTaEVV9hy85f7y8lENt55J8hk+EFmj3eByq3Ul +-oA48sbkXrX5GnDfxKhm+kVQtqz4TVEyq1gVWufY+GWHLs8iVJa0= ++MIIFZjCCA06gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZQxCzAJBgNVBAYTAlVT ++MQswCQYDVQQIEwJERTETMBEGA1UEBxMKV2lsbWluZ3RvbjEnMCUGA1UEChMeVGhl ++IEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFBcGFjaGUgVG9t ++Y2F0IFBNQzEeMBwGA1UEAxMVQXBhY2hlIFRvbWNhdCBUZXN0IENBMB4XDTI1MTIw ++NTE5MTMyM1oXDTI3MTIwNTE5MTMyM1owgYgxCzAJBgNVBAYTAlVTMQswCQYDVQQI ++EwJERTETMBEGA1UEBxMKV2lsbWluZ3RvbjEnMCUGA1UEChMeVGhlIEFwYWNoZSBT ++b2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFBcGFjaGUgVG9tY2F0IFBNQzES ++MBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC ++AQEAsdouUFxbItuEYV7kneGkJGyS05Zad1LI988WtWCApxUOEnUavD8LVDiW9i8K ++OGwhaNoTSaJ7fIDHqmn+44yxznPmW4DiNolJWwPOdke/NSDuxnjfYqrR17+RsPyq ++/X9ZP4pRn31ZD+HW3HsWgGI/bMIsG6UFHkCQYCDhw20//xxzhJKGWQPlSvbW3OZu ++aB8FX/chqfxe5vyLxzY0GPzVcCFUWvEQySTICJIskRNUsdlZcIA5n4yQrcnSuL2j ++KCA/ZzWi2SDiPy6+PYjSO307R0R5U+JSmHITrqKd/mi9NowuQ7E7zVA1PbX3L0Pq ++HsnREKzoJMGM7ubQwgbeFC0aiQIDAQABo4HLMIHIMAkGA1UdEwQCMAAwLAYJYIZI ++AYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQW ++BBRwNJmtKeMnnb2XclAgmNKXs2ytoTAfBgNVHSMEGDAWgBSsh9yLPYrbiUh+bzsc ++xnCSCRdv+TAxBggrBgEFBQcBAQQlMCMwIQYIKwYBBQUHMAGGFWh0dHA6Ly8xMjcu ++MC4wLjE6ODg4ODAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcN ++AQELBQADggIBAHevSrMJ2JtDWnN4ELoxraUGUEldFPd7HDjwOptns8txUnrfZ/rh ++wYsY3E6/IW1YsV/fLfHuYqG2eN3ixfJPC509zIjHJo3wQQKtveApIfkMCgfPOKTx ++24f35e1VWbU0fbQ5cMqBersg+i6FR3KcNfjy4Vf5nlycdpJQaslS6aDTTUED90+7 ++/Fy4qix9EP8/bILgYLjVWGbe4Nswt788Cbk8XDbMxtOqWJ+Q/ufrW2LiZqWI7N0r ++ZNElCOYKSZFfrB81jSyicd4BGLAOEz3Ng5JnYmOKY7vrjfm3lbC2qJwUjAsANItl ++r7dsuRuU4lQ/7ocPgak/YRBf7k1CtWd3MC4ET61dTt7jILOPykaYfZeRHUwE2buu ++Aj1vVd05ZPNThrZKautKekZODztEjsJGxksHHrFOihz9agZVLojGhYybwuqQ4QvZ ++sa8tgH+2osd6Y30KzWD2RupAPX1OiT4LXSKPtzo6OBgmo3bpzxi8wTyLBnS28qip ++tM/hllS9hyEdILYl0dnG+pQlxm4rHqFR4zH/Q3e94ExqwiSXQ5n2y90/YzGIrwfv ++4Cs7jjuAJiemw1iVU/o5FFlCPTrlAv8NW+BjrZNyjh1VwvlJfVzLNdIRtIYtJmF0 ++Hk3mx5fnmYv8kUHIn7jsPqP5GLUqLh3uLbbNjxS+uBxt/cNZYWaH6DGC + -----END CERTIFICATE----- +diff --git a/test/org/apache/tomcat/util/net/localhost-rsa-copy1.jks b/test/org/apache/tomcat/util/net/localhost-rsa-copy1.jks +index e14fa0f161..7422fd6f20 100644 +Binary files a/test/org/apache/tomcat/util/net/localhost-rsa-copy1.jks and b/test/org/apache/tomcat/util/net/localhost-rsa-copy1.jks differ +diff --git a/test/org/apache/tomcat/util/net/localhost-rsa-key.pem b/test/org/apache/tomcat/util/net/localhost-rsa-key.pem +index 495d6ae75c..752f23619e 100644 +--- a/test/org/apache/tomcat/util/net/localhost-rsa-key.pem ++++ b/test/org/apache/tomcat/util/net/localhost-rsa-key.pem +@@ -1,28 +1,28 @@ + -----BEGIN PRIVATE KEY----- +-MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDAQEx9WEuTSgxN +-ulnMUKGZwc7jPsiiQbDoJJKS1PbBMdnWQcD63tAuWQl1qcM71FbTT3mjMN9xFc3Y +-pwPWvvtJsMxpdcS5+6LeLtow23InHPmW4Z+9EzmP2qMSlcehZz5MFIyYA2qGGIs6 +-1U4Jd33BvmpTdczIDzX8Tf1AY6PkEAt32bOCGzBMNQKeQdyPtXCm1751HlenJhQz +-WucXaJgnEKacDBeBa9EVNkQBlfiROJxt0JdFDQI8wxggjaYq5+DfjcGBgXG7ibK2 +-AodRLHrKn8nsq+ABJgzcOL0Xvi3+SEx/nEIaOKA5YFw4ZVTUcavTptDJoqM8D1kJ +-jYtX3CsFAgMBAAECggEAR6xxF9YALwRPO/c6nTp+VOV1bTEt+ZgGDTX9UzKEYBhm +-v0M6YA0Ljgvxw+UrfTW3vQYHMjj5RJ69ZIU3oCsJYjrAqpyWYEQZPz42aDqX/08F +-GiQ5unLdZe08GKSwjVMBXbnXhKDZaO7jkWaOtmbPApkr6LulQfyxwsOMpCHBqzdN +-peFcartqRkqhSYyXsMNekp9kZfZqzOkLkpFH0Qo0Y0BkK/1Bo+96iiD72FT3Cmgm +-X5JQhjap6H6v2iy0A2ERAsYwNItnauMDGQbZoYk8pyKNfQBYpHKziA/AomEDXHLE +-N42hjqTjlWfVvJ70TcQ06OcBPnpxdNrFrhYq2LSbbQKBgQD4YQE9/+hh7JrHElBq +-bmF5sR2PyYp6+G1C74b8fSXS69Aijtbtvm2KNII2lCalP0iK1UPBlBAyhrqZs+Pw +-B0fc/FGgZq1O/aM9JYUxGn+LpRfD1a73UrmKakgQj5Ph+6R2VrWSNlDYe/2hhUAB +-WUnyFrPY56bQuhy+uu1K7amatwKBgQDGJmpLSGJ4BRZS2Nb6EDJJOva+KF49cKTC +-GO8UtXXllOP2Y9e86wQ+f+/ZPC53C36hNItd6n9PG2CIpEBeDYbiv6E6a+tNWe27 +-GqNCR/Rbs5MPiOm3tgbrK+eX0PT/+tKElopozp7ijIjYbjd5YbBuPDC7fr4pgBHc +-x1nSWUAcIwKBgEYsggFWT/16r7AWyImJbhjHpaZ+NpXPom8K5YRoh0s0PLXGzYqW +-BrTJaRCqgtClNIWlA2OpxXnA/u92F6w1ImSZFSbQW565omkgNKVO60I0/qXhtXFC +-+hDVPk8PgaSTSD/29427stO3FE2SLCc1ZuLy7xUVPdgy/PQWM/y7cPO5AoGBALR6 +-UN5485c1qvuSnVvuXJ7uocbZpdx4ONjBNSG7lcKiNnA4yEs+FAOJK8DFW9z6D0Bt +-R3MnHwXCfc53LFl+IJluyT8ZBJzTiACfZov8VckgapX/skCt/uc8ehiBLmXo4s34 +-/+AuMfFO52WaHdTk8Xm2QPzFmBbcm2hn+pvoxsfZAoGBAOxLL1iMDwMSAsECzy3s +-B337lOWxb5pE1vIDxuUHScy1GMNpQ9YFyXAwDb0RkVfbQAutu4Uj0Eto58YjtpIP +-y7Qw2vm19vVvyAjtdSTGHOhUB9v77MO8abrVBzlAgOVCGnvk3mUaCwL4uHKDMFLJ +-WePSjVhSZRNhjwootd8hbPtr ++MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCx2i5QXFsi24Rh ++XuSd4aQkbJLTllp3Usj3zxa1YICnFQ4SdRq8PwtUOJb2Lwo4bCFo2hNJont8gMeq ++af7jjLHOc+ZbgOI2iUlbA852R781IO7GeN9iqtHXv5Gw/Kr9f1k/ilGffVkP4dbc ++exaAYj9swiwbpQUeQJBgIOHDbT//HHOEkoZZA+VK9tbc5m5oHwVf9yGp/F7m/IvH ++NjQY/NVwIVRa8RDJJMgIkiyRE1Sx2VlwgDmfjJCtydK4vaMoID9nNaLZIOI/Lr49 ++iNI7fTtHRHlT4lKYchOuop3+aL02jC5DsTvNUDU9tfcvQ+oeydEQrOgkwYzu5tDC ++Bt4ULRqJAgMBAAECggEAAqX0dvsIodFhzimzbSpMuCdZnObktgPd60UpnYpJLz5k ++fTjQc/X3lVt9zffN2KC7yILhOiTTiZ7eSHiT+AUNN/YTdIJL7cuu7Hj+SZ1QYbMr ++lpYlV2PNRH+vAShpjp4KzWsMpyZJehMmEKStHjzGgEZxWIdBqOSFgA3Tgzf5Q/NS ++OiWNgarUkwVGLDRFH99WWbVmAJEnJeB9pxEQllqb/5AkgeiDD3Mx+BwNgO5GRu4u ++z4BwB6InA4e6T+vBhnPra3qxaS3bPfna1P2Gz7jsH8xOHFt51hIVMCzeVj0/wOWL ++Ie7YinLbCAhIa5YD8F1LTvrzCGuJn+a8Rlt6AXPYDQKBgQD1O0plltNO/3y8WN1i ++c3Ox7sQfTsTj5ZFoW4U6kbAIPF5ByKTBAXdWHWxUwBxoZA+4lgYPEvdwXWPG+VXy ++JXAFbR2/JQO/yROKnodB4O73WZitt81PIHdFP7myEeN5QSjnYSvd6lIiaxe178BM ++qyO/85GgcBf/o3MAQib0UCwXHQKBgQC5qXZjspZd8nC33NP6A6HYawjkrZeVYG8S ++qPgT0ctmsb1EpihK+wU1iAJzECZuTSgoZtcRPL/N17/xgSuwk6nTUJyOdQwnmVP+ +++Cfz7lcvx8Dg9FdrmjiEjFHnAYzaZ4bJP5Tub0tsUJRE/7YbhPlvlzaD95USLMKB ++IHzAQWx5XQKBgHgsmgS2qM6pvQK/uZ2pXiTwEQQWob3cnik50EwnYNBoZPhvzu0W ++Ptjgilnt2v39KwcV3do9PSy/V0oGneuQFRlTo6QsC25Mp1ri3P2XsQNd0Mgwrlf8 ++XPZ+iA2PXp3pJJZetBSH48AiIvhxiRcJNve18MNiqyAHhS+3O0e2kiSxAoGAL3uY ++rKzK6iIME+nlSMbPCKNvNdTztJ9iKNqP/7mjFJOWfU0ldu+2CFfNkJHr0j/nalXK ++4TyxLTrleyV3AATz5Phz4bcrsaD0K3xZ83fcUnr66E11Yi6iD7w3YiYyWNUrUqLx ++Ov25w2zkTrU7ZNRgWtrIdX3HYUuTPyUI4r6YuH0CgYA+Qs6ZexX1Xys6qBbO2OiR ++6+eRItL12doGDDCO7tqtscf8S3VGSLk29lfwipyz6YhO46yRP2FypqVHYYhwvYuW ++ubXGUUZhzI3JBvqI0Lrtwt0KJ2QUXzfqZqO1cvKe3+gooywmemvzg5s1jZ+/Ys3b ++rQR0KWEn04xhcDMyTr/A9Q== + -----END PRIVATE KEY----- +diff --git a/test/org/apache/tomcat/util/net/localhost-rsa.jks b/test/org/apache/tomcat/util/net/localhost-rsa.jks +index 5a18bd30ad..fcb9b0c066 100644 +Binary files a/test/org/apache/tomcat/util/net/localhost-rsa.jks and b/test/org/apache/tomcat/util/net/localhost-rsa.jks differ +diff --git a/test/org/apache/tomcat/util/net/ocsp-responder-rsa-cert.pem b/test/org/apache/tomcat/util/net/ocsp-responder-rsa-cert.pem +new file mode 100644 +index 0000000000..02d8d5656b +--- /dev/null ++++ b/test/org/apache/tomcat/util/net/ocsp-responder-rsa-cert.pem +@@ -0,0 +1,105 @@ ++Certificate: ++ Data: ++ Version: 3 (0x2) ++ Serial Number: 4101 (0x1005) ++ Signature Algorithm: sha256WithRSAEncryption ++ Issuer: C=US, ST=DE, L=Wilmington, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=Apache Tomcat Test CA ++ Validity ++ Not Before: Dec 5 19:33:55 2025 GMT ++ Not After : Dec 5 19:33:55 2027 GMT ++ Subject: C=US, ST=DE, L=Wilmington, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=OCSP Responder ++ Subject Public Key Info: ++ Public Key Algorithm: rsaEncryption ++ Public-Key: (2048 bit) ++ Modulus: ++ 00:9b:ae:b1:40:e5:e4:84:b6:c3:ec:93:cf:23:63: ++ 51:51:7f:bd:d3:64:1d:e0:23:bb:33:44:f1:47:c6: ++ f5:d2:4d:2a:13:2a:f4:4c:d2:44:13:56:03:73:16: ++ 30:98:be:66:b1:fb:3c:e9:9c:5a:61:25:b8:47:ef: ++ 72:04:ad:ab:6a:f8:83:fe:00:a6:c0:8b:a3:ef:34: ++ 09:4f:13:e6:fa:96:d3:2b:24:db:e3:d1:4a:40:50: ++ 5f:9f:8f:31:fa:de:42:0d:e6:a2:4a:7a:e6:a6:48: ++ 5a:97:04:b3:b9:bd:52:2e:2f:e9:02:27:1b:8f:bf: ++ 2b:19:28:e8:96:eb:29:d0:6e:39:0b:d9:bf:2a:d2: ++ 3e:65:a4:e0:87:30:ab:26:09:01:ea:4a:a7:a2:38: ++ 09:0e:f5:d3:b8:66:0a:cc:17:61:72:fb:a9:5e:e4: ++ 98:ff:30:e4:93:11:f7:6e:13:f3:76:f6:f4:44:b7: ++ a8:2a:79:b3:4f:59:d8:67:b7:c0:4c:cd:50:57:d2: ++ 09:65:f5:5e:2d:3b:e7:29:bf:e3:11:ff:37:1d:ad: ++ f2:cf:3b:ff:f7:49:d1:1a:05:1d:2b:0e:59:fe:fe: ++ e6:6a:5d:73:7f:0b:8c:b3:6d:c3:65:2a:93:f8:87: ++ b7:fd:c2:4a:e3:b8:d5:e6:55:8b:b3:e5:0e:4f:f4: ++ fa:a7 ++ Exponent: 65537 (0x10001) ++ X509v3 extensions: ++ X509v3 Basic Constraints: critical ++ CA:FALSE ++ X509v3 Key Usage: critical ++ Digital Signature ++ X509v3 Extended Key Usage: ++ OCSP Signing ++ X509v3 Subject Key Identifier: ++ FA:23:45:E2:94:8E:6A:88:B8:72:B1:53:EA:2A:AE:B9:39:F8:6A:C8 ++ X509v3 Authority Key Identifier: ++ AC:87:DC:8B:3D:8A:DB:89:48:7E:6F:3B:1C:C6:70:92:09:17:6F:F9 ++ Signature Algorithm: sha256WithRSAEncryption ++ Signature Value: ++ 3b:39:47:9e:28:f1:c0:46:eb:a8:de:0e:44:63:4d:3c:f4:59: ++ c1:a4:2a:eb:6a:e5:50:76:ea:76:f7:a9:ca:a2:cb:70:91:c9: ++ d3:c5:fd:d6:c9:86:3d:ad:f2:b8:a7:e4:b1:93:09:44:eb:27: ++ 30:01:85:13:db:0a:62:f0:0f:fa:59:de:af:93:1a:9e:df:07: ++ da:b8:ec:19:c1:c9:56:f9:a6:8e:4e:03:ae:e0:f9:d8:d2:b7: ++ d2:94:4a:70:95:b7:a8:7f:42:b2:1d:43:77:3a:9e:f8:76:93: ++ 4b:3a:48:09:5a:8b:3b:2c:38:97:ed:27:6b:d5:31:0b:f0:3d: ++ a9:dc:d4:0b:96:55:07:41:59:e1:1f:68:70:47:04:6a:11:a4: ++ 81:e7:7d:75:08:15:9f:ca:20:20:69:fe:ef:50:3f:cf:02:0b: ++ 96:4a:9e:e7:ce:08:07:8a:c0:93:a6:aa:a9:91:53:ac:5e:80: ++ 3b:bd:6f:cb:d2:7c:e3:9b:cd:ff:b0:12:ec:9a:71:48:e1:01: ++ 96:21:f4:ee:bb:f8:1d:99:77:00:68:ef:e3:bc:5d:1d:58:bc: ++ 91:88:51:39:93:b1:91:88:5c:d0:9d:0f:87:0b:0d:04:ca:be: ++ 79:05:b9:42:ae:b3:62:df:7a:02:d7:d5:4e:a8:27:8f:0b:b8: ++ 4c:aa:d5:07:a6:e4:65:b0:13:78:cd:3a:3b:10:58:49:13:d7: ++ 74:88:76:8a:77:a4:d4:24:01:61:fe:0b:46:fb:4e:15:3a:fa: ++ 2b:c3:ca:10:9c:5f:2b:f5:33:21:93:16:be:a4:c5:bd:a4:80: ++ 88:74:2a:1e:09:d4:2a:c6:af:ed:be:46:7f:b8:d7:ad:a6:e0: ++ 8f:92:ae:2b:8a:97:ca:9c:fb:21:29:48:f1:38:98:09:f9:2b: ++ 55:53:7c:99:a5:23:21:58:35:1a:36:15:67:79:80:3f:fc:94: ++ 60:94:2b:78:a5:f1:81:b4:51:5c:08:1a:50:24:21:da:0b:cc: ++ db:d7:3c:7d:d8:2c:b1:93:9d:f4:94:bb:fe:37:ad:8d:3f:06: ++ 9d:83:b0:e5:f2:ef:e4:88:75:e9:50:3a:f3:ef:aa:e4:00:54: ++ ad:1d:cd:a7:6b:ec:0e:b7:31:92:82:da:0d:4b:c3:27:ee:7a: ++ a8:f4:e0:a9:0a:f1:40:61:a0:09:a5:fb:24:f9:68:34:28:a9: ++ 1b:b6:5a:bd:aa:3a:c4:b8:89:92:ba:92:9c:81:f8:a8:ff:5a: ++ 0c:ef:af:97:b4:86:09:71:e8:13:28:1c:89:16:6f:43:de:1b: ++ 2f:1d:16:9f:37:7f:9c:5f:4e:4a:a8:22:4b:77:9e:f6:94:f8: ++ d9:05:4b:bc:a6:aa:f7:6e ++-----BEGIN CERTIFICATE----- ++MIIFFDCCAvygAwIBAgICEAUwDQYJKoZIhvcNAQELBQAwgZQxCzAJBgNVBAYTAlVT ++MQswCQYDVQQIEwJERTETMBEGA1UEBxMKV2lsbWluZ3RvbjEnMCUGA1UEChMeVGhl ++IEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFBcGFjaGUgVG9t ++Y2F0IFBNQzEeMBwGA1UEAxMVQXBhY2hlIFRvbWNhdCBUZXN0IENBMB4XDTI1MTIw ++NTE5MzM1NVoXDTI3MTIwNTE5MzM1NVowgY0xCzAJBgNVBAYTAlVTMQswCQYDVQQI ++EwJERTETMBEGA1UEBxMKV2lsbWluZ3RvbjEnMCUGA1UEChMeVGhlIEFwYWNoZSBT ++b2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFBcGFjaGUgVG9tY2F0IFBNQzEX ++MBUGA1UEAxMOT0NTUCBSZXNwb25kZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw ++ggEKAoIBAQCbrrFA5eSEtsPsk88jY1FRf73TZB3gI7szRPFHxvXSTSoTKvRM0kQT ++VgNzFjCYvmax+zzpnFphJbhH73IEratq+IP+AKbAi6PvNAlPE+b6ltMrJNvj0UpA ++UF+fjzH63kIN5qJKeuamSFqXBLO5vVIuL+kCJxuPvysZKOiW6ynQbjkL2b8q0j5l ++pOCHMKsmCQHqSqeiOAkO9dO4ZgrMF2Fy+6le5Jj/MOSTEfduE/N29vREt6gqebNP ++Wdhnt8BMzVBX0gll9V4tO+cpv+MR/zcdrfLPO//3SdEaBR0rDln+/uZqXXN/C4yz ++bcNlKpP4h7f9wkrjuNXmVYuz5Q5P9PqnAgMBAAGjdTBzMAwGA1UdEwEB/wQCMAAw ++DgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMJMB0GA1UdDgQWBBT6 ++I0XilI5qiLhysVPqKq65OfhqyDAfBgNVHSMEGDAWgBSsh9yLPYrbiUh+bzscxnCS ++CRdv+TANBgkqhkiG9w0BAQsFAAOCAgEAOzlHnijxwEbrqN4ORGNNPPRZwaQq62rl ++UHbqdvepyqLLcJHJ08X91smGPa3yuKfksZMJROsnMAGFE9sKYvAP+lner5Mant8H ++2rjsGcHJVvmmjk4DruD52NK30pRKcJW3qH9Csh1Ddzqe+HaTSzpICVqLOyw4l+0n ++a9UxC/A9qdzUC5ZVB0FZ4R9ocEcEahGkged9dQgVn8ogIGn+71A/zwILlkqe584I ++B4rAk6aqqZFTrF6AO71vy9J845vN/7AS7JpxSOEBliH07rv4HZl3AGjv47xdHVi8 ++kYhROZOxkYhc0J0PhwsNBMq+eQW5Qq6zYt96AtfVTqgnjwu4TKrVB6bkZbATeM06 ++OxBYSRPXdIh2inek1CQBYf4LRvtOFTr6K8PKEJxfK/UzIZMWvqTFvaSAiHQqHgnU ++Ksav7b5Gf7jXrabgj5KuK4qXypz7ISlI8TiYCfkrVVN8maUjIVg1GjYVZ3mAP/yU ++YJQreKXxgbRRXAgaUCQh2gvM29c8fdgssZOd9JS7/jetjT8GnYOw5fLv5Ih16VA6 ++8++q5ABUrR3Np2vsDrcxkoLaDUvDJ+56qPTgqQrxQGGgCaX7JPloNCipG7Zavao6 ++xLiJkrqSnIH4qP9aDO+vl7SGCXHoEygciRZvQ94bLx0Wnzd/nF9OSqgiS3ee9pT4 ++2QVLvKaq924= ++-----END CERTIFICATE----- +diff --git a/test/org/apache/tomcat/util/net/ocsp-responder-rsa-key.pem b/test/org/apache/tomcat/util/net/ocsp-responder-rsa-key.pem +new file mode 100644 +index 0000000000..1b5c681c93 +--- /dev/null ++++ b/test/org/apache/tomcat/util/net/ocsp-responder-rsa-key.pem +@@ -0,0 +1,28 @@ ++-----BEGIN PRIVATE KEY----- ++MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCbrrFA5eSEtsPs ++k88jY1FRf73TZB3gI7szRPFHxvXSTSoTKvRM0kQTVgNzFjCYvmax+zzpnFphJbhH ++73IEratq+IP+AKbAi6PvNAlPE+b6ltMrJNvj0UpAUF+fjzH63kIN5qJKeuamSFqX ++BLO5vVIuL+kCJxuPvysZKOiW6ynQbjkL2b8q0j5lpOCHMKsmCQHqSqeiOAkO9dO4 ++ZgrMF2Fy+6le5Jj/MOSTEfduE/N29vREt6gqebNPWdhnt8BMzVBX0gll9V4tO+cp ++v+MR/zcdrfLPO//3SdEaBR0rDln+/uZqXXN/C4yzbcNlKpP4h7f9wkrjuNXmVYuz ++5Q5P9PqnAgMBAAECggEAALCpC3sZZLc0rh9Hm0YM7boNutqmQUCXS1ZiZWmN3GVI ++KvaVR6Pk9lr6v+9YvsyVQvO0pOpzAhw7MWJv1HJ6oIpCd4VEN/VGgEBwTB/v9atm ++ezn6GYvrctaIXfoyBAUpMMuVa5QY4qLOd/3m2AttSlQfCcnnlt8Hj2B9i3G2aTya ++EgwH9GKBoQ0FIMP3Znytx2YEj2dMk2wxApafNhxpZ302JZgvQG163UovQeGfJXvN ++d0xwtA8Xyzuv+/l1Bx8KyD0dYjeA6++khJ/oDNkM1+JKfN6qRzIhcNYWZAx6Au78 ++tJwhBuUVqpbZnPkMXwyFjnAMvzSRcJrGIHdrIyPsWQKBgQDIgbcgiECHldkdrIUF ++3qKluX8g8mLTGts/wXG20iY730KThAnbxJ0ORPD5S3HZDSKZkG31ohoZHH7YT9iC ++0+pnu4bEhqtfPttXIK9q3PQh6i5jhiENc2x0NvJ0nUg8DkVqVmnmC3eIVIHfnnUU ++PDhe0GjHC02TegBFXfOFz3LDSQKBgQDGxRa4tKtB/0BeJCu4RD7pHuikt+NUKS7n ++4F3QBpyIzchjImIlHbutFwIbmUQ6C5TzEcgnTDAlJadBmljZYemaX5RN1UEPulPT ++NvSOdGdxyhJmKpFX+umUbypAcpKC6+EElaOuCUphMDOISIyooKUtJqWUXTVgO60x ++2vh94BHebwKBgGpIL/0DnEkCikLrdtukpsx35kZdlTrXio5iCNfizzd/Ybf2Do+6 ++yZGNw7oxXpiyGLwTzeWdVn4nF6mrsVWv5Rm1UnuL8v0awYOOpM1db98mVg6VQ6Hw ++o/V6Rsy/rlF/MxZp1dqGC4dtXCZfxSnDvhGsIU4Y1LcuvUQHyBUO4INZAoGAPATV ++GI7NS49Uk1iervREfsXrf5lbFlWdKT0RdrgYWiKxCGYgdo6k1d4lr8m21UQcBM1w ++v5T80Kqu91swyusKy6dis6HaFHKxzwxACifR+IUIyzq9SnCkSULL4kv2O3wYJPc+ ++RlXd1LzmQpeNiXmAhsKtqr/+VwGOCYjSEcgv/RcCgYBfUrsTG3dgWUVKviFzS63R ++nb0T9yKZij79HkIM0bROpaU5atTgQa/iwvSktw8mHGuJkjQZ6BqVpKHbDOq/Y/zB ++NihYIlf4WUPlQVr/sBLzfTtaxBKcnuVm1d8igKK3qQ0XuH5jk0gD6tzaU4ySTJ+P ++ldxvbWraHRcPzn08xJ6+UA== ++-----END PRIVATE KEY----- +diff --git a/test/org/apache/tomcat/util/net/ocsp/OcspBaseTest.java b/test/org/apache/tomcat/util/net/ocsp/OcspBaseTest.java +new file mode 100644 +index 0000000000..a4c5617309 +--- /dev/null ++++ b/test/org/apache/tomcat/util/net/ocsp/OcspBaseTest.java +@@ -0,0 +1,166 @@ ++/* ++ * 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.ocsp; ++ ++import java.io.File; ++import java.io.FileOutputStream; ++import java.io.IOException; ++import java.nio.channels.FileLock; ++import java.util.ArrayList; ++import java.util.Collection; ++import java.util.List; ++ ++import javax.servlet.http.HttpServletResponse; ++ ++import org.junit.AfterClass; ++import org.junit.Assert; ++import org.junit.Assume; ++import org.junit.BeforeClass; ++import org.junit.runners.Parameterized.Parameter; ++import org.junit.runners.Parameterized.Parameters; ++ ++import org.apache.catalina.Context; ++import org.apache.catalina.startup.Tomcat; ++import org.apache.catalina.startup.TomcatBaseTest; ++import org.apache.tomcat.util.buf.ByteChunk; ++import org.apache.tomcat.util.net.SSLHostConfig; ++import org.apache.tomcat.util.net.TesterSupport; ++import org.apache.tomcat.util.net.TesterSupport.SimpleServlet; ++import org.apache.tomcat.util.net.openssl.OpenSSLStatus; ++ ++public class OcspBaseTest extends TomcatBaseTest { ++ ++ private static final File lockFile = new File("test/org/apache/tomcat/util/net/ocsp/ocsp-responder.lock"); ++ private static FileLock lock = null; ++ ++ @BeforeClass ++ public static void obtainOcspResponderLock() throws IOException { ++ @SuppressWarnings("resource") ++ FileOutputStream fos = new FileOutputStream(lockFile); ++ lock = fos.getChannel().lock(); ++ } ++ ++ @AfterClass ++ public static void releaseOcspResponderLock() throws IOException { ++ // Should not be null be in case obtaining the lock fails, avoid a second error. ++ if (lock != null) { ++ lock.release(); ++ } ++ } ++ ++ ++ @Parameters(name = "{0} with OpenSSL trust {2}") ++ public static Collection parameters() { ++ List parameterSets = new ArrayList<>(); ++ parameterSets.add(new Object[] { "JSSE", Boolean.FALSE, Boolean.FALSE, ++ "org.apache.tomcat.util.net.jsse.JSSEImplementation"}); ++ parameterSets.add(new Object[] { "OpenSSL", Boolean.TRUE, Boolean.TRUE, ++ "org.apache.tomcat.util.net.openssl.OpenSSLImplementation" }); ++ parameterSets.add(new Object[] { "OpenSSL", Boolean.TRUE, Boolean.FALSE, ++ "org.apache.tomcat.util.net.openssl.OpenSSLImplementation" }); ++ parameterSets.add(new Object[] { "OpenSSL-FFM", Boolean.TRUE, Boolean.TRUE, ++ "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation" }); ++ parameterSets.add(new Object[] { "OpenSSL-FFM", Boolean.TRUE, Boolean.FALSE, ++ "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation" }); ++ ++ return parameterSets; ++ } ++ ++ @Parameter(0) ++ public String connectorName; ++ ++ @Parameter(1) ++ public boolean useOpenSSL; ++ ++ @Parameter(2) ++ public boolean useOpenSSLTrust; ++ ++ @Parameter(3) ++ public String sslImplementationName; ++ ++ @Override ++ public void setUp() throws Exception { ++ super.setUp(); ++ Tomcat tomcat = getTomcatInstance(); ++ TesterSupport.configureSSLImplementation(tomcat, sslImplementationName, useOpenSSL); ++ } ++ ++ ++ protected void doTest(boolean clientCertValid, boolean serverCertValid, ClientCertificateVerification verifyClientCert, ++ boolean verifyServerCert) throws Exception { ++ ++ if ("OpenSSL-FFM".equals(connectorName)) { ++ Assume.assumeFalse(OpenSSLStatus.isBoringSSL() || OpenSSLStatus.isLibreSSLPre35()); ++ } ++ Assume.assumeFalse(!useOpenSSLTrust && verifyClientCert == ClientCertificateVerification.OPTIONAL_NO_CA); ++ ++ Tomcat tomcat = getTomcatInstance(); ++ ++ // No file system docBase required ++ Context ctx = tomcat.addContext("", null); ++ ++ Tomcat.addServlet(ctx, "simple", new SimpleServlet()); ++ ctx.addServletMappingDecoded("/simple", "simple"); ++ ++ if (serverCertValid) { ++ TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_RSA_JKS, useOpenSSLTrust); ++ } else { ++ TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_CRL_RSA_JKS, useOpenSSLTrust); ++ } ++ SSLHostConfig sslHostConfig = tomcat.getConnector().findSslHostConfigs()[0]; ++ switch (verifyClientCert) { ++ case DEFAULT: ++ sslHostConfig.setCertificateVerification("required"); ++ break; ++ case DISABLED: ++ sslHostConfig.setOcspEnabled(false); ++ sslHostConfig.setCertificateVerification("required"); ++ break; ++ case ENABLED: ++ sslHostConfig.setOcspEnabled(true); ++ sslHostConfig.setCertificateVerification("required"); ++ break; ++ case OPTIONAL_NO_CA: ++ sslHostConfig.setOcspEnabled(true); ++ sslHostConfig.setCertificateVerification("optionalNoCA"); ++ break; ++ ++ } ++ ++ if (clientCertValid) { ++ TesterSupport.configureClientSsl(verifyServerCert, TesterSupport.CLIENT_JKS); ++ } else { ++ TesterSupport.configureClientSsl(verifyServerCert, TesterSupport.CLIENT_CRL_JKS); ++ } ++ ++ tomcat.start(); ++ ++ int rc = getUrl("https://localhost:" + getPort() + "/simple", new ByteChunk(), false); ++ ++ // If the TLS handshake fails, the test won't get this far. ++ Assert.assertEquals(HttpServletResponse.SC_OK, rc); ++ } ++ ++ ++ protected enum ClientCertificateVerification { ++ DEFAULT, ++ ENABLED, ++ OPTIONAL_NO_CA, ++ DISABLED ++ } ++ ++} +diff --git a/test/org/apache/tomcat/util/net/ocsp/TestOcspEnabled.java b/test/org/apache/tomcat/util/net/ocsp/TestOcspEnabled.java +new file mode 100644 +index 0000000000..e67d934b2c +--- /dev/null ++++ b/test/org/apache/tomcat/util/net/ocsp/TestOcspEnabled.java +@@ -0,0 +1,186 @@ ++/* ++ * 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.ocsp; ++ ++import java.io.File; ++import java.io.FileOutputStream; ++import java.io.IOException; ++import java.nio.channels.FileLock; ++import java.util.ArrayList; ++import java.util.Collection; ++import java.util.List; ++ ++import javax.net.ssl.SSLHandshakeException; ++import javax.servlet.http.HttpServletResponse; ++ ++import org.junit.AfterClass; ++import org.junit.Assert; ++import org.junit.BeforeClass; ++import org.junit.Test; ++import org.junit.runner.RunWith; ++import org.junit.runners.Parameterized; ++import org.junit.runners.Parameterized.Parameter; ++ ++import org.apache.catalina.Context; ++import org.apache.catalina.startup.Tomcat; ++import org.apache.catalina.startup.TomcatBaseTest; ++import org.apache.tomcat.util.buf.ByteChunk; ++import org.apache.tomcat.util.net.SSLHostConfig; ++import org.apache.tomcat.util.net.TesterSupport; ++import org.apache.tomcat.util.net.TesterSupport.SimpleServlet; ++ ++ ++@RunWith(Parameterized.class) ++public class TestOcspEnabled extends TomcatBaseTest { ++ ++ private static TesterOcspResponder ocspResponder; ++ private static final File lockFile = new File("test/org/apache/tomcat/util/net/ocsp/ocsp-responder.lock"); ++ private static FileLock lock = null; ++ ++ @BeforeClass ++ public static void obtainOcspResponderLock() throws IOException { ++ @SuppressWarnings("resource") ++ FileOutputStream fos = new FileOutputStream(lockFile); ++ lock = fos.getChannel().lock(); ++ } ++ ++ @AfterClass ++ public static void releaseOcspResponderLock() throws IOException { ++ // Should not be null be in case obtaining the lock fails, avoid a second error. ++ if (lock != null) { ++ lock.release(); ++ } ++ } ++ ++ ++ @Parameterized.Parameters(name = "{0} with OpenSSL trust {2}") ++ public static Collection parameters() { ++ List parameterSets = new ArrayList<>(); ++ parameterSets.add(new Object[] { "JSSE", Boolean.FALSE, Boolean.FALSE, ++ "org.apache.tomcat.util.net.jsse.JSSEImplementation"}); ++ parameterSets.add(new Object[] { "OpenSSL", Boolean.TRUE, Boolean.TRUE, ++ "org.apache.tomcat.util.net.openssl.OpenSSLImplementation" }); ++ parameterSets.add(new Object[] { "OpenSSL", Boolean.TRUE, Boolean.FALSE, ++ "org.apache.tomcat.util.net.openssl.OpenSSLImplementation" }); ++ parameterSets.add(new Object[] { "OpenSSL-FFM", Boolean.TRUE, Boolean.TRUE, ++ "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation" }); ++ parameterSets.add(new Object[] { "OpenSSL-FFM", Boolean.TRUE, Boolean.FALSE, ++ "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation" }); ++ ++ return parameterSets; ++ } ++ ++ @Parameter(0) ++ public String connectorName; ++ ++ @Parameter(1) ++ public boolean useOpenSSL; ++ ++ @Parameter(2) ++ public boolean useOpenSSLTrust; ++ ++ @Parameter(3) ++ public String sslImplementationName; ++ ++ ++ @BeforeClass ++ public static void startOcspResponder() throws IOException { ++ ocspResponder = new TesterOcspResponder(); ++ ocspResponder.start(); ++ } ++ ++ ++ @Override ++ public void setUp() throws Exception { ++ super.setUp(); ++ Tomcat tomcat = getTomcatInstance(); ++ TesterSupport.configureSSLImplementation(tomcat, sslImplementationName, useOpenSSL); ++ } ++ ++ ++ @AfterClass ++ public static void stopOcspResponder() { ++ ocspResponder.stop(); ++ ocspResponder = null; ++ } ++ ++ ++ @Test ++ public void testValidClientValidServerVerifyNone() throws Exception { ++ doTest(true, true, false, false, HttpServletResponse.SC_OK); ++ } ++ ++ @Test ++ public void testValidClientRevokedServerVerifyNone() throws Exception { ++ doTest(true, false, false, false, HttpServletResponse.SC_OK); ++ } ++ ++ @Test ++ public void testRevokedClientValidServerVerifyNone() throws Exception { ++ doTest(false, true, false, false, HttpServletResponse.SC_OK); ++ } ++ ++ @Test ++ public void testRevokedClientRevokedServerVerifyNone() throws Exception { ++ doTest(false, false, false, false, HttpServletResponse.SC_OK); ++ } ++ ++ ++ @Test ++ public void testValidClientValidServerVerifyClient() throws Exception { ++ doTest(true, true, true, false, HttpServletResponse.SC_OK); ++ } ++ ++ @Test(expected = SSLHandshakeException.class) ++ public void testRevokedClientValidServerVerifyClient() throws Exception { ++ doTest(false, true, true, false, HttpServletResponse.SC_OK); ++ } ++ ++ ++ private void doTest(boolean clientCertValid, boolean serverCertValid, boolean verifyClientCert, ++ boolean verifyServerCert, int expectedStatusCode) throws Exception { ++ Tomcat tomcat = getTomcatInstance(); ++ ++ // No file system docBase required ++ Context ctx = tomcat.addContext("", null); ++ ++ Tomcat.addServlet(ctx, "simple", new SimpleServlet()); ++ ctx.addServletMappingDecoded("/simple", "simple"); ++ ++ if (serverCertValid) { ++ TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_RSA_JKS, useOpenSSLTrust); ++ } else { ++ TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_CRL_RSA_JKS, useOpenSSLTrust); ++ } ++ SSLHostConfig sslHostConfig = tomcat.getConnector().findSslHostConfigs()[0]; ++ sslHostConfig.setCertificateVerification("required"); ++ sslHostConfig.setOcspEnabled(verifyClientCert); ++ ++ if (clientCertValid) { ++ TesterSupport.configureClientSsl(TesterSupport.CLIENT_JKS); ++ } else { ++ TesterSupport.configureClientSsl(TesterSupport.CLIENT_CRL_JKS); ++ } ++ // TODO enable client-side OCSP checks ++ ++ tomcat.start(); ++ ++ int rc = getUrl("https://localhost:" + getPort() + "/simple", new ByteChunk(), false); ++ ++ Assert.assertEquals(expectedStatusCode, rc); ++ } ++} +diff --git a/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java b/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java +new file mode 100644 +index 0000000000..9012d7196a +--- /dev/null ++++ b/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java +@@ -0,0 +1,432 @@ ++/* ++ * 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.ocsp; ++ ++import java.io.Closeable; ++import java.io.File; ++import java.io.FileInputStream; ++import java.io.IOException; ++import java.io.OutputStream; ++import java.net.InetSocketAddress; ++import java.net.ServerSocket; ++import java.net.URI; ++import java.net.URL; ++import java.nio.file.Files; ++import java.security.KeyStore; ++import java.security.KeyStoreException; ++import java.security.cert.CRLReason; ++import java.security.cert.CertPathValidator; ++import java.security.cert.CertPathValidatorException; ++import java.security.cert.Certificate; ++import java.security.cert.CertificateRevokedException; ++import java.security.cert.PKIXBuilderParameters; ++import java.security.cert.PKIXRevocationChecker; ++import java.security.cert.TrustAnchor; ++import java.security.cert.X509CertSelector; ++import java.security.cert.X509Certificate; ++import java.util.ArrayList; ++import java.util.Collection; ++import java.util.Date; ++import java.util.EnumSet; ++import java.util.Enumeration; ++import java.util.HashMap; ++import java.util.HashSet; ++import java.util.List; ++import java.util.Set; ++ ++import javax.net.ssl.CertPathTrustManagerParameters; ++import javax.net.ssl.HttpsURLConnection; ++import javax.net.ssl.KeyManagerFactory; ++import javax.net.ssl.SSLContext; ++import javax.net.ssl.SSLHandshakeException; ++import javax.net.ssl.SSLSocketFactory; ++import javax.net.ssl.TrustManagerFactory; ++import javax.security.auth.x500.X500Principal; ++ ++import jakarta.servlet.http.HttpServletResponse; ++ ++import org.junit.Assert; ++import org.junit.Assume; ++import org.junit.Test; ++import org.junit.runner.RunWith; ++import org.junit.runners.Parameterized; ++import org.junit.runners.Parameterized.Parameter; ++ ++import org.apache.catalina.Context; ++import org.apache.catalina.connector.Connector; ++import org.apache.catalina.startup.Tomcat; ++import org.apache.catalina.startup.TomcatBaseTest; ++import org.apache.tomcat.util.net.Constants; ++import org.apache.tomcat.util.net.SSLHostConfig; ++import org.apache.tomcat.util.net.SSLHostConfigCertificate; ++import org.apache.tomcat.util.net.TesterSupport; ++import org.apache.tomcat.util.net.openssl.OpenSSLStatus; ++ ++import com.sun.net.httpserver.Headers; ++import com.sun.net.httpserver.HttpServer; ++ ++ ++@RunWith(Parameterized.class) ++public class TestOcspIntegration extends TomcatBaseTest { ++ ++ private static final String CA_CERTIFICATE_PATH = "ca-cert.pem"; ++ private static final String SERVER_CERTIFICATE_PATH = "server-cert.pem"; ++ private static final String SERVER_CERTIFICATE_KEY_PATH = "server-key.pem"; ++ private static final String TRUSTSTORE_PATH = "trustStore.p12"; ++ private static final String TRUSTSTORE_PASS = "trust-password"; ++ private static final String KEYSTORE_TYPE = "PKCS12"; ++ private static final String OCSP_SERVER_CERT_GOOD_RESPONSE = "ocsp-good.der"; ++ private static final String OCSP_SERVER_CERT_REVOKED_RESPONSE = "ocsp-revoked.der"; ++ private static final String CLIENT_KEYSTORE_PATH = "client-keystore.p12"; ++ private static final String CLIENT_KEYSTORE_PASS = "client-password"; ++ private static final String OCSP_CLIENT_CERT_GOOD_RESPONSE = "ocsp-client-good.der"; ++ private static final String OCSP_CLIENT_CERT_REVOKED_RESPONSE = "ocsp-client-revoked.der"; ++ ++ @Parameterized.Parameters(name = "{0}") ++ public static Collection parameters() { ++ List parameterSets = new ArrayList<>(); ++ /* ++ * Future JSSE testing ++ parameterSets.add(new Object[] { ++ "JSSE", Boolean.FALSE, "org.apache.tomcat.util.net.jsse.JSSEImplementation"}); ++ */ ++ /* ++ * Disabled until a Tomcat Native release (2.0.10 onwards) is available with the OCSP fix ++ parameterSets.add(new Object[] { ++ "OpenSSL", Boolean.TRUE, "org.apache.tomcat.util.net.openssl.OpenSSLImplementation"}); ++ */ ++ parameterSets.add(new Object[] { ++ "OpenSSL-FFM", Boolean.TRUE, "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation"}); ++ ++ return parameterSets; ++ } ++ ++ @Parameter(0) ++ public String connectorName; ++ ++ @Parameter(1) ++ public boolean useOpenSSL; ++ ++ @Parameter(2) ++ public String sslImplementationName; ++ ++ ++ @Test ++ public void testOcspGood_ClientVerifiesServerCertificateOnly() throws Exception { ++ Assert.assertEquals(HttpServletResponse.SC_OK, testOCSP(OCSP_SERVER_CERT_GOOD_RESPONSE, false, true)); ++ } ++ ++ @Test ++ public void testOcspGood_Mutual() throws Exception { ++ testOCSPWithClientResponder(OCSP_CLIENT_CERT_GOOD_RESPONSE, () -> Assert.assertEquals(HttpServletResponse.SC_OK, ++ testOCSP(OCSP_SERVER_CERT_GOOD_RESPONSE, true, true))); ++ } ++ ++ @Test ++ public void testOcspGood_ServerVerifiesClientCertificateOnly() throws Exception { ++ testOCSPWithClientResponder(OCSP_CLIENT_CERT_GOOD_RESPONSE, () -> Assert.assertEquals(HttpServletResponse.SC_OK, ++ testOCSP(OCSP_SERVER_CERT_REVOKED_RESPONSE, true, false))); ++ } ++ ++ @Test(expected = CertificateRevokedException.class) ++ public void testOcspRevoked_ClientVerifiesServerCertificateOnly() throws Exception { ++ try { ++ testOCSP(OCSP_SERVER_CERT_REVOKED_RESPONSE, false, true); ++ } catch (SSLHandshakeException sslHandshakeException) { ++ handleExceptionWhenRevoked(sslHandshakeException); ++ } ++ } ++ ++ @Test(expected = CertificateRevokedException.class) ++ public void testOcspRevoked_Mutual() throws Exception { ++ try { ++ // The exception is thrown before server side verification, while client does OCSP verification. ++ testOCSP(OCSP_SERVER_CERT_REVOKED_RESPONSE, true, true); ++ } catch (SSLHandshakeException sslHandshakeException) { ++ handleExceptionWhenRevoked(sslHandshakeException); ++ } ++ } ++ ++ @Test(expected = SSLHandshakeException.class) ++ public void testOcspRevoked_ServerVerifiesClientCertificateOnly() throws Exception { ++ Assume.assumeFalse("BoringSSL does not support OCSP in a compatible way", ++ TesterSupport.isOpenSSLVariant(sslImplementationName, OpenSSLStatus.Name.BORINGSSL)); ++ testOCSPWithClientResponder(OCSP_CLIENT_CERT_REVOKED_RESPONSE, ++ () -> testOCSP(OCSP_SERVER_CERT_GOOD_RESPONSE, true, false)); ++ } ++ ++ @Test ++ public void testOcsp_NoVerification() throws Exception { ++ testOCSPWithClientResponder(OCSP_CLIENT_CERT_REVOKED_RESPONSE, () -> Assert ++ .assertEquals(HttpServletResponse.SC_OK, testOCSP(OCSP_SERVER_CERT_REVOKED_RESPONSE, false, false))); ++ } ++ ++ @Test ++ public void testOcspResponderUrlDiscoveryViaCertificateAIA() throws Exception { ++ final int ocspPort = 8888; ++ Assume.assumeTrue("Port " + ocspPort + " is not available.", isPortAvailable(ocspPort)); ++ Assert.assertEquals(HttpServletResponse.SC_OK, ++ testOCSP(OCSP_SERVER_CERT_GOOD_RESPONSE, false, true, true, ocspPort)); ++ } ++ ++ public static void testLongUrlForOcspViaAIAWithTomcatNative(Tomcat tomcat) throws Exception { ++ final int ocspResponderPortForClient = 8889; ++ Assume.assumeTrue("Port " + ocspResponderPortForClient + " is not available.", isPortAvailable(ocspResponderPortForClient)); ++ try (FakeOcspResponder fakeOcspResponder = new FakeOcspResponder(true, "/ocsp/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", ++ Files.readAllBytes(new File(getPath(OCSP_CLIENT_CERT_REVOKED_RESPONSE)).toPath()), ocspResponderPortForClient)) { ++ fakeOcspResponder.start(); ++ testOCSP(tomcat, OCSP_SERVER_CERT_GOOD_RESPONSE, true, false, false, 0, ++ "org.apache.tomcat.util.net.openssl.OpenSSLImplementation", true); ++ } ++ } ++ ++ @FunctionalInterface ++ private interface TestOCSPAction { ++ void execute() throws Exception; ++ } ++ ++ private void testOCSPWithClientResponder(String clientResponsePath, TestOCSPAction testOCSPAction) ++ throws Exception { ++ final int ocspResponderPortForClient = 8889; ++ Assume.assumeTrue("Port " + ocspResponderPortForClient + " is not available.", ++ isPortAvailable(ocspResponderPortForClient)); ++ try (FakeOcspResponder fakeOcspResponder = new FakeOcspResponder( ++ Files.readAllBytes(new File(getPath(clientResponsePath)).toPath()), ocspResponderPortForClient)) { ++ fakeOcspResponder.start(); ++ testOCSPAction.execute(); ++ } ++ } ++ ++ private int testOCSP(String pathToOcspResponse, boolean serverSideVerificationEnabled, ++ boolean clientSideOcspVerificationEnabled) throws Exception { ++ return testOCSP(pathToOcspResponse, serverSideVerificationEnabled, clientSideOcspVerificationEnabled, false, 0); ++ } ++ ++ private int testOCSP(String pathToOcspResponse, boolean serverSideVerificationEnabled, ++ boolean clientSideOcspVerificationEnabled, boolean clientDiscoversResponderFromAIA, int ocspResponderPort) ++ throws Exception { ++ return testOCSP(getTomcatInstance(), pathToOcspResponse, serverSideVerificationEnabled, ++ clientSideOcspVerificationEnabled, clientDiscoversResponderFromAIA, ocspResponderPort, ++ sslImplementationName, useOpenSSL); ++ } ++ ++ private static int testOCSP(Tomcat tomcat, String pathToOcspResponse, boolean serverSideVerificationEnabled, ++ boolean clientSideOcspVerificationEnabled, boolean clientDiscoversResponderFromAIA, int ocspResponderPort, ++ String sslImplementationName, boolean useOpenSSL) ++ throws Exception { ++ ++ File certificateFile = new File(getPath(SERVER_CERTIFICATE_PATH)); ++ File certificateKeyFile = new File(getPath(SERVER_CERTIFICATE_KEY_PATH)); ++ File certificateChainFile = new File(getPath(CA_CERTIFICATE_PATH)); ++ initSsl(tomcat, serverSideVerificationEnabled, certificateFile, certificateKeyFile, certificateChainFile); ++ ++ TesterSupport.configureSSLImplementation(tomcat, sslImplementationName, useOpenSSL); ++ ++ Context context = tomcat.addContext("", null); ++ Tomcat.addServlet(context, "simple", new TesterSupport.SimpleServlet()); ++ context.addServletMappingDecoded("/", "simple"); ++ ++ KeyStore trustStore = KeyStore.getInstance(KEYSTORE_TYPE); ++ String trustStorePass = Files.readString(new File(getPath(TRUSTSTORE_PASS)).toPath()).trim(); ++ trustStore.load(new FileInputStream(new File(getPath(TRUSTSTORE_PATH)).getAbsolutePath()), ++ trustStorePass.toCharArray()); ++ KeyStore clientKeystore = KeyStore.getInstance(KEYSTORE_TYPE); ++ String clientKeystorePass = Files.readString(new File(getPath(CLIENT_KEYSTORE_PASS)).toPath()).trim(); ++ clientKeystore.load(new FileInputStream(new File(getPath(CLIENT_KEYSTORE_PATH)).getAbsolutePath()), ++ clientKeystorePass.toCharArray()); ++ byte[] ocspResponse = Files.readAllBytes(new File(getPath(pathToOcspResponse)).toPath()); ++ try (FakeOcspResponder fakeOcspResponder = new FakeOcspResponder(ocspResponse, ocspResponderPort)) { ++ fakeOcspResponder.start(); ++ tomcat.start(); ++ ++ URL url = new URI("https://127.0.0.1:" + tomcat.getConnector().getLocalPort() + "/").toURL(); ++ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); ++ SSLSocketFactory sslSocketFactory; ++ if (clientSideOcspVerificationEnabled) { ++ sslSocketFactory = buildClientSslSocketFactoryWithOcsp( ++ clientDiscoversResponderFromAIA ? null : fakeOcspResponder.url(), trustStore, clientKeystore, ++ clientKeystorePass); ++ } else { ++ sslSocketFactory = buildClientSslSocketFactoryNoOcsp(trustStore, clientKeystore, clientKeystorePass); ++ } ++ connection.setSSLSocketFactory(sslSocketFactory); ++ connection.connect(); ++ return connection.getResponseCode(); ++ } finally { ++ tomcat.stop(); ++ } ++ } ++ ++ private static void initSsl(Tomcat tomcat, boolean serverSideVerificationEnabled, File certificateFile, ++ File certificateKeyFile, File certificateChainFile) { ++ Connector connector = tomcat.getConnector(); ++ connector.setSecure(true); ++ connector.setProperty("SSLEnabled", "true"); ++ ++ SSLHostConfig sslHostConfig = new SSLHostConfig(); ++ SSLHostConfigCertificate certificate = new SSLHostConfigCertificate(sslHostConfig, SSLHostConfigCertificate.Type.UNDEFINED); ++ sslHostConfig.addCertificate(certificate); ++ certificate.setCertificateFile(certificateFile.getAbsolutePath()); ++ certificate.setCertificateKeyFile(certificateKeyFile.getAbsolutePath()); ++ certificate.setCertificateChainFile(certificateChainFile.getAbsolutePath()); ++ if (serverSideVerificationEnabled) { ++ sslHostConfig.setCertificateVerification("required"); ++ } else { ++ sslHostConfig.setCertificateVerification("optionalNoCA"); ++ } ++ sslHostConfig.setCaCertificateFile(certificateChainFile.getAbsolutePath()); ++ connector.addSslHostConfig(sslHostConfig); ++ } ++ ++ private static SSLSocketFactory buildClientSslSocketFactoryWithOcsp(String ocspUrl, KeyStore trustStore, ++ KeyStore clientKeystore, String clientKeystorePass) throws Exception { ++ KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); ++ kmf.init(clientKeystore, clientKeystorePass.toCharArray()); ++ Set trustAnchors = getTrustAnchorsFromKeystore(trustStore); ++ PKIXRevocationChecker revocationChecker =(PKIXRevocationChecker) CertPathValidator.getInstance("PKIX").getRevocationChecker(); ++ if (ocspUrl != null) { ++ revocationChecker.setOcspResponder(new URI(ocspUrl)); ++ } ++ revocationChecker.setOptions(EnumSet.of(PKIXRevocationChecker.Option.NO_FALLBACK)); ++ ++ PKIXBuilderParameters pkix = new PKIXBuilderParameters(trustAnchors, new X509CertSelector()); ++ pkix.addCertPathChecker(revocationChecker); ++ ++ TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX"); ++ trustManagerFactory.init(new CertPathTrustManagerParameters(pkix)); ++ return initSSLContext(kmf, trustManagerFactory).getSocketFactory(); ++ } ++ ++ private static SSLSocketFactory buildClientSslSocketFactoryNoOcsp(KeyStore trustStore, KeyStore clientKeystore, ++ String clientKeystorePass) throws Exception { ++ KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); ++ kmf.init(clientKeystore, clientKeystorePass.toCharArray()); ++ TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); ++ trustManagerFactory.init(trustStore); ++ return initSSLContext(kmf, trustManagerFactory).getSocketFactory(); ++ } ++ ++ private static SSLContext initSSLContext(KeyManagerFactory keyManagerFactory, ++ TrustManagerFactory trustManagerFactory) throws Exception { ++ SSLContext sslContext; ++ if (TesterSupport.isTlsv13Available()) { ++ sslContext = SSLContext.getInstance(Constants.SSL_PROTO_TLSv1_3); ++ } else { ++ sslContext = SSLContext.getInstance(Constants.SSL_PROTO_TLSv1_2); ++ } ++ sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); ++ return sslContext; ++ } ++ ++ private static Set getTrustAnchorsFromKeystore(KeyStore keyStore) throws KeyStoreException { ++ Set trustAnchors = new HashSet<>(); ++ Enumeration aliases = keyStore.aliases(); ++ while (aliases.hasMoreElements()) { ++ String alias = aliases.nextElement(); ++ Certificate certificate = keyStore.getCertificate(alias); ++ if (certificate instanceof X509Certificate) { ++ trustAnchors.add(new TrustAnchor((X509Certificate)certificate, null)); ++ } ++ } ++ return trustAnchors; ++ } ++ ++ private static void handleExceptionWhenRevoked(Exception exception) throws Exception { ++ if (exception.getCause().getCause() instanceof CertPathValidatorException cpe) { ++ Assert.assertEquals("REVOKED", cpe.getReason().toString()); ++ Assert.assertTrue(cpe.toString().contains("reason: KEY_COMPROMISE")); ++ // Some JDKs only expose CertPathValidatorException ++ if (cpe.getCause() instanceof CertificateRevokedException) { ++ throw (CertificateRevokedException) cpe.getCause(); ++ } else { ++ throw new CertificateRevokedException(new Date(), CRLReason.KEY_COMPROMISE, new X500Principal(""), new HashMap<>()); ++ } ++ } ++ } ++ ++ private static class FakeOcspResponder implements Closeable { ++ private final byte[] ocspResponse; ++ private HttpServer server; ++ private int port; ++ private boolean strictPath = false; ++ private String path = "/ocsp"; ++ ++ FakeOcspResponder(boolean strictPath, String path, byte[] ocspResponse, int port) { ++ this(ocspResponse, port); ++ this.strictPath = strictPath; ++ this.path = path; ++ } ++ ++ FakeOcspResponder(byte[] ocspResponse, int port) { ++ this.ocspResponse = ocspResponse; ++ this.port = port; ++ } ++ ++ void start() throws IOException { ++ server = HttpServer.create(new InetSocketAddress("127.0.0.1", port), 0); ++ server.createContext(this.path, httpExchange -> { ++ if (strictPath) { ++ String path = httpExchange.getRequestURI().getPath(); ++ if (!this.path.equals(path)) { ++ httpExchange.sendResponseHeaders(404, -1); ++ httpExchange.close(); ++ return; ++ } ++ } ++ byte[] body = ocspResponse; ++ Headers headers = httpExchange.getResponseHeaders(); ++ headers.add("Content-Type", "application/ocsp-response"); ++ httpExchange.sendResponseHeaders(HttpServletResponse.SC_OK, body.length); ++ try (OutputStream os = httpExchange.getResponseBody()) { ++ os.write(body); ++ } ++ }); ++ server.start(); ++ port = server.getAddress().getPort(); ++ } ++ ++ String url() { ++ return "http://127.0.0.1:" + port + path; ++ } ++ @Override public void close() { ++ if (server != null) { ++ server.stop(0); ++ } ++ } ++ } ++ ++ private static String getPath(String file) throws IOException { ++ if (file == null) { ++ return null; ++ } ++ String packageName = TestOcspIntegration.class.getPackageName(); ++ String path = packageName.replace(".", File.separator); ++ File f = new File("test" + File.separator + path + File.separator + file); ++ ++ return f.getCanonicalPath(); ++ } ++ ++ @SuppressWarnings("unused") ++ private static boolean isPortAvailable(int port) { ++ try (ServerSocket serverSocket = new ServerSocket(port)) { ++ return true; ++ } catch (IOException e) { ++ return false; ++ } ++ } ++} +diff --git a/test/org/apache/tomcat/util/net/ocsp/TestOcspSoftFail.java b/test/org/apache/tomcat/util/net/ocsp/TestOcspSoftFail.java +new file mode 100644 +index 0000000000..382db3838b +--- /dev/null ++++ b/test/org/apache/tomcat/util/net/ocsp/TestOcspSoftFail.java +@@ -0,0 +1,59 @@ ++/* ++ * 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.ocsp; ++ ++import java.net.SocketException; ++ ++import javax.net.ssl.SSLHandshakeException; ++ ++import org.junit.Test; ++import org.junit.runner.RunWith; ++import org.junit.runners.Parameterized; ++ ++/* ++ * Only looking to test the Tomcat (server) side configuration of soft fail. ++ */ ++@RunWith(Parameterized.class) ++public class TestOcspSoftFail extends OcspBaseTest { ++ ++ @Test ++ public void testNoResponderDefaultSoftFail() throws Exception { ++ // Default behaviour should be the same for all configurations and equivalent to enabled. ++ doTest(false, false, ClientCertificateVerification.ENABLED, false, null); ++ } ++ ++ ++ @Test ++ public void testNoResponderWithSoftFail() throws Exception { ++ doTest(false, false, ClientCertificateVerification.ENABLED, false, Boolean.TRUE); ++ } ++ ++ ++ @Test(expected = SSLHandshakeException.class) ++ public void testNoResponderWithoutSoftFail() throws Exception { ++ try { ++ doTest(false, false, ClientCertificateVerification.ENABLED, false, Boolean.FALSE); ++ } catch (SocketException se) { ++ // NIO2 may throw a SocketException rather than a SSLHandshakeException ++ if (getTomcatInstance().getConnector().getProtocolHandlerClassName().contains("Nio2")) { ++ throw new SSLHandshakeException(se.getMessage()); ++ } else { ++ throw se; ++ } ++ } ++ } ++} +diff --git a/test/org/apache/tomcat/util/net/ocsp/TestOcspTimeout.java b/test/org/apache/tomcat/util/net/ocsp/TestOcspTimeout.java +new file mode 100644 +index 0000000000..290b08822a +--- /dev/null ++++ b/test/org/apache/tomcat/util/net/ocsp/TestOcspTimeout.java +@@ -0,0 +1,73 @@ ++/* ++ * 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.ocsp; ++ ++import java.net.SocketException; ++import java.net.SocketTimeoutException; ++ ++import javax.net.ssl.SSLHandshakeException; ++ ++import org.junit.AfterClass; ++import org.junit.BeforeClass; ++import org.junit.Test; ++import org.junit.runner.RunWith; ++import org.junit.runners.Parameterized; ++ ++/* ++ * The timeout for reading an OCSP response is 15s by default for both JSSE and OpenSSL. ++ */ ++@RunWith(Parameterized.class) ++public class TestOcspTimeout extends OcspBaseTest { ++ ++ private static TesterOcspResponderNoResponse ocspResponder; ++ ++ @BeforeClass ++ public static void startOcspResponder() { ++ /* ++ * Use shorter timeout to speed up test. ++ * ++ * Note: OpenSSL timeout set later as it requires access to SSLHostConfig. ++ */ ++ System.setProperty("com.sun.security.ocsp.readtimeout", "1000ms"); ++ ocspResponder = new TesterOcspResponderNoResponse(); ++ ocspResponder.start(); ++ } ++ ++ ++ @AfterClass ++ public static void stopOcspResponder() { ++ ocspResponder.stop(); ++ ocspResponder = null; ++ } ++ ++ ++ @Test ++ public void testTimeoutWithSoftFail() throws Exception { ++ doTest(false, false, ClientCertificateVerification.ENABLED, false, Boolean.TRUE); ++ } ++ ++ ++ @Test(expected = SSLHandshakeException.class) ++ public void testTimeoutWithoutSoftFail() throws Exception { ++ try { ++ doTest(false, false, ClientCertificateVerification.ENABLED, false, Boolean.FALSE); ++ } catch (SocketTimeoutException | SocketException e) { ++ // May throw a SocketTimeoutException or SocketException rather than a SSLHandshakeException ++ throw new SSLHandshakeException(e.getMessage()); ++ } ++ } ++} +diff --git a/test/org/apache/tomcat/util/net/ocsp/TesterOcspResponder.java b/test/org/apache/tomcat/util/net/ocsp/TesterOcspResponder.java +new file mode 100644 +index 0000000000..27def84db8 +--- /dev/null ++++ b/test/org/apache/tomcat/util/net/ocsp/TesterOcspResponder.java +@@ -0,0 +1,125 @@ ++/* ++ * 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.ocsp; ++ ++import java.io.BufferedReader; ++import java.io.IOException; ++import java.io.InputStreamReader; ++import java.io.PrintStream; ++import java.io.Reader; ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.List; ++import java.util.Map; ++import java.util.concurrent.TimeUnit; ++ ++import org.junit.Assert; ++ ++import org.apache.tomcat.util.net.TesterSupport; ++ ++/* ++ * The OpenSSL ocsp tool is great, but it does generate a lot of output. That output needs to be swallowed, else the ++ * process will freeze when the output buffers (stdout and stderr) are full. ++ * ++ * There is a command line option to redirect stdout (which could be redirected to /dev/null), but there is no option to ++ * redirect stderr. Therefore, this class uses a couple of dedicated threads to read stdout and stderr. By default, the ++ * output is ignored, but it can be dumped to Java's stdout/stderr if required for debugging purposes. ++ */ ++public class TesterOcspResponder { ++ ++ private static List ocspArgs = Arrays.asList("ocsp", "-port", "8888", "-text", "-index", ++ TesterSupport.DB_INDEX, "-CA", TesterSupport.CA_CERT_PEM, "-rkey", TesterSupport.OCSP_RESPONDER_RSA_KEY, ++ "-rsigner", TesterSupport.OCSP_RESPONDER_RSA_CERT, "-nmin", "60"); ++ ++ private Process p; ++ ++ public void start() throws IOException { ++ if (p != null) { ++ throw new IllegalStateException("Already started"); ++ } ++ ++ String openSSLPath = System.getProperty("tomcat.test.openssl.path"); ++ String openSSLLibPath = null; ++ if (openSSLPath == null || openSSLPath.length() == 0) { ++ openSSLPath = "openssl"; ++ } else { ++ // Explicit OpenSSL path may also need explicit lib path ++ // (e.g. Gump needs this) ++ openSSLLibPath = openSSLPath.substring(0, openSSLPath.lastIndexOf('/')); ++ openSSLLibPath = openSSLLibPath + "/../:" + openSSLLibPath + "/../lib:" + openSSLLibPath + "/../lib64"; ++ } ++ List cmd = new ArrayList<>(); ++ cmd.add(openSSLPath); ++ cmd.addAll(ocspArgs); ++ ++ ProcessBuilder pb = new ProcessBuilder(cmd.toArray(new String[0])); ++ ++ if (openSSLLibPath != null) { ++ Map env = pb.environment(); ++ String libraryPath = env.get("LD_LIBRARY_PATH"); ++ if (libraryPath == null) { ++ libraryPath = openSSLLibPath; ++ } else { ++ libraryPath = libraryPath + ":" + openSSLLibPath; ++ } ++ env.put("LD_LIBRARY_PATH", libraryPath); ++ } ++ ++ p = pb.start(); ++ ++ redirect(new BufferedReader(new InputStreamReader(p.getInputStream())) , System.out, true); ++ redirect(new BufferedReader(new InputStreamReader(p.getErrorStream())), System.err, true); ++ ++ Assert.assertTrue(p.isAlive()); ++ } ++ ++ public void stop() { ++ if (p == null) { ++ throw new IllegalStateException("Not started"); ++ } ++ p.destroy(); ++ ++ try { ++ if (!p.waitFor(30, TimeUnit.SECONDS)) { ++ throw new IllegalStateException("Failed to stop"); ++ } ++ } catch (InterruptedException e) { ++ throw new IllegalStateException("Interrupted while waiting to stop", e); ++ } ++ } ++ ++ ++ private void redirect(final Reader r, final PrintStream os, final boolean swallow) { ++ /* ++ * InputStream will close when process ends. Thread will exit once stream closes. ++ */ ++ new Thread( () -> { ++ char[] cbuf = new char[1024]; ++ try { ++ int read; ++ while ((read = r.read(cbuf)) > 0) { ++ if (!swallow) { ++ os.print(new String(cbuf, 0, read)); ++ } ++ } ++ } catch (IOException ignore) { ++ // Ignore ++ } ++ ++ }).start(); ++ } ++} +diff --git a/test/org/apache/tomcat/util/net/ocsp/ocsp-responder.lock b/test/org/apache/tomcat/util/net/ocsp/ocsp-responder.lock +new file mode 100644 +index 0000000000..e69de29bb2 +diff --git a/test/org/apache/tomcat/util/net/user1.jks b/test/org/apache/tomcat/util/net/user1.jks +index 76625a9eab..727de3cccc 100644 +Binary files a/test/org/apache/tomcat/util/net/user1.jks and b/test/org/apache/tomcat/util/net/user1.jks differ +diff --git a/test/org/apache/tomcat/util/net/user2-crl.jks b/test/org/apache/tomcat/util/net/user2-crl.jks +new file mode 100644 +index 0000000000..068517b598 +Binary files /dev/null and b/test/org/apache/tomcat/util/net/user2-crl.jks differ +diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml +--- a/webapps/docs/changelog.xml 2026-04-21 15:48:53.192243701 -0400 ++++ b/webapps/docs/changelog.xml 2026-04-21 15:49:48.893470762 -0400 +@@ -104,6 +104,782 @@ + They eventually become mixed with the numbered issues (i.e., numbered + issues do not "pop up" wrt. others). + --> ++
++ ++ ++ ++ Refactor generation of the remote user element in the access log to ++ remove unnecessary code. (markt) ++ ++ ++ Fix a regression in the previous release that meant ?- ++ could appear in the access log rather than ? when the query ++ string was present but empty. (markt) ++ ++ ++ Failed precondition should make WebDAV DELETE fail. 982 ++ submitted by Mahmoud Alarby. (remm) ++ ++ ++ Align the escaping in ExtendedAccessLogValve with the other ++ AccessLogValve implementations. (markt) ++ ++ ++ ++ ++ ++ ++ Avoid various edge cases if Content-Length is set via ++ setHeader(String,String) or ++ addHeader(String,String) with an invalid value by always ++ clearing the previous value whether the new value is valid or not and ++ ignoring any invalid new value. (markt) ++ ++ ++ Refactor the calculation of the real index in the HPACK dynamic header ++ table implementation to reduce code duplication. (markt) ++ ++ ++ Fix various minor issues with some HTTP/2 stream error messages for ++ HTTP/2. (markt) ++ ++ ++ Consistently reject URIs containing NULL bytes when ++ normalizing. (markt) ++ ++ ++ Fix a few minor memory leaks on error paths reading TLS keys and ++ certificates when using FFM. (markt) ++ ++ ++ Avoid possible NPEs when using a TLS enabled custom connector. (remm) ++ ++ ++ Refactor clean-up after HTTP/2 headers have been processed to aid GC ++ after a stream reset. (markt) ++ ++ ++ Align HTTP/2 trailer fields with HTTP/1.1 and filter out any fields ++ not permitted in trailers. (markt) ++ ++ ++ ++ ++ ++ ++ 69993: Update the URL to the CDDL 1.0 license. (markt) ++ ++ ++ ++ ++ ++ ++ Add escaping for URI and query string in the access log. (markt) ++ ++ ++ ++ ++ ++ ++ Align buffer reuse of the OpenSSLEngine for tomcat-native with the FFM ++ code. (remm) ++ ++ ++ Fix an HTTP/2 header frame parsing bug that could result in a connection ++ being closed without a GOAWAY frame if an invalid ++ HEADERS frame was received. (markt) ++ ++ ++ 69982: Fix a bug in the non-blocking flushing code for ++ NIO+TLS that meant that a response may not be fully written until the ++ connection is closed. Pull request 966 provided by Phil Clay. ++ (markt) ++ ++ ++ Ensure the HTTP/2 request header read buffer is reset (including ++ restoration to default size) after a stream reset. (markt) ++ ++ ++ Provide trailer field filtering equivalent to that provided for ++ non-trailer fields. Control characters (excluding TAB), and characters ++ with code points above 255 will be replaced with a space. (markt) ++ ++ ++ Align OpenSSl FFM behaviour with Tomcat Native for various OCSP edge ++ cases. (markt) ++ ++ ++ ++ ++ ++ ++ 69995: Make dependent ordering predictable. Patch submitted ++ by Jan Luehe. (remm) ++ ++ ++ ++ ++ ++ ++ Reduce log verbosity of the Kubernetes connection attempts and failure. ++ (remm) ++ ++ ++ Better error handling for the EncryptInterceptor. (markt) ++ ++ ++ ++ ++ ++ ++ Update bnd to 7.2.3. (markt) ++ ++ ++ Improvements to French translations. (remm) ++ ++ ++ Improvements to Japanese translations provided by tak7iji. (markt) ++ ++ ++ ++ ++ ++ ++ 69967: Fix inconsistencies related to ++ Content-Length and Content-Type headers when ++ accessed using the getHeader method and similar. (remm) ++ ++ ++ 69940: Improve redirect handling in the ++ LoadBalancerDrainingValve. (schultz) ++ ++ ++ ++ ++ ++ ++ 69938: Avoid changing the closed state of TLS channel when ++ resetting it after close. (remm) ++ ++ ++ Add an HTTP configuration setting, noCompressionEncodings, ++ that can be used to control which content encodings will not be ++ compressed when compression is enabled. Based on pull request ++ 914 by Long9725. (markt) ++ ++ ++ Add size limit for OCSP responses. Based on code submitted by Chenjp. ++ (remm) ++ ++ ++ To maintain the documented alignment with the OpenSSL development ++ branch, the use of the aliases SSLv3, EXPORT, ++ EXPORT40, EXPORT56, KRB5, ++ kFZA, aFZA, eFZA and ++ FZA are no longer supported when setting the ++ ciphers attribute of an SSLHostConfig element. ++ (markt) ++ ++ ++ To maintain the documented alignment with the OpenSSL development ++ branch, add support for the aliases ARIAGCM and ++ CBCwhen setting the ciphers attribute of ++ an SSLHostConfig element. (markt) ++ ++ ++ 69870: Add a drainTimeout to the HTTP/2 ++ UpgradeProtocol element to allow configuration of an ++ time between the two final GOAWAY frames sent by Tomcat ++ when closing am HTTP/2 connection. Pull request 917 provided by ++ Kai Burjack. (markt) ++ ++ ++ Log an information message if an APR Connector is used, recommending ++ that the appropriate NIO Connector is used instead. (markt) ++ ++ ++ Respect the value for the jdk.tls.namedGroups system ++ property as the default value for the configured group list on the ++ Connector. (remm) ++ ++ ++ 69964: Respect the configured cipher order, which was no ++ longer respected following the addition of TLS 1.3 specific cipher ++ configuration. TLS 1.3 ciphers will always be first in the list. (remm) ++ ++ ++ Free the x509 object in the FFM code when getting the peer certificate ++ if getting the bytes from the certificate somehow fails. ++ Pull request 951 provided by Chenjp. (remm) ++ ++ ++ Improve HPACK exception use, making sure HpackException ++ is thrown instead of unexpected types. (remm) ++ ++ ++ Update the parser for the HTTP Host header and ++ :authority pseudo header to convert the port, if any, to an ++ Integer rather than a Long to be consistent ++ with how port is exposed in the Servlet API. (markt) ++ ++ ++ To aid the migration from the single ciphers configuration ++ attribute to the use of ciphers and ++ cipherSuites, TLS 1.3 cipher suites listed in the ++ ciphers attribute will be removed from the ++ ciphers attribute and added to the end of the ++ cipherSuites attribute. This behaviour will be removed in ++ Tomcat 12.0.x onwards. (markt) ++ ++ ++ Replace the external OpenSSL based OCSP responder used during unit tests ++ with a Bouncy Castle based, in-process Java OCSP responder. (markt) ++ ++ ++ Relax HTTP/2 header validation and respond to invalid requests with a ++ stream reset or a 400 response as appropriate rather then with a ++ connection reset. (markt) ++ ++ ++ Add validation of chunk extensions for chunked transfer encoding. ++ (markt) ++ ++ ++ Update the recommended version for Tomcat Native to 1.3.7. (markt) ++ ++ ++ Align the FFM handling of OCSP TRY_LATER responses with ++ Tomcat Native. (remm) ++ ++ ++ Free CA certificate after calling SSL_CTX_add_client_CA ++ in the FFM code. Based on code from PR 44 from tomcat-native. (remm) ++ ++ ++ Free certificate chain if an error occurs, in the FFM code. (remm) ++ ++ ++ Report handshake issues as SSLException in the FFM ++ code, rather than IllegalStateException. (remm) ++ ++ ++ Fix case sensitive handling of the protocol host name. (remm) ++ ++ ++ ++ ++ ++ ++ 69948: Avoid ArrayOutOfBoundsException instead of ++ PropertyNotFoundException when generating a properties not found ++ exception in AstValue. Based on 950 submitted by Jérôme ++ Besnard. (remm) ++ ++ ++ Add support for specifying Java 27 (with the value 27) as ++ the compiler source and/or compiler target for JSP compilation. If used ++ with an Eclipse JDT compiler version that does not support these values, ++ a warning will be logged and the default will be used. ++ (markt) ++ ++ ++ ++ ++ ++ ++ 69970: Support raw IPv6 in Kubernetes membership provider ++ for the service host. (remm) ++ ++ ++ Add support for new algorithms provided by JPA providers to the ++ EncryptInterceptor. (markt) ++ ++ ++ ++ ++ ++ ++ 69972: Remove unwanted space in DIGEST authorization header. ++ Patch submitted by Stefan Kalscheuer in 957. (remm) ++ ++ ++ 69844: Close the connection with a protocol error if the ++ server sends masked frames. (markt) ++ ++ ++ ++ ++ ++ ++ 69931: Add <label> for fields in the HTML manager ++ application. Patch provided by yukitidev. (schultz) ++ ++ ++ ++ ++ ++ ++ Update the internal fork of Apache Commons BCEL to 6.12.0. (markt) ++ ++ ++ Update Tomcat Native to 1.3.7. (markt) ++ ++ ++ Update Objenesis to 3.5. (markt) ++ ++ ++ Update Byte Buddy to 1.18.7. (markt) ++ ++ ++ Update BND to 7.2.1. (markt) ++ ++ ++ Improvements to French translations. (remm) ++ ++ ++ Improvements to Chinese translations provided by eaststrongox. (markt) ++ ++ ++ Improvements to Japanese translations provided by tak7iji. (markt) ++ ++ ++ ++ ++ ++ ++ Prevent concurrent release of OpenSSLEngine resources and ++ the termination of the Tomcat Native library as it can cause crashes ++ during Tomcat shutdown. (markt) ++ ++ ++ ++ ++ ++ ++ Add property "gpg.sign.files" to optionally disable release artefact ++ signing with GPG. (rjung) ++ ++ ++ ++ ++ ++ ++ 69623: Additional fix for the long standing regression that ++ meant that calls to ClassLoader.getResource().getContent() ++ failed when made from within a web application with resource caching ++ enabled if the target resource was packaged in a JAR file. (markt) ++ ++ ++ Pull request 923: Avoid adding multiple CSRF tokens to a URL in ++ the CsrfPreventionFilter. (schultz) ++ ++ ++ 69918: Ensure request parameters are correctly parsed for ++ HTTP/2 requests when the content-length header is not set. (dsoumis) ++ ++ ++ Update the minimum and recommended versions for Tomcat Native to 1.3.4. ++ (markt) ++ ++ ++ Add a new ssoReauthenticationMode to the Tomcat provided ++ Authenticators that provides a per Authenticator override of the SSO ++ Valve requireReauthentication attribute. (markt) ++ ++ ++ Ensure URL encoding errors in the Rewrite Valve trigger an exception ++ rather than silently using a replacement character. (markt) ++ ++ ++ ++ ++ ++ ++ Improve warnings when setting ciphers lists in the FFM code, mirroring ++ the tomcat-native changes. (remm) ++ ++ ++ 69910: Dereference TLS objects right after closing a socket ++ to improve memory efficiency. (remm) ++ ++ ++ Relax the JSSE vs OpenSSL configuration style checks on ++ SSLHostConfig to reflect the existing implementation that ++ allows one configuration style to be used for the trust attributes and a ++ different style for all the other attributes. (markt) ++ ++ ++ Better warning message when OpenSSLConf configuration ++ elements are used with a JSSE TLS implementation. (markt) ++ ++ ++ When using OpenSSL via FFM, don't log a warning about missing CA ++ certificates unless CA certificates were configured and the ++ configuration failed. (markt) ++ ++ ++ For configuration consistency between OpenSSL and JSSE TLS ++ implementations, TLSv1.3 cipher suites included in the ++ ciphers attribute of an SSLHostConfig are now ++ always ignored (previously they would be ignored with OpenSSL ++ implementations and used with JSSE implementations) and a warning is ++ logged that the cipher suite has been ignored. (markt) ++ ++ ++ Add the ciphersuite attribute to ++ SSLHostConfig to configure the TLSv1.3 cipher suites. ++ (markt) ++ ++ ++ Add OCSP support to JSSE based TLS connectors and make the use of OCSP ++ configurable per connector for both JSSE and OpenSSL based TLS ++ implementations. Align the checks performed by OpenSSL with those ++ performed by JSSE. (markt) ++ ++ ++ Add support for soft failure of OCSP checks with soft failure support ++ disabled by default. (markt) ++ ++ ++ Add support for configuring the verification flags passed to ++ OCSP_basic_verify when using an OpenSSL based TLS ++ implementation. (markt) ++ ++ ++ Fix OpenSSL FFM code compatibility with LibreSSL versions below 3.5. ++ (remm) ++ ++ ++ ++ ++ ++ ++ 69333: Correct a regression in the previous fix for ++ 69333 and ensure that reuse() or ++ release() is always called for a tag. (markt) ++ ++ ++ ++ ++ ++ ++ 62814: Document that human-readable names may be used for ++ mapSendOptions and align documentation with ++ channelSendOptions. Based on pull request 929 by ++ archan0621. (markt) ++ ++ ++ ++ ++ ++ ++ 69920: When attempting to write to a closed ++ Writer or OutputStream obtained from a ++ WebSocket session, throw an IOException rather than an ++ IllegalStateExcpetion as required by Writer ++ and strongly suggested by OutputStream. (markt) ++ ++ ++ ++ ++ ++ ++ Add test.silent property to suppress JUnit console output ++ during test execution. Useful for cleaner console output when running ++ tests with multiple threads. (csutherl) ++ ++ ++ Update the internal fork of Commons Pool to 2.13.1. (markt) ++ ++ ++ Update the internal fork of Commons DBCP to 2.14.0. (markt) ++ ++ ++ Update Commons Daemon to 1.5.1. (markt) ++ ++ ++ Update ByteBuddy to 1.18.3. (markt) ++ ++ ++ Update UnboundID to 7.0.4. (markt) ++ ++ ++ Update Checkstyle to 12.3.1. (markt) ++ ++ ++ Improvements to French translations. (markt) ++ ++ ++ Improvements to Japanese translations provided by tak7iji. (markt) ++ ++ ++ Improvements to Chinese translations provided by Yang. vincent.h and ++ yong hu. (markt) ++ ++ ++ Update Tomcat Native to 1.3.5. (markt) ++ ++ ++ Update bnd to 7.2.0. (markt) ++ ++ ++ ++ ++ ++ ++ 69871: Increase log level to INFO for missing configuration ++ for the rewrite valve. (remm) ++ ++ ++ Add log warnings for additional Host appBase suspicious ++ values. (remm) ++ ++ ++ Remove hard dependency on tomcat-jni.jar for catalina.jar. ++ org.apache.catalina.Connector no longer requires ++ org.apache.tomcat.jni.AprStatus to be present. (markt) ++ ++ ++ Add the ability to use a custom function to generate the client ++ identifier in the CrawlerSessionManagerValve. This is only ++ available programmatically. Pull request 902 by Brian Matzon. ++ (markt) ++ ++ ++ Change the SSO reauthentication behaviour for SPNEGO authentication so ++ that a normal SPNEGO authentication is performed if the SSL Valve is ++ configured with reauthentication enabled. This is so that the delegated ++ credentials will be available to the web application. (markt) ++ ++ ++ ++ ++ ++ ++ Don't log an incorrect certificate KeyStore location when ++ creating a TLS connector if the KeyStore instance has been ++ set directly on the connector. (markt) ++ ++ ++ HTTP/0.9 only allows GET as the HTTP method. (remm) ++ ++ ++ Add strictSni attribute on the Connector to ++ allow matching the SSLHostConfig configuration associated ++ with the SNI host name to the SSLHostConfig configuration ++ matched from the HTTP protocol host name. Non matching configurations ++ will cause the request to be rejected. The attribute default value is ++ true, enabling the matching. (remm) ++ ++ ++ ++ ++ ++ ++ 69877: Catch IllegalArgumentException when processing URIs ++ when creating the classpath to handle invalid URIs. (remm) ++ ++ ++ Fix populating the classpath with the webapp classloader repositories. ++ (remm) ++ ++ ++ ++ ++ ++ ++ Correct a regression introduced in 9.0.109 that broke some clustering ++ configurations. (markt) ++ ++ ++ ++ ++ ++ ++ Manager: Fix abrupt truncation of the HTML and JSON complete server ++ status output if one or more of the web applications failed to start. ++ (schultz) ++ ++ ++ Manager: Include web application state in the HTML and JSON complete ++ server status output. (markt) ++ ++ ++ Documentation: Expand the documentation to better explain when OCSP is ++ supported and when it is not. (markt) ++ ++ ++ ++ ++ ++ ++ 64083: If the underlying connection has been closed, don't ++ add it to the pool when it is returned. Pull request 235 by ++ Alex Panchenko. (markt) ++ ++ ++ ++ ++ ++ ++ Add test profile system for selective test execution. Profiles can be ++ specified via -Dtest.profile=<name> to run specific ++ test subsets without using patterns directly. Profile patterns are ++ defined in test-profiles.properties. (csutherl) ++ ++ ++ Update file extension to media type mappings to align with the current ++ list used by the Apache Web Server (httpd). (markt) ++ ++ ++ Update Commons Daemon to 1.5.0. (markt) ++ ++ ++ Update Byte Buddy to 1.18.2. (markt) ++ ++ ++ Update Checkstyle to 12.2.0. (markt) ++ ++ ++ Improvements to Spanish translations provided by White Vogel. (markt) ++ ++ ++ Improvements to French translations. (remm) ++ ++ ++ Improvements to Japanese translations provided by tak7iji. (markt) ++ ++ ++ ++ ++ ++ ++ When generating the class path in the Loader, re-order the check on ++ individual class path components to avoid a potential ++ NullPointerException. Identified by Coverity Scan. (markt) ++ ++ ++ Fix SSL socket factory configuration in the JNDI realm. Based on pull ++ request 915 by Joshua Rogers. (remm) ++ ++ ++ Add an attribute, digestInRfc3112Order, to ++ MessageDigestCredentialHandler to control the order in ++ which the credential and salt are digested. By default, the current, ++ non-RFC 3112 compliant, order of salt then credential will be used. This ++ default will change in Tomcat 12 to the RFC 3112 compliant order of ++ credential then salt. (markt) ++ ++ ++ ++ ++ ++ ++ Graceful failure for OCSP on BoringSSL in the FFM code. (remm) ++ ++ ++ 69866: Fix a memory leak when using a trust store with the ++ OpenSSL provider. Pull request 912 by aogburn. (markt) ++ ++ ++ Fix AJP message length check. Pull request 916 by Joshua ++ Rogers. (remm) ++ ++ ++ ++ ++ ++ ++ 69862: Avoid NPE unwrapping Servlet exception which would ++ hide some exception details. Patch submitted by Eric Blanquer. (remm) ++ ++ ++ ++ ++ ++ ++ Update the internal fork of Apache Commons BCEL to 6.11.0. (markt) ++ ++ ++ Update to Byte Buddy 1.17.8. (markt) ++ ++ ++ Update to Checkstyle 12.1.1. (markt) ++ ++ ++ Update to Jacoco 0.8.14. (markt) ++ ++ ++ Update to SpotBugs 4.9.8. (markt) ++ ++ ++ Update to JSign 7.4. (markt) ++ ++ ++ Update Maven Resolver Ant Tasks to 1.6.0. (rjung) ++ ++ ++ Improvements to French translations. (remm) ++ ++ ++ Improvements to Japanese translations provided by tak7iji. (markt) ++ ++ ++ ++ ++ ++ ++ Log warnings when the SSO configuration does not comply with the ++ documentation. (remm) ++ ++ ++ Deprecate the RemoteAddrFilter and ++ RemoteAddrValve in favour of the ++ RemoteCIDRFilter and RemoteCIDRValve. (markt) ++ ++ ++ 69837: Fix corruption of the class path generated by the ++ Loader when running on Windows. (markt) ++ ++ ++ Reject requests that map to invalid Windows file names earlier. (markt) ++ ++ ++ 69839: Ensure that changes to session IDs (typically after ++ authentication) are promulgated to the SSO Valve to ensure that SSO ++ entries are fully clean-up on session expiration. Patch provided by Kim ++ Johan Andersson. (markt) ++ ++ ++ Fix a race condition in the creation of the storage location for the ++ FileStore. (markt) ++ ++ ++ ++ ++ ++ ++ 69848: Fix copy/paste errors in 9.0.110 that meant DELETE ++ requests received via the AJP connector were processed as OPTIONS ++ requests and PROPFIND requests were processed as TRACE. (markt) ++ ++ ++ Various OCSP processing issues in the OpenSSL FFM code. (dsoumis) ++ ++ ++ ++ ++ ++ ++ 69845: When using permessage-deflate with Java ++ 25 onwards, handle the underlying Inflater and/or ++ Deflater throwing IllegalStateException ++ when closed rather than NullPointerException as they do in ++ Java 24 and earlier. (markt) ++ ++ ++ ++
+
+ + + diff --git a/rhel-168081.patch b/rhel-168081.patch index cc65185..3781b0b 100644 --- a/rhel-168081.patch +++ b/rhel-168081.patch @@ -31,24 +31,4 @@ diff -up ./test/org/apache/catalina/realm/TestRealmBase.java ./test/org/apache/c SecurityConstraint[] constraintsDelete = mapRealm.findSecurityConstraints(request, context); -diff -up ./webapps/docs/changelog.xml.orig ./webapps/docs/changelog.xml ---- ./webapps/docs/changelog.xml.orig 2026-04-14 15:48:53.192243701 -0400 -+++ ./webapps/docs/changelog.xml 2026-04-14 15:49:48.893470762 -0400 -@@ -104,6 +104,17 @@ - They eventually become mixed with the numbered issues (i.e., numbered - issues do not "pop up" wrt. others). - --> -+
-+ -+ -+ -+ 69848: Fix copy/paste error that meant DELETE -+ requests received via the AJP connector were processed as OPTIONS -+ requests. (markt) -+ -+ -+ -+
-
- - + diff --git a/tomcat.spec b/tomcat.spec index 6146ece..935ebe7 100644 --- a/tomcat.spec +++ b/tomcat.spec @@ -56,7 +56,7 @@ Name: tomcat Epoch: 1 Version: %{major_version}.%{minor_version}.%{micro_version} -Release: 3%{?dist} +Release: 4%{?dist} Summary: Apache Servlet/JSP Engine, RI for Servlet %{servletspec}/JSP %{jspspec} API License: ASL 2.0 @@ -82,6 +82,7 @@ Patch3: %{name}-%{major_version}.%{minor_version}-catalina-policy.patch Patch4: rhbz-1857043.patch Patch6: %{name}-%{major_version}.%{minor_version}-bnd-annotation.patch Patch7: rhel-168081.patch +Patch8: rhel-150714.patch BuildArch: noarch @@ -199,6 +200,7 @@ find . -type f \( -name "*.bat" -o -name "*.class" -o -name Thumbs.db -o -name " %patch -P4 -p0 %patch -P6 -p0 %patch -P7 -p1 +%patch -P8 -p1 # Remove webservices naming resources as it's generally unused %{__rm} -rf java/org/apache/naming/factory/webservices @@ -563,8 +565,10 @@ fi %defattr(0644,tomcat,tomcat,0755) %{appdir}/ROOT - %changelog +* Tue Apr 20 2026 Pietro Meloni - 1:9.0.110-4 +- Resolves: RHEL-150714 Certificate revocation bypass due to improper OCSP response validation + * Tue Apr 14 2026 Coty Sutherland - 1:9.0.110-3 - Resolves: RHEL-168081 Fix copy/paste error in AJP connector that caused DELETE requests to be processed as OPTIONS requests (BZ#69848)