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