This is an automated email from the ASF dual-hosted git repository.
dsoumis pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push:
new 5694f50881 Add tests for OCSP integration
5694f50881 is described below
commit 5694f50881b66208f201e5579f6762cb3427c5ec
Author: Dimitris Soumis <[email protected]>
AuthorDate: Thu Sep 25 14:55:54 2025 +0300
Add tests for OCSP integration
---
.gitignore | 1 +
.../tomcat/util/net/ocsp/TestOcspIntegration.java | 439 +++++++++++++++++++++
test/org/apache/tomcat/util/net/ocsp/ca-cert.pem | 19 +
.../util/net/ocsp/generate-ocsp-test-artifacts.sh | 133 +++++++
test/org/apache/tomcat/util/net/ocsp/ocsp-good.der | Bin 0 -> 1280 bytes
.../apache/tomcat/util/net/ocsp/ocsp-revoked.der | Bin 0 -> 1302 bytes
.../apache/tomcat/util/net/ocsp/server-cert.pem | 86 ++++
.../org/apache/tomcat/util/net/ocsp/server-key.pem | 28 ++
.../org/apache/tomcat/util/net/ocsp/trust-password | 1 +
.../org/apache/tomcat/util/net/ocsp/trustStore.p12 | Bin 0 -> 1174 bytes
10 files changed, 707 insertions(+)
diff --git a/.gitignore b/.gitignore
index 46615adb41..023f8103da 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,3 +51,4 @@ modules/**/target
modules/jdbc-pool/bin
modules/jdbc-pool/includes
webapps/docs/jdbc-pool.xml
+/test/org/apache/tomcat/util/net/ocsp/ocsp-work/
diff --git a/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java
b/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java
new file mode 100644
index 0000000000..2025be4313
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java
@@ -0,0 +1,439 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.net.ocsp;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.cert.CRLReason;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateRevokedException;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.PKIXRevocationChecker;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.net.ssl.CertPathTrustManagerParameters;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManagerFactory;
+import javax.security.auth.x500.X500Principal;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.compat.JreCompat;
+import org.apache.tomcat.util.net.Constants;
+import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.tomcat.util.net.SSLHostConfigCertificate;
+import org.apache.tomcat.util.net.TesterSupport;
+import org.apache.tomcat.util.net.openssl.OpenSSLConf;
+import org.apache.tomcat.util.net.openssl.OpenSSLConfCmd;
+import org.apache.tomcat.util.net.openssl.OpenSSLImplementation;
+
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpServer;
+
+@RunWith(Parameterized.class)
+public class TestOcspIntegration extends TomcatBaseTest {
+ private static final String CA_CERTIFICATE_PATH = "ca-cert.pem";
+ private static final String SERVER_CERTIFICATE_PATH = "server-cert.pem";
+ private static final String SERVER_CERTIFICATE_KEY_PATH = "server-key.pem";
+ private static final String TRUSTSTORE_PATH = "trustStore.p12";
+ private static final String TRUSTSTORE_PASS = "trust-password";
+ private static final String KEYSTORE_TYPE = "PKCS12";
+ private static final String OCSP_GOOD_RESPONSE = "ocsp-good.der";
+ private static final String OCSP_REVOKED_RESPONSE = "ocsp-revoked.der";
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ List<Object[]> parameterSets = new ArrayList<>();
+ parameterSets.add(new Object[] { Boolean.FALSE });
+ parameterSets.add(new Object[] { Boolean.TRUE });
+ return parameterSets;
+ }
+
+ @Parameterized.Parameter
+ public boolean ffm;
+ @Before
+ public void runtimeCheck() {
+ if (ffm) {
+ Assume.assumeTrue(JreCompat.isJre22Available());
+ }
+ }
+
+ @Test
+ public void testOcspGood() throws Exception {
+ Assert.assertEquals(HttpServletResponse.SC_OK,
testOCSP(OCSP_GOOD_RESPONSE, false, false, ffm));
+ }
+ @Test(expected = CertificateRevokedException.class)
+ public void testOcspRevoked() throws Exception {
+ try {
+ testOCSP(OCSP_REVOKED_RESPONSE, false, false, ffm);
+ }catch (SSLHandshakeException sslHandshakeException) {
+ if (sslHandshakeException.getCause().getCause() instanceof
CertPathValidatorException) {
+ CertPathValidatorException cpe = (CertPathValidatorException)
sslHandshakeException.getCause().getCause();
+ Assert.assertEquals("REVOKED", cpe.getReason().toString());
+ Assert.assertTrue(cpe.toString().contains("reason:
KEY_COMPROMISE"));
+ // Some JDKs only expose CertPathValidatorException
+ if (cpe.getCause() instanceof CertificateRevokedException) {
+ throw (CertificateRevokedException) cpe.getCause();
+ } else {
+ throw new CertificateRevokedException(new Date(),
CRLReason.KEY_COMPROMISE, new X500Principal(""), new HashMap<>());
+ }
+ }
+ }
+ }
+ @Test
+ public void testOcspNoCheck() throws Exception {
+ Assert.assertEquals(HttpServletResponse.SC_OK,
testOCSP(OCSP_REVOKED_RESPONSE, false, true, ffm));
+ }
+ @Test
+ public void testOcspNoCheck_01() throws Exception {
+ Assume.assumeTrue(isSslConfCtxNewAvailable());
+ Assert.assertEquals(HttpServletResponse.SC_OK,
testOCSP(OCSP_REVOKED_RESPONSE, true, true, ffm));
+ }
+ @Test(expected = SSLHandshakeException.class)
+ public void testOcspNoCheck_02() throws Exception {
+ Assume.assumeTrue(isSslConfCtxNewAvailable());
+ testOCSP(OCSP_REVOKED_RESPONSE, true, false, ffm);
+ }
+ @Test
+ public void testOcspNoCheck_03() throws Exception {
+ Assert.assertEquals(HttpServletResponse.SC_OK,
testOCSP(OCSP_REVOKED_RESPONSE, false, true, ffm));
+ }
+ @Test
+ public void testOcspResponderUrlDiscoveryViaCertificateAIA() throws
Exception {
+ final int ocspPort = 8888;
+ Assume.assumeTrue(isPortAvailable(ocspPort));
+ Assert.assertEquals(HttpServletResponse.SC_OK,
testOCSP(OCSP_GOOD_RESPONSE, false, false, ffm,
+ true, "127.0.0.1", ocspPort));
+ }
+ //This test is a reference to CVE-2017-15698 of tomcat-native
+ @Test
+ public void testOcspWithLongResponderUrlViaProxy() throws Exception {
+ final int ocspPort = 8889;
+ Assume.assumeTrue(isPortAvailable(ocspPort));
+ StringBuilder longHostname = new StringBuilder();
+ for (int i = 0; i < 128; i++) {
+ longHostname.append("a");
+ }
+
+ String originalProxyHost = System.getProperty("http.proxyHost");
+ String originalProxyPort = System.getProperty("http.proxyPort");
+
+ try (ForwardingProxy proxy = new ForwardingProxy("127.0.0.1",
ocspPort)) {
+ Thread proxyThread = new Thread(proxy);
+ proxyThread.start();
+ System.setProperty("http.proxyHost", "127.0.0.1");
+ System.setProperty("http.proxyPort",
String.valueOf(proxy.getPort()));
+ try {
+ testOCSP(OCSP_REVOKED_RESPONSE, false, false, ffm,
+ false, longHostname.toString(), ocspPort);
+ Assert.fail("Should have thrown an exception");
+ } catch (SSLHandshakeException sslHandshakeException) {
+ Assert.assertTrue(true);
+ }
+ } finally {
+ if (originalProxyHost == null) {
+ System.clearProperty("http.proxyHost");
+ } else {
+ System.setProperty("http.proxyHost", originalProxyHost);
+ }
+ if (originalProxyPort == null) {
+ System.clearProperty("http.proxyPort");
+ } else {
+ System.setProperty("http.proxyPort", originalProxyPort);
+ }
+ }
+ }
+ private int testOCSP(String pathToOcspResponse, boolean
serverSideOcspVerificationDisabled, boolean clientSideOcspVerificationDisabled,
boolean ffm) throws Exception {
+ return testOCSP(pathToOcspResponse,
serverSideOcspVerificationDisabled, clientSideOcspVerificationDisabled, ffm,
+ false, "127.0.0.1", 0);
+ }
+ private int testOCSP(String pathToOcspResponse, boolean
serverSideOcspVerificationDisabled, boolean clientSideOcspVerificationDisabled,
boolean ffm,
+ boolean discoverResponderFromAIA, String
ocspResponderHostname, int ocspResponderPort) 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));
+ Tomcat tomcat = getTomcatInstance();
+ initSsl(tomcat, certificateFile, certificateKeyFile,
certificateChainFile);
+ TesterSupport.configureSSLImplementation(tomcat,
+ ffm ?
"org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation" :
OpenSSLImplementation.class.getName(),
+ true);
+ if (serverSideOcspVerificationDisabled) {
+ SSLHostConfig sslHostConfig =
tomcat.getConnector().findSslHostConfigs()[0];
+ OpenSSLConf conf = new OpenSSLConf();
+ OpenSSLConfCmd cmd = new OpenSSLConfCmd();
+ cmd.setName("NO_OCSP_CHECK");
+ cmd.setValue("true");
+ conf.addCmd(cmd);
+ sslHostConfig.setOpenSslConf(conf);
+ }
+
+ Context context = tomcat.addContext("", null);
+ Tomcat.addServlet(context, "simple", new
TesterSupport.SimpleServlet());
+ context.addServletMappingDecoded("/", "simple");
+
+ KeyStore trustStorePath = KeyStore.getInstance(KEYSTORE_TYPE);
+ String trustStorePass = new String(Files.readAllBytes(new
File(getPath(TRUSTSTORE_PASS)).toPath())).trim();
+ trustStorePath.load(Files.newInputStream(Paths.get(new
File(getPath(TRUSTSTORE_PATH)).getAbsolutePath())),
trustStorePass.toCharArray());
+ byte[] ocspResponse = Files.readAllBytes(new
File(getPath(pathToOcspResponse)).toPath());
+ try (FakeOcspResponder fakeOcspResponder = new
FakeOcspResponder(ocspResponse, ocspResponderHostname, ocspResponderPort)) {
+ fakeOcspResponder.start();
+ tomcat.start();
+
+ URL url = new URI("https://127.0.0.1:" + getPort() + "/").toURL();
+ HttpsURLConnection connection = (HttpsURLConnection)
url.openConnection();
+ SSLSocketFactory sslSocketFactory;
+ if (clientSideOcspVerificationDisabled) {
+ sslSocketFactory =
buildClientSslSocketFactoryNoOcsp(trustStorePath);
+ } else {
+ sslSocketFactory =
buildClientSslSocketFactoryWithOcsp(discoverResponderFromAIA ? null :
fakeOcspResponder.url(), trustStorePath);
+ }
+ connection.setSSLSocketFactory(sslSocketFactory);
+ connection.connect();
+ return connection.getResponseCode();
+ } finally {
+ tomcat.stop();
+ }
+ }
+
+ private static void initSsl(Tomcat tomcat, File certificateFile, File
certificateKeyFile, File certificateChainFile) {
+ Connector connector = tomcat.getConnector();
+ connector.setSecure(true);
+ Assert.assertTrue(connector.setProperty("SSLEnabled", "true"));
+
+ SSLHostConfig sslHostConfig = new SSLHostConfig();
+ SSLHostConfigCertificate certificate = new
SSLHostConfigCertificate(sslHostConfig,
SSLHostConfigCertificate.Type.UNDEFINED);
+ sslHostConfig.addCertificate(certificate);
+ connector.addSslHostConfig(sslHostConfig);
+ certificate.setCertificateFile(certificateFile.getAbsolutePath());
+
certificate.setCertificateKeyFile(certificateKeyFile.getAbsolutePath());
+
certificate.setCertificateChainFile(certificateChainFile.getAbsolutePath());
+ }
+
+ private static SSLSocketFactory buildClientSslSocketFactoryWithOcsp(String
ocspUrl, KeyStore trustStore) throws Exception {
+ Set<TrustAnchor> trustAnchors =
getTrustAnchorsFromKeystore(trustStore);
+ PKIXRevocationChecker revocationChecker =(PKIXRevocationChecker)
CertPathValidator.getInstance("PKIX").getRevocationChecker();
+ if (ocspUrl != null) {
+ revocationChecker.setOcspResponder(new URI(ocspUrl));
+ }
+
revocationChecker.setOptions(EnumSet.of(PKIXRevocationChecker.Option.NO_FALLBACK));
+
+ PKIXBuilderParameters pkix = new PKIXBuilderParameters(trustAnchors,
new X509CertSelector());
+ pkix.addCertPathChecker(revocationChecker);
+
+ TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance("PKIX");
+ trustManagerFactory.init(new CertPathTrustManagerParameters(pkix));
+ return initSSLContext(trustManagerFactory).getSocketFactory();
+ }
+ private static SSLSocketFactory buildClientSslSocketFactoryNoOcsp(KeyStore
trustStore) throws Exception {
+ TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ trustManagerFactory.init(trustStore);
+ return initSSLContext(trustManagerFactory).getSocketFactory();
+ }
+ private static SSLContext initSSLContext(TrustManagerFactory
trustManagerFactory) throws Exception {
+ SSLContext sslContext;
+ if (TesterSupport.isTlsv13Available()) {
+ sslContext = SSLContext.getInstance(Constants.SSL_PROTO_TLSv1_3);
+ } else {
+ sslContext = SSLContext.getInstance(Constants.SSL_PROTO_TLSv1_2);
+ }
+ sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
+ return sslContext;
+ }
+ 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;
+ }
+
+ private static class FakeOcspResponder implements Closeable {
+ private final byte[] ocspResponse;
+ private HttpServer server;
+ private int port;
+ private final String hostname;
+
+ FakeOcspResponder(byte[] ocspResponse, String hostname, int port) {
+ this.ocspResponse = ocspResponse;
+ this.hostname = hostname;
+ this.port = port;
+ }
+
+ void start() throws IOException {
+ server = HttpServer.create(new InetSocketAddress("127.0.0.1",
port), 0);
+ server.createContext("/ocsp", httpExchange -> {
+ byte[] body = ocspResponse;
+ Headers headers = httpExchange.getResponseHeaders();
+ headers.add("Content-Type", "application/ocsp-response");
+ httpExchange.sendResponseHeaders(HttpServletResponse.SC_OK,
body.length);
+ try (OutputStream os = httpExchange.getResponseBody()) {
+ os.write(body);
+ }
+ });
+ server.start();
+ port = server.getAddress().getPort();
+ }
+
+ String url() {
+ return "http://" + hostname + ":" + port + "/ocsp";
+ }
+ @Override public void close() {
+ if (server != null) {
+ server.stop(0);
+ }
+ }
+ }
+ private static class ForwardingProxy implements Closeable, Runnable {
+ private final ServerSocket serverSocket;
+ private final String targetHost;
+ private final int targetPort;
+ private volatile boolean running = true;
+
+ ForwardingProxy(String targetHost, int targetPort) throws IOException {
+ this.serverSocket = new ServerSocket(0);
+ this.targetHost = targetHost;
+ this.targetPort = targetPort;
+ }
+
+ public int getPort() {
+ return serverSocket.getLocalPort();
+ }
+
+ @Override
+ public void close() throws IOException {
+ running = false;
+ serverSocket.close();
+ }
+
+ @SuppressWarnings("unused")
+ @Override
+ public void run() {
+ try {
+ while (running) {
+ try (Socket clientSocket = serverSocket.accept();
+ Socket targetSocket = new Socket(targetHost,
targetPort)) {
+
+ Thread clientToTarget = new Thread(() -> {
+ try {
+ transfer(clientSocket.getInputStream(),
targetSocket.getOutputStream());
+ } catch (IOException ignored) {}
+ });
+
+ Thread targetToClient = new Thread(() -> {
+ try {
+ transfer(targetSocket.getInputStream(),
clientSocket.getOutputStream());
+ } catch (IOException ignored) {}
+ });
+
+ clientToTarget.start();
+ targetToClient.start();
+ clientToTarget.join();
+ targetToClient.join();
+
+ } catch (IOException | InterruptedException ignored) {}
+ }
+ } finally {
+ try {
+ close();
+ } catch (IOException ignored) {}
+ }
+ }
+
+ private void transfer(InputStream in, OutputStream out) throws
IOException {
+ byte[] buffer = new byte[4096];
+ int read;
+ while ((read = in.read(buffer)) != -1) {
+ out.write(buffer, 0, read);
+ }
+ }
+ }
+
+ private String getPath(String file) throws IOException {
+ if (file == null) {
+ return null;
+ }
+ String packageName = this.getClass().getPackage().getName();
+ String path = packageName.replace(".", File.separator);
+ File f = new File("test" + File.separator + path + File.separator +
file);
+
+ return f.getCanonicalPath();
+ }
+
+ @SuppressWarnings("unused")
+ private boolean isPortAvailable(int port) {
+ try (ServerSocket serverSocket = new ServerSocket(port)) {
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+ private boolean isSslConfCtxNewAvailable() {
+ if (!ffm) {
+ return true;
+ }
+ try {
+
Class.forName("org.apache.tomcat.util.openssl.openssl_h$SSL_CONF_CTX_new");
+ return true;
+ } catch (UnsatisfiedLinkError | NoClassDefFoundError |
ClassNotFoundException | ExceptionInInitializerError e) {
+ // This is the expected error on systems with an incompatible
library (like LibreSSL).
+ return false;
+ }
+ }
+}
diff --git a/test/org/apache/tomcat/util/net/ocsp/ca-cert.pem
b/test/org/apache/tomcat/util/net/ocsp/ca-cert.pem
new file mode 100644
index 0000000000..06daf2fb59
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/ocsp/ca-cert.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDFTCCAf2gAwIBAgIUcS5KhacXj80CKSlsbE3o7ymWS64wDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAeFw0yNTEwMDIxNjM5NTBaFw0zNTA5MzAx
+NjM5NTBaMBIxEDAOBgNVBAMMB1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQCQV9h7r3p4YvnkcL9odnoRaB/QeMicN/tbiSNe3teUI1hazVRh
+6nOMRvyLeaLkB/36d7WTUvZYs07ATWlYpQtQhZmu2yz2uRYjoy1j83zvUt0K13Ke
+F2okzJuueNU8U1qbnxcB8TpiGd+i5gVsghhDS1Qf7sAYv6R1k9+CgHe6ol9gGYno
+qW5/gfLkawmRb8uhsHW7eL0H7hhcHMUiPe/5PT0E7B1WunY4CFuwj0o2XcESkVV1
+YkWDt6n0EuKhWUf/EJSoP1RRs2r5GTJExmep+9Cjs6MGqNpeW85H1uBLNF3KaQuG
+IuA5+2VFAsA6EoPGCrZHSPw16RtARCbJxZpBAgMBAAGjYzBhMA8GA1UdEwEB/wQF
+MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTENStVyjBKve22uGyrVwiF
+ucLvcDAfBgNVHSMEGDAWgBTENStVyjBKve22uGyrVwiFucLvcDANBgkqhkiG9w0B
+AQsFAAOCAQEAVLwgD142VZm5Ti6DyyQaHaqCB8QnVYRJ8hv0aCysTsL6Sb5CgcbD
+iRhNdeS3KPV2xfvAc2rPM0lfr4GPg9sVAgyFKDiFhT5NGlUifCLB0exbYGUPsjCW
+dSVUfTiPMrSkriGM178NvAs0f0Px9WLcl9Mf5qwiRxjDmDmEaBgzV5gySYEMmzSx
+kvi7EBRV+IloNDxJTFHRQGyO2MD7YTjHYeaJcob8YOWuGkfuLNSSJ5boY6JDNEcc
+dHl6LY0M9UG5BEbCFfzYqDL/vvawxPz1Btfyd1qkFqAIWXgtwcr0YuKCIO82fe9A
+BUf3IjydPYc7Y4Vqu1wqocmiI0ztnInKSA==
+-----END CERTIFICATE-----
diff --git
a/test/org/apache/tomcat/util/net/ocsp/generate-ocsp-test-artifacts.sh
b/test/org/apache/tomcat/util/net/ocsp/generate-ocsp-test-artifacts.sh
new file mode 100755
index 0000000000..18b7da9497
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/ocsp/generate-ocsp-test-artifacts.sh
@@ -0,0 +1,133 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Generate OCSP-ready test material for Tomcat integration tests.
+#
+# Output:
+# ca-cert.pem
+# server-cert.pem
+# server-key.pem
+# trustStore.p12
+# trust-password
+# ocsp-good.der
+# ocsp-revoked.der
+#
+# Usage: generate-ocsp-test-artifacts.sh
+#
+
+PASS="changeit"
+WORK_DIR="ocsp-work"
+
+command -v openssl >/dev/null 2>&1 || (printf "OpenSSL not found. Please
install it.\r\n" && exit)
+command -v keytool >/dev/null 2>&1 || (printf "keytool not found. Please
install it.\r\n" && exit)
+
+rm -rf "$WORK_DIR"
+mkdir -p "$WORK_DIR"/private "$WORK_DIR"/newcerts "$WORK_DIR"/certs
+touch "$WORK_DIR/index"
+echo 1000 > "$WORK_DIR/serial"
+
+printf "Writing minimal OpenSSL config..."
+cat > "$WORK_DIR/openssl.cnf" <<'EOF'
+[ ca ]
+default_ca = CA_default
+
+[ CA_default ]
+dir = .
+database = $dir/index
+new_certs_dir = $dir/newcerts
+serial = $dir/serial
+default_md = sha256
+policy = policy_loose
+copy_extensions = copy
+private_key = $dir/private/ca.key.pem
+certificate = $dir/certs/ca.cert.pem
+
+[ policy_loose ]
+commonName = supplied
+
+[ v3_ca ]
+basicConstraints = critical,CA:TRUE
+keyUsage = critical,keyCertSign,cRLSign
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer
+
+[ v3_server ]
+basicConstraints = critical,CA:FALSE
+keyUsage = critical,digitalSignature,keyEncipherment
+extendedKeyUsage = serverAuth
+authorityInfoAccess = OCSP;URI:http://127.0.0.1:8888/ocsp
+subjectAltName = @san
+[ san ]
+IP.1 = 127.0.0.1
+DNS.1 = localhost
+
+[ v3_ocsp ]
+basicConstraints = critical,CA:FALSE
+keyUsage = critical,digitalSignature
+extendedKeyUsage = OCSPSigning
+EOF
+printf "Done.\r\n"
+
+cd "$WORK_DIR" || (printf "Something went wrong.\r\n" && exit)
+
+printf "Generating CA key and certificate...\r\n"
+openssl genrsa -out private/ca.key.pem 2048
+openssl req -x509 -new -nodes -key private/ca.key.pem -days 3650 -subj
"/CN=Test CA" -config openssl.cnf -extensions v3_ca -out certs/ca.cert.pem
+printf "Done.\r\n"
+
+printf "Generating server key and certificate...\r\n"
+openssl genrsa -out private/server.key.pem 2048
+openssl req -new -key private/server.key.pem -out server.csr.pem -subj
"/CN=localhost"
+openssl ca -batch -config openssl.cnf -extensions v3_server -in server.csr.pem
-out certs/server.cert.pem -days 365
+printf "Done.\r\n"
+
+printf "Generating OCSP responder key and certificate...\r\n"
+openssl genrsa -out private/ocsp.key.pem 2048
+openssl req -new -key private/ocsp.key.pem -out ocsp.csr.pem -subj "/CN=Test
OCSP Responder"
+openssl ca -batch -config openssl.cnf -extensions v3_ocsp -in ocsp.csr.pem
-out certs/ocsp.cert.pem -days 365
+printf "Done.\r\n"
+
+printf "Building OCSP request for the server certificate...\r\n"
+openssl ocsp -issuer certs/ca.cert.pem -cert certs/server.cert.pem -no_nonce
-reqout request.der
+printf "Done.\r\n"
+
+printf "Answering request with good status (ocsp-good.der)...\r\n"
+openssl ocsp -index index -CA certs/ca.cert.pem -rsigner certs/ocsp.cert.pem
-rkey private/ocsp.key.pem -no_nonce -ndays 1 -reqin request.der -respout
../ocsp-good.der
+printf "Done.\r\n"
+
+printf "Revoking the server certificate in the CA database...\r\n"
+openssl ca -config openssl.cnf -revoke certs/server.cert.pem -crl_reason
keyCompromise
+printf "Done.\r\n"
+
+printf "Answering request with REVOKED status (ocsp-revoked.der)...\r\n"
+openssl ocsp -index index -CA certs/ca.cert.pem -rsigner certs/ocsp.cert.pem
-rkey private/ocsp.key.pem -no_nonce -ndays 1 -reqin request.der -respout
../ocsp-revoked.der
+printf "Done.\r\n"
+
+cp certs/ca.cert.pem ../ca-cert.pem
+cp private/server.key.pem ../server-key.pem
+cp certs/server.cert.pem ../server-cert.pem
+
+printf "Creating PKCS12 client's truststore (trustStore.p12) with the
CA...\r\n"
+echo "$PASS" > ../trust-password
+keytool -importcert -alias ocsp-ca -file certs/ca.cert.pem -keystore
../trustStore.p12 -storetype PKCS12 -storepass "$PASS" -noprompt
+printf "Done.\r\n"
+
+printf "\r\nOptional verification:\r\n"
+printf " openssl ocsp -respin ocsp-good.der -verify_other
ocsp-work/certs/ocsp.cert.pem -CAfile ca-cert.pem\r\n"
+printf " openssl ocsp -respin ocsp-revoked.der -verify_other
ocsp-work/certs/ocsp.cert.pem -CAfile ca-cert.pem\r\n"
\ No newline at end of file
diff --git a/test/org/apache/tomcat/util/net/ocsp/ocsp-good.der
b/test/org/apache/tomcat/util/net/ocsp/ocsp-good.der
new file mode 100644
index 0000000000..212fccdf64
Binary files /dev/null and b/test/org/apache/tomcat/util/net/ocsp/ocsp-good.der
differ
diff --git a/test/org/apache/tomcat/util/net/ocsp/ocsp-revoked.der
b/test/org/apache/tomcat/util/net/ocsp/ocsp-revoked.der
new file mode 100644
index 0000000000..4ada62e06e
Binary files /dev/null and
b/test/org/apache/tomcat/util/net/ocsp/ocsp-revoked.der differ
diff --git a/test/org/apache/tomcat/util/net/ocsp/server-cert.pem
b/test/org/apache/tomcat/util/net/ocsp/server-cert.pem
new file mode 100644
index 0000000000..3482f37610
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/ocsp/server-cert.pem
@@ -0,0 +1,86 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 4096 (0x1000)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: CN=Test CA
+ Validity
+ Not Before: Oct 2 16:39:51 2025 GMT
+ Not After : Oct 2 16:39:51 2026 GMT
+ Subject: CN=localhost
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b8:c7:fd:37:78:39:13:9a:47:de:18:8b:ca:2a:
+ 6f:49:ba:7e:53:f9:cc:69:95:0b:26:e4:24:25:d1:
+ 89:c2:6d:f1:40:9f:1e:2a:ce:40:0d:3e:a2:2f:98:
+ bb:af:8d:a0:b5:9d:63:1a:cb:b2:51:79:cc:e7:8d:
+ 4a:50:8f:b5:11:66:cc:eb:f8:b3:77:f1:2a:f6:7c:
+ db:8a:9f:d0:90:d4:79:9e:af:20:59:0c:d8:e8:74:
+ 4b:c7:89:bb:8d:41:f3:24:e6:2e:eb:f5:c2:8f:65:
+ 84:fe:be:03:5a:19:8e:a8:c2:5e:f6:54:d6:e5:39:
+ 8a:1a:22:76:ed:31:70:53:c9:48:e5:0d:af:f4:86:
+ 14:60:6e:52:fc:eb:e3:c1:9c:49:f6:f1:47:0e:3e:
+ d8:b0:87:b4:af:0d:2f:84:de:19:e9:6b:f0:09:10:
+ 91:aa:fa:ac:c0:7b:46:de:34:cb:cc:71:14:93:5e:
+ ff:8b:19:33:8c:74:96:2d:06:7a:f9:3c:4b:03:58:
+ 22:55:90:36:ed:43:5b:e8:b7:32:ae:c4:3e:de:b2:
+ 9a:fd:8a:bd:60:b2:2c:0f:5b:b3:2d:61:bc:c6:b2:
+ de:c3:ea:f1:4a:d3:72:72:95:16:fd:1f:64:b3:6d:
+ c2:93:2d:dd:e6:bf:02:76:be:c8:a7:f2:1d:e5:c2:
+ 9d:ad
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ X509v3 Key Usage: critical
+ Digital Signature, Key Encipherment
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ Authority Information Access:
+ OCSP - URI:http://127.0.0.1:8888/ocsp
+ X509v3 Subject Alternative Name:
+ IP Address:127.0.0.1, DNS:localhost
+ X509v3 Subject Key Identifier:
+ DB:59:2C:AD:B5:4D:47:FF:AA:D4:84:5A:49:7B:F7:D5:AA:1D:FE:78
+ X509v3 Authority Key Identifier:
+ C4:35:2B:55:CA:30:4A:BD:ED:B6:B8:6C:AB:57:08:85:B9:C2:EF:70
+ Signature Algorithm: sha256WithRSAEncryption
+ Signature Value:
+ 2c:4d:51:a4:b6:34:c8:57:8d:84:b4:bf:1d:cd:3c:9c:42:bd:
+ 9a:a7:e3:38:d3:43:0f:85:51:bd:23:0b:27:71:ca:27:84:23:
+ 66:da:ab:b6:5e:96:64:da:cf:59:35:63:e3:e3:d6:2a:f7:0f:
+ c4:12:10:95:b3:67:87:48:8c:8e:6a:96:73:f6:63:ad:3a:9d:
+ 19:2a:da:41:8d:bc:04:57:04:13:6b:53:ac:7c:7b:d8:9b:5b:
+ 5c:0f:cd:f3:55:d6:af:69:c9:ea:8d:3f:9a:3a:75:69:88:07:
+ 8f:d1:6c:34:fd:7d:e1:51:9e:2d:9d:65:c3:0a:5e:04:83:9c:
+ 09:b4:76:f3:97:7c:06:8b:35:87:b0:2c:b1:4f:95:61:47:a5:
+ e3:e4:d7:32:8b:21:82:0b:e8:16:7b:81:6f:e2:c7:78:5c:98:
+ 95:60:82:0e:64:62:49:06:09:e4:0a:4a:dc:ee:f0:63:3d:cd:
+ d1:80:21:b1:11:0e:0a:f2:55:ce:b2:c4:36:f5:40:76:4a:f0:
+ f6:e1:7d:d5:b9:43:cb:4d:a7:fe:2a:ab:87:7a:e3:0c:66:aa:
+ 47:35:eb:fd:58:44:4a:fc:92:f0:7c:dc:a1:17:ba:19:61:3a:
+ 2a:1f:99:10:ee:3a:48:cb:99:88:92:17:70:4f:0e:47:d1:3a:
+ ac:da:7f:9a
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UEAwwHVGVz
+dCBDQTAeFw0yNTEwMDIxNjM5NTFaFw0yNjEwMDIxNjM5NTFaMBQxEjAQBgNVBAMM
+CWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALjH/Td4
+OROaR94Yi8oqb0m6flP5zGmVCybkJCXRicJt8UCfHirOQA0+oi+Yu6+NoLWdYxrL
+slF5zOeNSlCPtRFmzOv4s3fxKvZ824qf0JDUeZ6vIFkM2Oh0S8eJu41B8yTmLuv1
+wo9lhP6+A1oZjqjCXvZU1uU5ihoidu0xcFPJSOUNr/SGFGBuUvzr48GcSfbxRw4+
+2LCHtK8NL4TeGelr8AkQkar6rMB7Rt40y8xxFJNe/4sZM4x0li0Gevk8SwNYIlWQ
+Nu1DW+i3Mq7EPt6ymv2KvWCyLA9bsy1hvMay3sPq8UrTcnKVFv0fZLNtwpMt3ea/
+Ana+yKfyHeXCna0CAwEAAaOByjCBxzAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQE
+AwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATA2BggrBgEFBQcBAQQqMCgwJgYIKwYB
+BQUHMAGGGmh0dHA6Ly8xMjcuMC4wLjE6ODg4OC9vY3NwMBoGA1UdEQQTMBGHBH8A
+AAGCCWxvY2FsaG9zdDAdBgNVHQ4EFgQU21ksrbVNR/+q1IRaSXv31aod/ngwHwYD
+VR0jBBgwFoAUxDUrVcowSr3ttrhsq1cIhbnC73AwDQYJKoZIhvcNAQELBQADggEB
+ACxNUaS2NMhXjYS0vx3NPJxCvZqn4zjTQw+FUb0jCydxyieEI2baq7ZelmTaz1k1
+Y+Pj1ir3D8QSEJWzZ4dIjI5qlnP2Y606nRkq2kGNvARXBBNrU6x8e9ibW1wPzfNV
+1q9pyeqNP5o6dWmIB4/RbDT9feFRni2dZcMKXgSDnAm0dvOXfAaLNYewLLFPlWFH
+pePk1zKLIYIL6BZ7gW/ix3hcmJVggg5kYkkGCeQKStzu8GM9zdGAIbERDgryVc6y
+xDb1QHZK8PbhfdW5Q8tNp/4qq4d64wxmqkc16/1YREr8kvB83KEXuhlhOiofmRDu
+OkjLmYiSF3BPDkfROqzaf5o=
+-----END CERTIFICATE-----
diff --git a/test/org/apache/tomcat/util/net/ocsp/server-key.pem
b/test/org/apache/tomcat/util/net/ocsp/server-key.pem
new file mode 100644
index 0000000000..f08c40add2
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/ocsp/server-key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4x/03eDkTmkfe
+GIvKKm9Jun5T+cxplQsm5CQl0YnCbfFAnx4qzkANPqIvmLuvjaC1nWMay7JReczn
+jUpQj7URZszr+LN38Sr2fNuKn9CQ1HmeryBZDNjodEvHibuNQfMk5i7r9cKPZYT+
+vgNaGY6owl72VNblOYoaInbtMXBTyUjlDa/0hhRgblL86+PBnEn28UcOPtiwh7Sv
+DS+E3hnpa/AJEJGq+qzAe0beNMvMcRSTXv+LGTOMdJYtBnr5PEsDWCJVkDbtQ1vo
+tzKuxD7espr9ir1gsiwPW7MtYbzGst7D6vFK03JylRb9H2SzbcKTLd3mvwJ2vsin
+8h3lwp2tAgMBAAECggEAAj041iS9+vz/dPNdh6AhrsY0mgY+Eg4iefYBcsyKEPyY
+aVI5oTF++N9y7gUO9S7QApNl1uEeso+5dMQzYSw0o1bYZzL20x5tiQTJ+5vkgPTK
+L9WVx0POwcEIWrfSZCunQMxh8i24DgPKwPQtgeMrEDl2wXZqujwLGTZPXvVz6S1T
+JOvekFb8rrYe8JQq7Xphv5NdCzsrGt7f2A9UB6+vQyj8K1OmSusyPymd2THDXmVm
+QW9NnR/wdC4XfzE9iU0xdpFu0IqyICfIo0BWkIwfSkGUVEBa21McnQAQlBJ2Y2YP
+nsz7mcxZ7CybWvGRgInXqx4W1gccthYHPKGCwMgW0QKBgQDgNyfxzNvYcJl9lZa5
+n0WQYAncx9qJ+npo2PHSep2JDncwKVSyhx1ftG67VZVVxmLiNu3Sg7h4TL0TPvzp
+pLfjZqCNmh+288XI0Qg2P9SxtkTYCbeusG7pefYSbIyrXlbn22QgPx4JzqWwJ/HB
+a8awfFEuR39A6WiKZJvz80BNuQKBgQDS+cC7IfQnFM3M9UsdrTnNNnwx8MsHZ8f5
+tj8CBmwZhzny3bJuZyG/QiP3NLjaRuwxjhCrhViX937hxTy/aaokUV/U/txu8B4r
++5fWDovj2w2Q72nULYGMJpy5efSrzfg+n0QfLckpgVOkp23bVZ4V6qu0JqcnTBdV
+oserK1jplQKBgB2PJ/T4zQL71UB5OCXAmasu/h3dOzJ1ky2/bCITiDFJdu0ihcgD
+iBvV7cNExStJvD+WfkQCmY9Cjqq64xzqWlPgc8o3R3cXLDwNUsfUInZLUUgp/81H
+9VdSfN5w8RqmlU573fdiWtNwKufOXFDjpI4f1ZQEWQJ1wxJ7sRO4PgIBAoGAFf8m
+QhyTKTP5FSKKF2kFOskwf8B8WqRObFG9Fgf3Y13/A7xrqykjkp5hw5eCond8jtoi
+ENARYZ7TYtS+tbJdo/W8CSgdcY+lha5wsUfI/BPNwUvE+/NWq3cnJgt6ICOUe2r9
+nkaWz7YFM4ilYpX1qpFSH7FcAjPjxvD24njdh1UCgYEAhpF455frdcJbImAqOQFx
+u7dmaEQ+HPW02MX2ZLr8R3sPRnFV5yERy6BkV8DmD7zomg4Jrd17PsgX5UBIt62K
+A8+HUp/0kcU9TCCHGY/V08oJ3XyFv1fp8HxbWXVRGk966n/b7DcHYg9ryV5ZYgWa
+85j4AofZZAeAeIesR2KwZL4=
+-----END PRIVATE KEY-----
diff --git a/test/org/apache/tomcat/util/net/ocsp/trust-password
b/test/org/apache/tomcat/util/net/ocsp/trust-password
new file mode 100644
index 0000000000..1d40192aeb
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/ocsp/trust-password
@@ -0,0 +1 @@
+changeit
diff --git a/test/org/apache/tomcat/util/net/ocsp/trustStore.p12
b/test/org/apache/tomcat/util/net/ocsp/trustStore.p12
new file mode 100644
index 0000000000..5a21631da1
Binary files /dev/null and
b/test/org/apache/tomcat/util/net/ocsp/trustStore.p12 differ
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]