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

markt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 0d5a383053e688b501c7a9d93ad82a2536f25558
Author: Mark Thomas <[email protected]>
AuthorDate: Mon Dec 1 14:07:59 2025 +0000

    Add OCSP support for all Connectors along with enable/disable support
---
 .../apache/catalina/storeconfig/OpenSSLConfSF.java |  13 +-
 java/org/apache/tomcat/util/net/SSLHostConfig.java |  10 ++
 java/org/apache/tomcat/util/net/SSLUtilBase.java   |  24 ++-
 .../tomcat/util/net/openssl/OpenSSLConfCmd.java    |  11 ++
 .../tomcat/util/net/openssl/OpenSSLContext.java    |  16 +-
 .../util/net/openssl/panama/OpenSSLContext.java    |   6 +-
 .../tomcat/util/net/ocsp/TestOcspIntegration.java  | 189 +++++++++++++++------
 webapps/docs/changelog.xml                         |   5 +
 webapps/docs/config/http.xml                       |   8 +
 9 files changed, 217 insertions(+), 65 deletions(-)

diff --git a/java/org/apache/catalina/storeconfig/OpenSSLConfSF.java 
b/java/org/apache/catalina/storeconfig/OpenSSLConfSF.java
index 167f2836c7..2c35cdacd4 100644
--- a/java/org/apache/catalina/storeconfig/OpenSSLConfSF.java
+++ b/java/org/apache/catalina/storeconfig/OpenSSLConfSF.java
@@ -17,6 +17,7 @@
 package org.apache.catalina.storeconfig;
 
 import java.io.PrintWriter;
+import java.util.Set;
 
 import org.apache.tomcat.util.net.openssl.OpenSSLConf;
 import org.apache.tomcat.util.net.openssl.OpenSSLConfCmd;
