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]


Reply via email to