This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 10.1.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 906f63f3c2849f24bf64d37167d399e6ba054d43 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 22f4fe6256..016041a7bb 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> @@ -37,9 +40,13 @@ public class OpenSSLConfSF extends StoreFactoryBase { if (aOpenSSLConf instanceof OpenSSLConf) { OpenSSLConf openSslConf = (OpenSSLConf) aOpenSSLConf; // 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 61c0efcb04..013bb4e9cf 100644 --- a/java/org/apache/tomcat/util/net/SSLHostConfig.java +++ b/java/org/apache/tomcat/util/net/SSLHostConfig.java @@ -115,6 +115,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; @@ -559,6 +560,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 0325324f70..537781a4c5 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; @@ -521,8 +524,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()) { @@ -530,11 +535,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 3706009295..c9068a2359 100644 --- a/java/org/apache/tomcat/util/net/openssl/OpenSSLConfCmd.java +++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLConfCmd.java @@ -20,11 +20,22 @@ 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"; + 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 a4ac3f7f4e..7968505674 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")); @@ -364,6 +374,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 44bcad84db..94be4408a8 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; @@ -355,7 +430,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<>()); } } } @@ -404,7 +480,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); } @@ -430,4 +508,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 2d4dc2b028..d918664c30 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -170,6 +170,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="Cluster"> diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml index 4a12e601c8..9f76b17fee 100644 --- a/webapps/docs/config/http.xml +++ b/webapps/docs/config/http.xml @@ -1466,6 +1466,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]
