This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/11.0.x by this push:
new 80e7ada50c Enable OCSP on the client side (to verify server certs)
80e7ada50c is described below
commit 80e7ada50c6f46885877968466b1049fba3aeff2
Author: Mark Thomas <[email protected]>
AuthorDate: Thu Dec 4 17:19:03 2025 +0000
Enable OCSP on the client side (to verify server certs)
---
test/org/apache/tomcat/util/net/TesterSupport.java | 66 +++++++--
.../tomcat/util/net/ocsp/TestOcspEnabled.java | 151 ++++++++++++++++++---
2 files changed, 186 insertions(+), 31 deletions(-)
diff --git a/test/org/apache/tomcat/util/net/TesterSupport.java
b/test/org/apache/tomcat/util/net/TesterSupport.java
index 6a769978f6..ba1a514a52 100644
--- a/test/org/apache/tomcat/util/net/TesterSupport.java
+++ b/test/org/apache/tomcat/util/net/TesterSupport.java
@@ -24,12 +24,24 @@ import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyStore;
+import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
+import java.security.cert.CertPathValidator;
+import java.security.cert.Certificate;
import java.security.cert.CertificateException;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.PKIXRevocationChecker;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
+import java.util.EnumSet;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+import javax.net.ssl.CertPathTrustManagerParameters;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
@@ -183,33 +195,61 @@ public final class TesterSupport {
}
protected static TrustManager[] getTrustManagers() throws Exception {
- TrustManagerFactory tmf = TrustManagerFactory.getInstance(
- TrustManagerFactory.getDefaultAlgorithm());
- tmf.init(getKeyStore(CA_JKS));
+ return getTrustManagers(false);
+ }
+
+ protected static TrustManager[] getTrustManagers(boolean enableOcsp)
throws Exception {
+ KeyStore trustStore = getKeyStore(CA_JKS);
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
+ if (enableOcsp) {
+ Set<TrustAnchor> trustAnchors =
getTrustAnchorsFromKeystore(trustStore);
+ PKIXBuilderParameters pkix = new
PKIXBuilderParameters(trustAnchors, new X509CertSelector());
+ PKIXRevocationChecker revocationChecker =
+ (PKIXRevocationChecker)
CertPathValidator.getInstance("PKIX").getRevocationChecker();
+
revocationChecker.setOptions(EnumSet.of(PKIXRevocationChecker.Option.NO_FALLBACK));
+ pkix.addCertPathChecker(revocationChecker);
+ tmf.init(new CertPathTrustManagerParameters(pkix));
+ } else {
+ tmf.init(trustStore);
+ }
return tmf.getTrustManagers();
}
+ private static Set<TrustAnchor> getTrustAnchorsFromKeystore(KeyStore
keyStore) throws KeyStoreException {
+ Set<TrustAnchor> trustAnchors = new HashSet<>();
+ Enumeration<String> aliases = keyStore.aliases();
+ while (aliases.hasMoreElements()) {
+ String alias = aliases.nextElement();
+ Certificate certificate = keyStore.getCertificate(alias);
+ if (certificate instanceof X509Certificate) {
+ trustAnchors.add(new TrustAnchor((X509Certificate)
certificate, null));
+ }
+ }
+ return trustAnchors;
+ }
+
public static ClientSSLSocketFactory configureClientSsl() {
- return configureClientSsl(false, null, CLIENT_JKS);
+ return configureClientSsl(false, null, false, CLIENT_JKS);
}
- public static ClientSSLSocketFactory configureClientSsl(String keyStore) {
- return configureClientSsl(false, null, keyStore);
+ public static ClientSSLSocketFactory configureClientSsl(boolean
forceTls12) {
+ return configureClientSsl(forceTls12, null, false, CLIENT_JKS);
}
public static ClientSSLSocketFactory configureClientSsl(String[] ciphers) {
- return configureClientSsl(false, ciphers, CLIENT_JKS);
+ return configureClientSsl(false, ciphers, false, CLIENT_JKS);
}
- public static ClientSSLSocketFactory configureClientSsl(boolean
forceTls12) {
- return configureClientSsl(forceTls12, null, CLIENT_JKS);
+ public static ClientSSLSocketFactory configureClientSsl(boolean
forceTls12, String[] ciphers) {
+ return configureClientSsl(forceTls12, ciphers, false, CLIENT_JKS);
}
- public static ClientSSLSocketFactory configureClientSsl(boolean
forceTls12, String[] ciphers) {
- return configureClientSsl(forceTls12, ciphers, CLIENT_JKS);
+ public static ClientSSLSocketFactory configureClientSsl(boolean
enableOcsp, String keyStore) {
+ return configureClientSsl(false, null, enableOcsp, keyStore);
}
- public static ClientSSLSocketFactory configureClientSsl(boolean
forceTls12, String[] ciphers, String keyStore) {
+ public static ClientSSLSocketFactory configureClientSsl(boolean
forceTls12, String[] ciphers, boolean enableOcsp,
+ String keyStore) {
ClientSSLSocketFactory clientSSLSocketFactory = null;
try {
SSLContext sc;
@@ -218,7 +258,7 @@ public final class TesterSupport {
} else {
sc = SSLContext.getInstance(Constants.SSL_PROTO_TLSv1_2);
}
- sc.init(getUserKeyManagers(keyStore), getTrustManagers(), null);
+ sc.init(getUserKeyManagers(keyStore),
getTrustManagers(enableOcsp), null);
clientSSLSocketFactory = new
ClientSSLSocketFactory(sc.getSocketFactory());
if (ciphers != null) {
clientSSLSocketFactory.setCipher(ciphers);
diff --git a/test/org/apache/tomcat/util/net/ocsp/TestOcspEnabled.java
b/test/org/apache/tomcat/util/net/ocsp/TestOcspEnabled.java
index 31b77e1709..9b52c8cee5 100644
--- a/test/org/apache/tomcat/util/net/ocsp/TestOcspEnabled.java
+++ b/test/org/apache/tomcat/util/net/ocsp/TestOcspEnabled.java
@@ -30,6 +30,7 @@ import jakarta.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;
@@ -121,39 +122,133 @@ public class TestOcspEnabled extends TomcatBaseTest {
@Test
- public void testValidClientValidServerVerifyNone() throws Exception {
- doTest(true, true, false, false, HttpServletResponse.SC_OK);
+ public void testRevokedClientRevokedServerVerifyNone() throws Exception {
+ doTest(false, false, ClientCertificateVerification.DISABLED, false);
+ }
+
+ @Test(expected = SSLHandshakeException.class)
+ public void testRevokedClientRevokedServerVerifyServer() throws Exception {
+ doTest(false, false, ClientCertificateVerification.DISABLED, true);
}
@Test
- public void testValidClientRevokedServerVerifyNone() throws Exception {
- doTest(true, false, false, false, HttpServletResponse.SC_OK);
+ public void testRevokedClientRevokedServerVerifyClientOptional() throws
Exception {
+ doTest(false, false, ClientCertificateVerification.OPTIONAL_NO_CA,
false);
+ }
+
+ @Test(expected = SSLHandshakeException.class)
+ public void
testRevokedClientRevokedServerVerifyClientOpionalVerifyServer() throws
Exception {
+ // Same as false, false, false, true since server certificate is
verified before client certificate
+ doTest(false, false, ClientCertificateVerification.OPTIONAL_NO_CA,
true);
+ }
+
+ @Test(expected = SSLHandshakeException.class)
+ public void testRevokedClientRevokedServerVerifyClient() throws Exception {
+ doTest(false, false, ClientCertificateVerification.ENABLED, false);
+ }
+
+ @Test(expected = SSLHandshakeException.class)
+ public void testRevokedClientRevokedServerVerifyBoth() throws Exception {
+ // Same as false, false, false, true since server certificate is
verified before client certificate
+ doTest(false, false, ClientCertificateVerification.ENABLED, true);
}
@Test
public void testRevokedClientValidServerVerifyNone() throws Exception {
- doTest(false, true, false, false, HttpServletResponse.SC_OK);
+ doTest(false, true, ClientCertificateVerification.DISABLED, false);
}
@Test
- public void testRevokedClientRevokedServerVerifyNone() throws Exception {
- doTest(false, false, false, false, HttpServletResponse.SC_OK);
+ public void testRevokedClientValidServerVerifyServer() throws Exception {
+ doTest(false, true, ClientCertificateVerification.DISABLED, true);
}
+ @Test
+ public void testRevokedClientValidServerVerifyClientOptional() throws
Exception {
+ doTest(false, true, ClientCertificateVerification.OPTIONAL_NO_CA,
false);
+ }
@Test
- public void testValidClientValidServerVerifyClient() throws Exception {
- doTest(true, true, true, false, HttpServletResponse.SC_OK);
+ public void testRevokedClientValidServerVerifyClientOptionalVerifyServer()
throws Exception {
+ doTest(false, true, ClientCertificateVerification.OPTIONAL_NO_CA,
true);
}
@Test(expected = SSLHandshakeException.class)
public void testRevokedClientValidServerVerifyClient() throws Exception {
- doTest(false, true, true, false, HttpServletResponse.SC_OK);
+ doTest(false, true, ClientCertificateVerification.ENABLED, false);
+ }
+
+ @Test(expected = SSLHandshakeException.class)
+ public void testRevokedClientValidServerVerifyBoth() throws Exception {
+ doTest(false, true, ClientCertificateVerification.ENABLED, true);
+ }
+
+ @Test
+ public void testValidClientRevokedServerVerifyNone() throws Exception {
+ doTest(true, false, ClientCertificateVerification.DISABLED, false);
+ }
+
+ @Test(expected = SSLHandshakeException.class)
+ public void testValidClientRevokedServerVerifyServer() throws Exception {
+ doTest(true, false, ClientCertificateVerification.DISABLED, true);
+ }
+
+ @Test
+ public void testValidClientRevokedServerVerifyClientOptional() throws
Exception {
+ doTest(true, false, ClientCertificateVerification.OPTIONAL_NO_CA,
false);
+ }
+
+ @Test(expected = SSLHandshakeException.class)
+ public void testValidClientRevokedServerVerifyClientOptionalVerifyServer()
throws Exception {
+ doTest(true, false, ClientCertificateVerification.OPTIONAL_NO_CA,
true);
+ }
+
+ @Test
+ public void testValidClientRevokedServerVerifyClient() throws Exception {
+ doTest(true, false, ClientCertificateVerification.ENABLED, false);
+ }
+
+ @Test(expected = SSLHandshakeException.class)
+ public void testValidClientRevokedServerVerifyBoth() throws Exception {
+ doTest(true, false, ClientCertificateVerification.ENABLED, true);
+ }
+
+ @Test
+ public void testValidClientValidServerVerifyNone() throws Exception {
+ doTest(true, true, ClientCertificateVerification.DISABLED, false);
+ }
+
+ @Test
+ public void testValidClientValidServerVerifyServer() throws Exception {
+ doTest(true, true, ClientCertificateVerification.DISABLED, true);
+ }
+
+ @Test
+ public void testValidClientValidServerVerifyClientOptional() throws
Exception {
+ doTest(true, true, ClientCertificateVerification.OPTIONAL_NO_CA,
false);
}
+ @Test
+ public void testValidClientValidServerVerifyClientOptionalVerifyServer()
throws Exception {
+ doTest(true, true, ClientCertificateVerification.OPTIONAL_NO_CA, true);
+ }
+
+ @Test
+ public void testValidClientValidServerVerifyClient() throws Exception {
+ doTest(true, true, ClientCertificateVerification.ENABLED, false);
+ }
+
+ @Test
+ public void testValidClientValidServerVerifyBoth() throws Exception {
+ doTest(true, true, ClientCertificateVerification.ENABLED, true);
+ }
+
+
+ private void doTest(boolean clientCertValid, boolean serverCertValid,
ClientCertificateVerification verifyClientCert,
+ boolean verifyServerCert) throws Exception {
+
+ Assume.assumeFalse(!useOpenSSLTrust && verifyClientCert ==
ClientCertificateVerification.OPTIONAL_NO_CA);
- private void doTest(boolean clientCertValid, boolean serverCertValid,
boolean verifyClientCert,
- boolean verifyServerCert, int expectedStatusCode) throws Exception
{
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
@@ -168,20 +263,40 @@ public class TestOcspEnabled extends TomcatBaseTest {
TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_CRL_RSA_JKS,
useOpenSSLTrust);
}
SSLHostConfig sslHostConfig =
tomcat.getConnector().findSslHostConfigs()[0];
- sslHostConfig.setCertificateVerification("required");
- sslHostConfig.setOcspEnabled(verifyClientCert);
+ switch (verifyClientCert) {
+ case DISABLED:
+ sslHostConfig.setOcspEnabled(false);
+ sslHostConfig.setCertificateVerification("required");
+ break;
+ case ENABLED:
+ sslHostConfig.setOcspEnabled(true);
+ sslHostConfig.setCertificateVerification("required");
+ break;
+ case OPTIONAL_NO_CA:
+ sslHostConfig.setOcspEnabled(true);
+ sslHostConfig.setCertificateVerification("optionalNoCA");
+ break;
+
+ }
if (clientCertValid) {
- TesterSupport.configureClientSsl(TesterSupport.CLIENT_JKS);
+ TesterSupport.configureClientSsl(verifyServerCert,
TesterSupport.CLIENT_JKS);
} else {
- TesterSupport.configureClientSsl(TesterSupport.CLIENT_CRL_JKS);
+ TesterSupport.configureClientSsl(verifyServerCert,
TesterSupport.CLIENT_CRL_JKS);
}
- // TODO enable client-side OCSP checks
tomcat.start();
int rc = getUrl("https://localhost:" + getPort() + "/simple", new
ByteChunk(), false);
- Assert.assertEquals(expectedStatusCode, rc);
+ // If the TLS handshake fails, the test won't get this far.
+ Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+ }
+
+
+ private enum ClientCertificateVerification {
+ ENABLED,
+ OPTIONAL_NO_CA,
+ DISABLED
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]