@@ -26,6 +27,8 @@ import org.apache.tomcat.util.net.openssl.OpenSSLConfCmd;
  */
 public class OpenSSLConfSF extends StoreFactoryBase {
 
+    private static final Set<String> INTERNAL_COMMANDS = 
Set.of(OpenSSLConfCmd.NO_OCSP_CHECK);
+
     /**
      * Store nested OpenSSLConfCmd elements.
      * <p>
@@ -36,9 +39,13 @@ public class OpenSSLConfSF extends StoreFactoryBase {
             throws Exception {
         if (aOpenSSLConf instanceof OpenSSLConf openSslConf) {
             // Store nested <OpenSSLConfCmd> elements
-            OpenSSLConfCmd[] openSSLConfCmds = 
openSslConf.getCommands().toArray(new OpenSSLConfCmd[0]);
-            storeElementArray(aWriter, indent + 2, openSSLConfCmds);
+            for (OpenSSLConfCmd openSSLConfCmd : openSslConf.getCommands()) {
+                // Don't store the internal commands that are created from 
other SslHostConfig attributes.
+                if (INTERNAL_COMMANDS.contains(openSSLConfCmd.getName())) {
+                    continue;
+                }
+                storeElement(aWriter, indent + 2, openSSLConfCmd);
+            }
         }
     }
-
 }
diff --git a/java/org/apache/tomcat/util/net/SSLHostConfig.java 
b/java/org/apache/tomcat/util/net/SSLHostConfig.java
index 82859e273f..319707070a 100644
--- a/java/org/apache/tomcat/util/net/SSLHostConfig.java
+++ b/java/org/apache/tomcat/util/net/SSLHostConfig.java
@@ -117,6 +117,7 @@ public class SSLHostConfig implements Serializable {
     private LinkedHashSet<Cipher> cipherSuiteList = null;
     private List<String> jsseCipherNames = null;
     private boolean honorCipherOrder = false;
+    private boolean ocspEnabled = false;
     private final Set<String> protocols = new HashSet<>();
     // Values <0 mean use the implementation default
     private int sessionCacheSize = -1;
@@ -561,6 +562,15 @@ public class SSLHostConfig implements Serializable {
     }
 
 
+    public boolean getOcspEnabled() {
+        return ocspEnabled;
+    }
+
+    public void setOcspEnabled(boolean ocspEnabled) {
+        this.ocspEnabled = ocspEnabled;
+    }
+
+
     public void setProtocols(String input) {
         protocols.clear();
         explicitlyRequestedProtocols.clear();
diff --git a/java/org/apache/tomcat/util/net/SSLUtilBase.java 
b/java/org/apache/tomcat/util/net/SSLUtilBase.java
index ed85d4ab19..e2c9e141fd 100644
--- a/java/org/apache/tomcat/util/net/SSLUtilBase.java
+++ b/java/org/apache/tomcat/util/net/SSLUtilBase.java
@@ -28,6 +28,7 @@ import java.security.KeyStore;
 import java.security.cert.CRL;
 import java.security.cert.CRLException;
 import java.security.cert.CertPathParameters;
+import java.security.cert.CertPathValidator;
 import java.security.cert.CertStore;
 import java.security.cert.CertStoreParameters;
 import java.security.cert.Certificate;
@@ -37,12 +38,14 @@ import java.security.cert.CertificateFactory;
 import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.CollectionCertStoreParameters;
 import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.PKIXRevocationChecker;
 import java.security.cert.X509CertSelector;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
+import java.util.EnumSet;
 import java.util.Enumeration;
 import java.util.List;
 import java.util.Locale;
@@ -520,8 +523,10 @@ public abstract class SSLUtilBase implements SSLUtil {
      *
      * @throws Exception An error occurred
      */
-    protected CertPathParameters getParameters(String crlf, KeyStore 
trustStore, boolean revocationEnabled)
-            throws Exception {
+    protected CertPathParameters getParameters(final String crlf, final 
KeyStore trustStore,
+            final boolean revocationEnabled) throws Exception {
+
+        boolean enableRevocation = revocationEnabled;
 
         PKIXBuilderParameters xparams = new PKIXBuilderParameters(trustStore, 
new X509CertSelector());
         if (crlf != null && !crlf.isEmpty()) {
@@ -529,11 +534,20 @@ public abstract class SSLUtilBase implements SSLUtil {
             CertStoreParameters csp = new CollectionCertStoreParameters(crls);
             CertStore store = CertStore.getInstance("Collection", csp);
             xparams.addCertStore(store);
-            xparams.setRevocationEnabled(true);
-        } else {
-            xparams.setRevocationEnabled(revocationEnabled);
+            enableRevocation = true;
+        }
+
+        if (sslHostConfig.getOcspEnabled()) {
+            PKIXRevocationChecker revocationChecker =(PKIXRevocationChecker) 
CertPathValidator.getInstance("PKIX").getRevocationChecker();
+            
revocationChecker.setOptions(EnumSet.of(PKIXRevocationChecker.Option.SOFT_FAIL));
+            xparams.addCertPathChecker(revocationChecker);
+            enableRevocation = true;
         }
+
+        xparams.setRevocationEnabled(enableRevocation);
+
         
xparams.setMaxPathLength(sslHostConfig.getCertificateVerificationDepth());
+
         return xparams;
     }
 
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLConfCmd.java 
b/java/org/apache/tomcat/util/net/openssl/OpenSSLConfCmd.java
index f11cc483af..1d2362a5ed 100644
--- a/java/org/apache/tomcat/util/net/openssl/OpenSSLConfCmd.java
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLConfCmd.java
@@ -21,12 +21,23 @@ import java.io.Serializable;
 
 public class OpenSSLConfCmd implements Serializable {
 
+    // Tomcat / Tomcat Native custom commands. Used internally by Tomcat. Not 
intended for direct use by users.
+    public static final String NO_OCSP_CHECK = "NO_OCSP_CHECK";
+
     @Serial
     private static final long serialVersionUID = 1L;
 
     private String name = null;
     private String value = null;
 
+    public OpenSSLConfCmd() {
+    }
+
+    public OpenSSLConfCmd(String name, String value) {
+        this.name = name;
+        this.value = value;
+    }
+
     public String getName() {
         return name;
     }
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java 
b/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java
index f29c08d39c..eb58a94375 100644
--- a/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java
@@ -102,8 +102,18 @@ public class OpenSSLContext implements 
org.apache.tomcat.util.net.SSLContext {
         boolean success = false;
         try {
             // Create OpenSSLConfCmd context if used
-            OpenSSLConf openSslConf = sslHostConfig.getOpenSslConf();
-            if (openSslConf != null) {
+            if (sslHostConfig.getOpenSslConf() == null && 
sslHostConfig.getTrustManagerClassName() == null &&
+                    sslHostConfig.getTruststore() == null) {
+                /*
+                 * If an instance of OpenSSLConf is required, it must be 
created here so the reference can be placed in
+                 * the (immutable) OpenSSLState record.
+                 *
+                 * If OpenSSL managed trust is used, an instance of 
OpenSSLConf is required to pass OCSP configuration
+                 * parameters to Tomcat Native. Create one if one hasn't 
already been created.
+                 */
+                sslHostConfig.setOpenSslConf(new OpenSSLConf());
+            }
+            if (sslHostConfig.getOpenSslConf() != null) {
                 try {
                     if (log.isTraceEnabled()) {
                         log.trace(sm.getString("openssl.makeConf"));
@@ -355,6 +365,8 @@ public class OpenSSLContext implements 
org.apache.tomcat.util.net.SSLContext {
                 SSLContext.setCACertificate(state.ctx,
                         
SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificateFile()),
                         
SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificatePath()));
+                sslHostConfig.getOpenSslConf().addCmd(new 
OpenSSLConfCmd(OpenSSLConfCmd.NO_OCSP_CHECK,
+                        Boolean.toString(!sslHostConfig.getOcspEnabled())));
             }
 
             if (negotiableProtocols != null && !negotiableProtocols.isEmpty()) 
{
diff --git a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java 
b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
index 77e4de2c87..1abac2d005 100644
--- a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
+++ b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
@@ -353,7 +353,7 @@ public class OpenSSLContext implements 
org.apache.tomcat.util.net.SSLContext {
                 log.trace(sm.getString("opensslconf.checkCommand", name, 
value));
             }
             try (var localArena = Arena.ofConfined()) {
-                if (name.equals("NO_OCSP_CHECK")) {
+                if (name.equals(OpenSSLConfCmd.NO_OCSP_CHECK)) {
                     ok = true;
                 } else {
                     int code = SSL_CONF_cmd_value_type(state.confCtx, 
localArena.allocateFrom(name));
@@ -422,7 +422,7 @@ public class OpenSSLContext implements 
org.apache.tomcat.util.net.SSLContext {
                 log.trace(sm.getString("opensslconf.applyCommand", name, 
value));
             }
             try (var localArena = Arena.ofConfined()) {
-                if (name.equals("NO_OCSP_CHECK")) {
+                if (name.equals(OpenSSLConfCmd.NO_OCSP_CHECK)) {
                     noOcspCheck = Boolean.parseBoolean(value);
                     rc = 1;
                 } else {
@@ -549,7 +549,7 @@ public class OpenSSLContext implements 
org.apache.tomcat.util.net.SSLContext {
                 case REQUIRED -> SSL_VERIFY_FAIL_IF_NO_PEER_CERT();
             };
 
-            if (value == OPTIONAL_NO_CA) {
+            if (value == OPTIONAL_NO_CA || !sslHostConfig.getOcspEnabled()) {
                 noOcspCheck = true;
             }
 
diff --git a/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java 
b/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java
index 9012d7196a..0d9403f68f 100644
--- a/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java
+++ b/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java
@@ -97,21 +97,19 @@ public class TestOcspIntegration extends TomcatBaseTest {
     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}")
+    @Parameterized.Parameters(name = "{0} with OpenSSL trust {2}")
     public static Collection<Object[]> parameters() {
         List<Object[]> 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"});
+        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;
     }
@@ -123,30 +121,57 @@ public class TestOcspIntegration extends TomcatBaseTest {
     public boolean useOpenSSL;
 
     @Parameter(2)
+    public boolean useOpenSSLTrust;
+
+    @Parameter(3)
     public String sslImplementationName;
 
 
     @Test
     public void testOcspGood_ClientVerifiesServerCertificateOnly() throws 
Exception {
-        Assert.assertEquals(HttpServletResponse.SC_OK, 
testOCSP(OCSP_SERVER_CERT_GOOD_RESPONSE, false, true));
+        Assert.assertEquals(HttpServletResponse.SC_OK,
+                testOCSP(OCSP_SERVER_CERT_GOOD_RESPONSE, 
ServerSideOcspVerification.DISABLED, true));
+    }
+
+    @Test
+    public void testOcspGood_ClientVerifiesServerCertificateOnlyNoCA() throws 
Exception {
+        // optionalNoCA is only available with OpenSSL trust
+        Assume.assumeTrue(useOpenSSLTrust);
+        Assert.assertEquals(HttpServletResponse.SC_OK,
+                testOCSP(OCSP_SERVER_CERT_GOOD_RESPONSE, 
ServerSideOcspVerification.OPTIONAL_NO_CA, true));
     }
 
     @Test
     public void testOcspGood_Mutual() throws Exception {
+        // Native 2.0.x validates the response timestamp which fails for the 
canned response.
+        Assume.assumeFalse(useOpenSSLTrust);
         testOCSPWithClientResponder(OCSP_CLIENT_CERT_GOOD_RESPONSE, () -> 
Assert.assertEquals(HttpServletResponse.SC_OK,
-                testOCSP(OCSP_SERVER_CERT_GOOD_RESPONSE, true, true)));
+                testOCSP(OCSP_SERVER_CERT_GOOD_RESPONSE, 
ServerSideOcspVerification.ENABLED, true)));
     }
 
     @Test
     public void testOcspGood_ServerVerifiesClientCertificateOnly() throws 
Exception {
+        // Native 2.0.x validates the response timestamp which fails for the 
canned response.
+        Assume.assumeFalse(useOpenSSLTrust);
         testOCSPWithClientResponder(OCSP_CLIENT_CERT_GOOD_RESPONSE, () -> 
Assert.assertEquals(HttpServletResponse.SC_OK,
-                testOCSP(OCSP_SERVER_CERT_REVOKED_RESPONSE, true, false)));
+                testOCSP(OCSP_SERVER_CERT_REVOKED_RESPONSE, 
ServerSideOcspVerification.ENABLED, false)));
     }
 
     @Test(expected = CertificateRevokedException.class)
     public void testOcspRevoked_ClientVerifiesServerCertificateOnly() throws 
Exception {
         try {
-            testOCSP(OCSP_SERVER_CERT_REVOKED_RESPONSE, false, true);
+            testOCSP(OCSP_SERVER_CERT_REVOKED_RESPONSE, 
ServerSideOcspVerification.DISABLED, true);
+        } catch (SSLHandshakeException sslHandshakeException) {
+            handleExceptionWhenRevoked(sslHandshakeException);
+        }
+    }
+
+    @Test(expected = CertificateRevokedException.class)
+    public void testOcspRevoked_ClientVerifiesServerCertificateOnlyNoCA() 
throws Exception {
+        // optionalNoCA is only available with OpenSSL trust
+        Assume.assumeTrue(useOpenSSLTrust);
+        try {
+            testOCSP(OCSP_SERVER_CERT_REVOKED_RESPONSE, 
ServerSideOcspVerification.OPTIONAL_NO_CA, true);
         } catch (SSLHandshakeException sslHandshakeException) {
             handleExceptionWhenRevoked(sslHandshakeException);
         }
@@ -156,7 +181,7 @@ public class TestOcspIntegration extends TomcatBaseTest {
     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);
+            testOCSP(OCSP_SERVER_CERT_REVOKED_RESPONSE, 
ServerSideOcspVerification.ENABLED, true);
         } catch (SSLHandshakeException sslHandshakeException) {
             handleExceptionWhenRevoked(sslHandshakeException);
         }
@@ -167,13 +192,23 @@ public class TestOcspIntegration extends TomcatBaseTest {
         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));
+                () -> testOCSP(OCSP_SERVER_CERT_GOOD_RESPONSE, 
ServerSideOcspVerification.ENABLED, 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)));
+        testOCSPWithClientResponder(OCSP_CLIENT_CERT_REVOKED_RESPONSE,
+                () -> Assert.assertEquals(HttpServletResponse.SC_OK,
+                        testOCSP(OCSP_SERVER_CERT_REVOKED_RESPONSE, 
ServerSideOcspVerification.DISABLED, false)));
+    }
+
+    @Test
+    public void testOcsp_NoVerificationNoCA() throws Exception {
+        // optionalNoCA is only available with OpenSSL trust
+        Assume.assumeTrue(useOpenSSLTrust);
+        testOCSPWithClientResponder(OCSP_CLIENT_CERT_REVOKED_RESPONSE,
+                () -> Assert.assertEquals(HttpServletResponse.SC_OK,
+                        testOCSP(OCSP_SERVER_CERT_REVOKED_RESPONSE, 
ServerSideOcspVerification.OPTIONAL_NO_CA, false)));
     }
 
     @Test
@@ -181,17 +216,30 @@ public class TestOcspIntegration extends TomcatBaseTest {
         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));
+                testOCSP(OCSP_SERVER_CERT_GOOD_RESPONSE, 
ServerSideOcspVerification.DISABLED, true, true, ocspPort));
+    }
+
+    @Test
+    public void testOcspResponderUrlDiscoveryViaCertificateAIANoCA() throws 
Exception {
+        final int ocspPort = 8888;
+        // optionalNoCA is only available with OpenSSL trust
+        Assume.assumeTrue(useOpenSSLTrust);
+        Assume.assumeTrue("Port " + ocspPort + " is not available.", 
isPortAvailable(ocspPort));
+        Assert.assertEquals(HttpServletResponse.SC_OK, 
testOCSP(OCSP_SERVER_CERT_GOOD_RESPONSE,
+                ServerSideOcspVerification.OPTIONAL_NO_CA, 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)) {
+        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);
+            testOCSP(tomcat, OCSP_SERVER_CERT_GOOD_RESPONSE, 
ServerSideOcspVerification.ENABLED, false, false, 0,
+                    
"org.apache.tomcat.util.net.openssl.OpenSSLImplementation", true, true);
         }
     }
 
@@ -212,28 +260,31 @@ public class TestOcspIntegration extends TomcatBaseTest {
         }
     }
 
-    private int testOCSP(String pathToOcspResponse, boolean 
serverSideVerificationEnabled,
+    private int testOCSP(String pathToOcspResponse, ServerSideOcspVerification 
serverSideOcspVerification,
             boolean clientSideOcspVerificationEnabled) throws Exception {
-        return testOCSP(pathToOcspResponse, serverSideVerificationEnabled, 
clientSideOcspVerificationEnabled, false, 0);
+        return testOCSP(pathToOcspResponse, serverSideOcspVerification, 
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 int testOCSP(String pathToOcspResponse, ServerSideOcspVerification 
serverSideOcspVerification,
+            boolean clientSideOcspVerificationEnabled, boolean 
clientDiscoversResponderFromAIA, int ocspResponderPort)
+            throws Exception {
+        return testOCSP(getTomcatInstance(), pathToOcspResponse, 
serverSideOcspVerification,
+                clientSideOcspVerificationEnabled, 
clientDiscoversResponderFromAIA, ocspResponderPort,
+                sslImplementationName, useOpenSSL, useOpenSSLTrust);
     }
 
-    private static int testOCSP(Tomcat tomcat, String pathToOcspResponse, 
boolean serverSideVerificationEnabled,
-            boolean clientSideOcspVerificationEnabled, boolean 
clientDiscoversResponderFromAIA, int ocspResponderPort,
-                                String sslImplementationName, boolean 
useOpenSSL)
-            throws Exception {
+    private static int testOCSP(Tomcat tomcat, String pathToOcspResponse,
+            ServerSideOcspVerification serverSideOcspVerification, boolean 
clientSideOcspVerificationEnabled,
+            boolean clientDiscoversResponderFromAIA, int ocspResponderPort, 
String sslImplementationName,
+            boolean useOpenSSL, boolean useOpenSSLTrust) 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);
+        File truststoreFile = new File(getPath(TRUSTSTORE_PATH));
+        String truststorePass = Files.readString(new 
File(getPath(CLIENT_KEYSTORE_PASS)).toPath()).trim();
+        initSsl(tomcat, serverSideOcspVerification, useOpenSSLTrust, 
certificateFile, certificateKeyFile,
+                certificateChainFile, truststoreFile, truststorePass);
 
         TesterSupport.configureSSLImplementation(tomcat, 
sslImplementationName, useOpenSSL);
 
@@ -272,24 +323,46 @@ public class TestOcspIntegration extends TomcatBaseTest {
         }
     }
 
-    private static void initSsl(Tomcat tomcat, boolean 
serverSideVerificationEnabled, File certificateFile,
-            File certificateKeyFile, File certificateChainFile) {
+    private static void initSsl(Tomcat tomcat, ServerSideOcspVerification 
serverSideOcspVerification,
+            boolean useOpenSSLTrust, File certificateFile, File 
certificateKeyFile, File certificateChainFile,
+            File truststoreFile, String truststorePassword) {
         Connector connector = tomcat.getConnector();
         connector.setSecure(true);
         connector.setProperty("SSLEnabled", "true");
 
         SSLHostConfig sslHostConfig = new SSLHostConfig();
-        SSLHostConfigCertificate certificate = new 
SSLHostConfigCertificate(sslHostConfig, 
SSLHostConfigCertificate.Type.UNDEFINED);
+        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");
+
+        switch (serverSideOcspVerification) {
+            case DISABLED:
+                sslHostConfig.setCertificateVerification("required");
+                sslHostConfig.setOcspEnabled(false);
+                break;
+            case OPTIONAL_NO_CA:
+                sslHostConfig.setCertificateVerification("optionalNoCA");
+                sslHostConfig.setOcspEnabled(true);
+                break;
+            case ENABLED:
+                sslHostConfig.setCertificateVerification("required");
+                sslHostConfig.setOcspEnabled(true);
+                break;
+            default:
+                break;
+
+        }
+
+        if (useOpenSSLTrust) {
+            
sslHostConfig.setCaCertificateFile(certificateChainFile.getAbsolutePath());
         } else {
-            sslHostConfig.setCertificateVerification("optionalNoCA");
+            sslHostConfig.setTruststoreType("PKCS12");
+            sslHostConfig.setTruststoreFile(truststoreFile.getAbsolutePath());
+            sslHostConfig.setTruststorePassword(truststorePassword);
         }
-        
sslHostConfig.setCaCertificateFile(certificateChainFile.getAbsolutePath());
         connector.addSslHostConfig(sslHostConfig);
     }
 
@@ -298,7 +371,8 @@ public class TestOcspIntegration extends TomcatBaseTest {
         KeyManagerFactory kmf = 
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
         kmf.init(clientKeystore, clientKeystorePass.toCharArray());
         Set<TrustAnchor> trustAnchors = 
getTrustAnchorsFromKeystore(trustStore);
-        PKIXRevocationChecker revocationChecker =(PKIXRevocationChecker) 
CertPathValidator.getInstance("PKIX").getRevocationChecker();
+        PKIXRevocationChecker revocationChecker =
+                (PKIXRevocationChecker) 
CertPathValidator.getInstance("PKIX").getRevocationChecker();
         if (ocspUrl != null) {
             revocationChecker.setOcspResponder(new URI(ocspUrl));
         }
@@ -316,7 +390,8 @@ public class TestOcspIntegration extends TomcatBaseTest {
             String clientKeystorePass) throws Exception {
         KeyManagerFactory kmf = 
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
         kmf.init(clientKeystore, clientKeystorePass.toCharArray());
-        TrustManagerFactory trustManagerFactory = 
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        TrustManagerFactory trustManagerFactory =
+                
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
         trustManagerFactory.init(trustStore);
         return initSSLContext(kmf, trustManagerFactory).getSocketFactory();
     }
@@ -340,7 +415,7 @@ public class TestOcspIntegration extends TomcatBaseTest {
             String alias = aliases.nextElement();
             Certificate certificate = keyStore.getCertificate(alias);
             if (certificate instanceof X509Certificate) {
-                trustAnchors.add(new TrustAnchor((X509Certificate)certificate, 
null));
+                trustAnchors.add(new TrustAnchor((X509Certificate) 
certificate, null));
             }
         }
         return trustAnchors;
@@ -354,7 +429,8 @@ public class TestOcspIntegration extends TomcatBaseTest {
             if (cpe.getCause() instanceof CertificateRevokedException) {
                 throw (CertificateRevokedException) cpe.getCause();
             } else {
-                throw new CertificateRevokedException(new Date(), 
CRLReason.KEY_COMPROMISE, new X500Principal(""), new HashMap<>());
+                throw new CertificateRevokedException(new Date(), 
CRLReason.KEY_COMPROMISE, new X500Principal(""),
+                        new HashMap<>());
             }
         }
     }
@@ -403,7 +479,9 @@ public class TestOcspIntegration extends TomcatBaseTest {
         String url() {
             return "http://127.0.0.1:"; + port + path;
         }
-        @Override public void close() {
+
+        @Override
+        public void close() {
             if (server != null) {
                 server.stop(0);
             }
@@ -429,4 +507,11 @@ public class TestOcspIntegration extends TomcatBaseTest {
             return false;
         }
     }
+
+
+    private enum ServerSideOcspVerification {
+        ENABLED,
+        OPTIONAL_NO_CA,
+        DISABLED
+    }
 }
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 95ebcc5e7c..37822bd41b 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -279,6 +279,11 @@
         <code>SSLHostConfig</code> to configure the TLSv1.3 cipher suites.
         (markt)
       </add>
+      <add>
+        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. (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Jasper">
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
index 625444bf8f..77ed17c417 100644
--- a/webapps/docs/config/http.xml
+++ b/webapps/docs/config/http.xml
@@ -1354,6 +1354,14 @@
       documentation for the default value.</p>
     </attribute>
 
+    <attribute name="ocspEnabled" required="false">
+      <p>If enabled, client certificates with an OCSP responder URI in the
+      Authority Information Access extension will be validated using that OCSP
+      responder.</p>
+      <p>If not specified, the default value of <code>false</code> will be
+      used.</p>
+    </attribute>
+
     <attribute name="protocols" required="false">
       <p>The names of the protocols to support when communicating with clients.
       This should be a list of any combination of the following:


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to