Skip to content

Commit 4c1f613

Browse files
committed
Replace OpenSSL OCSP responder with an in-process BouncyCastle solution
1 parent 59acafb commit 4c1f613

File tree

11 files changed

+385
-92
lines changed

11 files changed

+385
-92
lines changed

build.properties.default

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,3 +349,23 @@ derby-shared.jar=${derby.home}/derby-shared-${derby.version}.jar
349349
derby-shared.loc=${base-maven.loc}/org/apache/derby/derbyshared/${derby.version}/derbyshared-${derby.version}.jar
350350
derby-tools.jar=${derby.home}/derby-tools-${derby.version}.jar
351351
derby-tools.loc=${base-maven.loc}/org/apache/derby/derbytools/${derby.version}/derbytools-${derby.version}.jar
352+
353+
# ----- Bouncy Castle, used by unit tests ----
354+
bouncycastle.version=1.83
355+
bouncycastle-provider.checksum.enabled=true
356+
bouncycastle-provider.checksum.algorithm=SHA-512
357+
bouncycastle-provider.checksum.value=e51cc843ca130ad4a15ff667360063bbb583af3f22e14193840a734da6665b470ba1855ce975f88a94ddd6419aa020d3a9966980bc1deb7514f92a7215d6e229
358+
bouncycastle-pkix.checksum.enabled=true
359+
bouncycastle-pkix.checksum.algorithm=SHA-512
360+
bouncycastle-pkix.checksum.value=9c67d990a56a5c448f9bb9edbb8b99dc15971e16de7e6f10c3eab129a1389eef4882c5a5d910ac4ddc27d44f7bc9fa4054c7f56dc031154c131bccc50ebd67b9
361+
bouncycastle-util.checksum.enabled=true
362+
bouncycastle-util.checksum.algorithm=SHA-512
363+
bouncycastle-util.checksum.value=e19831d4afc0a709fd57694f33bfe3a8e881cba287c34fb076a44ef436e56e6bdf49299ca9525028a4de9bf5fadeaa254654d0e527855f1f5659c5a7be538576
364+
365+
bouncycastle.home=${base.path}/bouncycastle-${bouncycastle.version}
366+
bouncycastle-provider.jar=${bouncycastle.home}/bouncycastle-provider-${bouncycastle.version}.jar
367+
bouncycastle-provider.loc=${base-maven.loc}/org/bouncycastle/bcprov-jdk18on/${bouncycastle.version}/bcprov-jdk18on-${bouncycastle.version}.jar
368+
bouncycastle-pkix.jar=${bouncycastle.home}/bouncycastle-pkix-${bouncycastle.version}.jar
369+
bouncycastle-pkix.loc=${base-maven.loc}/org/bouncycastle/bcpkix-jdk18on/${bouncycastle.version}/bcpkix-jdk18on-${bouncycastle.version}.jar
370+
bouncycastle-util.jar=${bouncycastle.home}/bouncycastle-util-${bouncycastle.version}.jar
371+
bouncycastle-util.loc=${base-maven.loc}/org/bouncycastle/bcutil-jdk18on/${bouncycastle.version}/bcutil-jdk18on-${bouncycastle.version}.jar

build.xml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,9 @@
257257
<pathelement location="${derby.jar}"/>
258258
<pathelement location="${derby-shared.jar}"/>
259259
<pathelement location="${derby-tools.jar}"/>
260+
<pathelement location="${bouncycastle-provider.jar}"/>
261+
<pathelement location="${bouncycastle-pkix.jar}"/>
262+
<pathelement location="${bouncycastle-util.jar}"/>
260263
<path refid="compile.classpath" />
261264
<path refid="tomcat.classpath" />
262265
</path>
@@ -273,6 +276,9 @@
273276
<filter token="MIGRATION_JAR" value="${migration-lib.jar}"/>
274277
<filter token="UNBOUNDID_JAR" value="${unboundid.jar}"/>
275278
<filter token="JUNIT_JAR" value="${junit.jar}"/>
279+
<filter token="BC_PROVIDER_JAR" value="${bouncycastle-provider.jar}"/>
280+
<filter token="BC_PKIX_JAR" value="${bouncycastle-pkix.jar}"/>
281+
<filter token="BC_UTIL_JAR" value="${bouncycastle-util.jar}"/>
276282
<filter token="OUTPUT_DIR" value="${tomcat.output}"/>
277283
</filterset>
278284

@@ -3753,6 +3759,33 @@ Configured for ${release.asfusername} to release Tomcat ${version.major}.${versi
37533759
<param name="checksum.value" value="${derby-tools.checksum.value}"/>
37543760
</antcall>
37553761

3762+
<antcall target="downloadfile">
3763+
<param name="sourcefile" value="${bouncycastle-provider.loc}"/>
3764+
<param name="destfile" value="${bouncycastle-provider.jar}"/>
3765+
<param name="destdir" value="${bouncycastle.home}"/>
3766+
<param name="checksum.enabled" value="${bouncycastle-provider.checksum.enabled}"/>
3767+
<param name="checksum.algorithm" value="${bouncycastle-provider.checksum.algorithm}"/>
3768+
<param name="checksum.value" value="${bouncycastle-provider.checksum.value}"/>
3769+
</antcall>
3770+
3771+
<antcall target="downloadfile">
3772+
<param name="sourcefile" value="${bouncycastle-pkix.loc}"/>
3773+
<param name="destfile" value="${bouncycastle-pkix.jar}"/>
3774+
<param name="destdir" value="${bouncycastle.home}"/>
3775+
<param name="checksum.enabled" value="${bouncycastle-pkix.checksum.enabled}"/>
3776+
<param name="checksum.algorithm" value="${bouncycastle-pkix.checksum.algorithm}"/>
3777+
<param name="checksum.value" value="${bouncycastle-pkix.checksum.value}"/>
3778+
</antcall>
3779+
3780+
<antcall target="downloadfile">
3781+
<param name="sourcefile" value="${bouncycastle-util.loc}"/>
3782+
<param name="destfile" value="${bouncycastle-util.jar}"/>
3783+
<param name="destdir" value="${bouncycastle.home}"/>
3784+
<param name="checksum.enabled" value="${bouncycastle-util.checksum.enabled}"/>
3785+
<param name="checksum.algorithm" value="${bouncycastle-util.checksum.algorithm}"/>
3786+
<param name="checksum.value" value="${bouncycastle-util.checksum.value}"/>
3787+
</antcall>
3788+
37563789
</target>
37573790

37583791
<target name="download-jacoco"

res/ide-support/eclipse/eclipse.classpath

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,8 @@
3838
<classpathentry kind="lib" path="@BND_JAR@"/>
3939
<classpathentry kind="lib" path="@MIGRATION_JAR@"/>
4040
<classpathentry kind="lib" path="@UNBOUNDID_JAR@"/>
41+
<classpathentry kind="lib" path="@BC_PROVIDER_JAR@"/>
42+
<classpathentry kind="lib" path="@BC_PKIX_JAR@"/>
43+
<classpathentry kind="lib" path="@BC_UTIL_JAR@"/>
4144
<classpathentry kind="output" path=".settings/output"/>
4245
</classpath>

res/ide-support/idea/tomcat.iml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,33 @@
126126
<SOURCES />
127127
</library>
128128
</orderEntry>
129+
<orderEntry type="module-library">
130+
<library>
131+
<CLASSES>
132+
<root url="jar://@BC_PROVIDER_JAR@!/" />
133+
</CLASSES>
134+
<JAVADOC />
135+
<SOURCES />
136+
</library>
137+
</orderEntry>
138+
<orderEntry type="module-library">
139+
<library>
140+
<CLASSES>
141+
<root url="jar://@BC_PKIX_JAR@!/" />
142+
</CLASSES>
143+
<JAVADOC />
144+
<SOURCES />
145+
</library>
146+
</orderEntry>
147+
<orderEntry type="module-library">
148+
<library>
149+
<CLASSES>
150+
<root url="jar://@BC_UTIL_JAR@!/" />
151+
</CLASSES>
152+
<JAVADOC />
153+
<SOURCES />
154+
</library>
155+
</orderEntry>
129156

130157
<orderEntry type="module-library">
131158
<library>

res/ide-support/netbeans/nb-tomcat-build.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ nb-test.io-method=org.apache.coyote.http11.Http11NioProtocol
3737
# it is not possible to retrieve the classpaths from the build to
3838
# use in the NetBeans targets, so they must be explicitly declared
3939

40-
nb-test.classpath=${test.classes}:${tomcat.build}/webapps/examples/WEB-INF/classes:@JUNIT_JAR@:@EASYMOCK_JAR@:@BYTEBUDDY_JAR@:@OBJENESIS_JAR@:@HAMCREST_JAR@:@ECJ_JAR@:@UNBOUNDID_JAR@:${tomcat.classes}
40+
nb-test.classpath=${test.classes}:${tomcat.build}/webapps/examples/WEB-INF/classes:@JUNIT_JAR@:@EASYMOCK_JAR@:@BYTEBUDDY_JAR@:@OBJENESIS_JAR@:@HAMCREST_JAR@:@ECJ_JAR@:@UNBOUNDID_JAR@:@BC_PROVIDER_JAR@:@BC_PKIX_JAR@:@BC_UTIL_JAR@:${tomcat.classes}
4141

4242
# Extra properties used by the Tomcat project additional NetBeans targets.
4343

res/ide-support/netbeans/project.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@
189189
<compilation-unit>
190190
<package-root>test</package-root>
191191
<unit-tests/>
192-
<classpath mode="compile">output/classes:output/testclasses:output/build/webapps/examples/WEB-INF/classes:@JUNIT_JAR@:@EASYMOCK_JAR@:@BYTEBUDDY_JAR@:@OBJENESIS_JAR@:@HAMCREST_JAR@:@UNBOUNDID_JAR@</classpath>
192+
<classpath mode="compile">output/classes:output/testclasses:output/build/webapps/examples/WEB-INF/classes:@JUNIT_JAR@:@EASYMOCK_JAR@:@BYTEBUDDY_JAR@:@OBJENESIS_JAR@:@HAMCREST_JAR@:@UNBOUNDID_JAR@:@BC_PROVIDER_JAR@:@BC_PKIX_JAR@:@BC_UTIL_JAR@</classpath>
193193
<source-level>@BUILD_JAVA_VERSION@</source-level>
194194
</compilation-unit>
195195
</java-data>

test/org/apache/tomcat/security/TestSecurity2017Ocsp.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
*/
1717
package org.apache.tomcat.security;
1818

19-
import java.io.IOException;
20-
2119
import javax.net.ssl.SSLHandshakeException;
2220

2321
import jakarta.servlet.http.HttpServletResponse;
@@ -50,7 +48,7 @@ public static void startOcspResponder() {
5048
ocspResponder = new TesterOcspResponder();
5149
try {
5250
ocspResponder.start();
53-
} catch (IOException ioe) {
51+
} catch (Exception e) {
5452
ocspResponder = null;
5553
}
5654
}

test/org/apache/tomcat/util/net/ocsp/TestOcspEnabled.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
*/
1717
package org.apache.tomcat.util.net.ocsp;
1818

19-
import java.io.IOException;
2019
import java.util.ArrayList;
2120
import java.util.Collection;
2221
import java.util.List;
@@ -43,8 +42,8 @@ public static void startOcspResponder() {
4342
ocspResponder = new TesterOcspResponder();
4443
try {
4544
ocspResponder.start();
46-
} catch (IOException ioe) {
47-
ocspResponder = null;
45+
} catch (Exception e) {
46+
e.printStackTrace();
4847
}
4948
}
5049

test/org/apache/tomcat/util/net/ocsp/TesterOcspResponder.java

Lines changed: 55 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -16,108 +16,79 @@
1616
*/
1717
package org.apache.tomcat.util.net.ocsp;
1818

19-
import java.io.IOException;
20-
import java.io.PrintStream;
21-
import java.io.Reader;
22-
import java.util.ArrayList;
23-
import java.util.Arrays;
24-
import java.util.List;
25-
import java.util.Map;
26-
import java.util.concurrent.TimeUnit;
19+
import java.io.File;
20+
import java.nio.file.FileSystems;
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
2723

28-
import org.junit.Assert;
24+
import org.apache.catalina.Context;
25+
import org.apache.catalina.LifecycleException;
26+
import org.apache.catalina.connector.Connector;
27+
import org.apache.catalina.startup.ExpandWar;
28+
import org.apache.catalina.startup.Tomcat;
2929

30-
import org.apache.tomcat.util.net.TesterSupport;
31-
32-
/*
33-
* The OpenSSL ocsp tool is great, but it does generate a lot of output. That output needs to be swallowed, else the
34-
* process will freeze when the output buffers (stdout and stderr) are full.
35-
*
36-
* There is a command line option to redirect stdout (which could be redirected to /dev/null), but there is no option to
37-
* redirect stderr. Therefore, this class uses a couple of dedicated threads to read stdout and stderr. By default, the
38-
* output is ignored, but it can be dumped to Java's stdout/stderr if required for debugging purposes.
39-
*/
4030
public class TesterOcspResponder {
4131

42-
private static List<String> ocspArgs = Arrays.asList("ocsp", "-port", "8888", "-text", "-index",
43-
TesterSupport.DB_INDEX, "-CA", TesterSupport.CA_CERT_PEM, "-rkey", TesterSupport.OCSP_RESPONDER_RSA_KEY,
44-
"-rsigner", TesterSupport.OCSP_RESPONDER_RSA_CERT, "-nmin", "60");
32+
private File catalinaBase;
33+
private Tomcat ocspResponder;
4534

46-
private Process p;
35+
public void start() throws Exception {
36+
ocspResponder = new Tomcat();
4737

48-
public void start() throws IOException {
49-
if (p != null) {
50-
throw new IllegalStateException("Already started");
51-
}
38+
Connector connector = new Connector("HTTP/1.1");
39+
connector.setPort(8888);
40+
connector.setThrowOnFailure(true);
41+
connector.setEncodedSolidusHandling("passthrough");
42+
ocspResponder.getService().addConnector(connector);
5243

53-
String openSSLPath = System.getProperty("tomcat.test.openssl.path");
54-
String openSSLLibPath = null;
55-
if (openSSLPath == null || openSSLPath.length() == 0) {
56-
openSSLPath = "openssl";
57-
} else {
58-
// Explicit OpenSSL path may also need explicit lib path
59-
// (e.g. Gump needs this)
60-
openSSLLibPath = openSSLPath.substring(0, openSSLPath.lastIndexOf('/'));
61-
openSSLLibPath = openSSLLibPath + "/../:" + openSSLLibPath + "/../lib:" + openSSLLibPath + "/../lib64";
44+
// Create a temporary directory structure for the OCSP responder
45+
File tempBase = new File(System.getProperty("tomcat.test.temp", "output/tmp"));
46+
if (!tempBase.mkdirs() && !tempBase.isDirectory()) {
47+
throw new IllegalStateException("Unable to create tempBase");
6248
}
63-
List<String> cmd = new ArrayList<>();
64-
cmd.add(openSSLPath);
65-
cmd.addAll(ocspArgs);
6649

67-
ProcessBuilder pb = new ProcessBuilder(cmd.toArray(new String[0]));
68-
69-
if (openSSLLibPath != null) {
70-
Map<String,String> env = pb.environment();
71-
String libraryPath = env.get("LD_LIBRARY_PATH");
72-
if (libraryPath == null) {
73-
libraryPath = openSSLLibPath;
74-
} else {
75-
libraryPath = libraryPath + ":" + openSSLLibPath;
76-
}
77-
env.put("LD_LIBRARY_PATH", libraryPath);
50+
// Create and configure CATALINA_BASE
51+
Path tempBasePath = FileSystems.getDefault().getPath(tempBase.getAbsolutePath());
52+
catalinaBase = Files.createTempDirectory(tempBasePath, "ocsp").toFile();
53+
if (!catalinaBase.isDirectory()) {
54+
throw new IllegalStateException("Unable to create CATALINA_BASE for OCSP responder");
7855
}
56+
ocspResponder.setBaseDir(catalinaBase.getAbsolutePath());
7957

80-
p = pb.start();
58+
// Create and configure a web apps directory
59+
File appBase = new File(catalinaBase, "webapps");
60+
if (!appBase.exists() && !appBase.mkdir()) {
61+
throw new IllegalStateException("Unable to create appBase for OCSP responder");
62+
}
63+
ocspResponder.getHost().setAppBase(appBase.getAbsolutePath());
8164

82-
redirect(p.inputReader(), System.out, true);
83-
redirect(p.errorReader(), System.err, true);
65+
// Configure the ROOT web application
66+
// No file system docBase required
67+
Context ctx = ocspResponder.addContext("", null);
68+
Tomcat.addServlet(ctx, "responder", new TesterOcspResponderServlet());
69+
ctx.addServletMappingDecoded("/", "responder");
8470

85-
Assert.assertTrue(p.isAlive());
71+
// Start the responder
72+
ocspResponder.start();
8673
}
8774

8875
public void stop() {
89-
if (p == null) {
90-
throw new IllegalStateException("Not started");
91-
}
92-
p.destroy();
93-
94-
try {
95-
if (!p.waitFor(30, TimeUnit.SECONDS)) {
96-
throw new IllegalStateException("Failed to stop");
76+
if (ocspResponder != null) {
77+
try {
78+
ocspResponder.stop();
79+
} catch (LifecycleException e) {
80+
// Good enough for testing
81+
e.printStackTrace();
9782
}
98-
} catch (InterruptedException e) {
99-
throw new IllegalStateException("Interrupted while waiting to stop", e);
100-
}
101-
}
102-
103-
104-
private void redirect(final Reader r, final PrintStream os, final boolean swallow) {
105-
/*
106-
* InputStream will close when process ends. Thread will exit once stream closes.
107-
*/
108-
new Thread( () -> {
109-
char[] cbuf = new char[1024];
11083
try {
111-
int read;
112-
while ((read = r.read(cbuf)) > 0) {
113-
if (!swallow) {
114-
os.print(new String(cbuf, 0, read));
115-
}
116-
}
117-
} catch (IOException ignore) {
118-
// Ignore
84+
ocspResponder.destroy();
85+
} catch (LifecycleException e) {
86+
// Good enough for testing
87+
e.printStackTrace();
11988
}
120-
121-
}).start();
89+
}
90+
if (catalinaBase != null) {
91+
ExpandWar.deleteDir(catalinaBase);
92+
}
12293
}
12394
}

0 commit comments

Comments
 (0)