This is an automated email from the ASF dual-hosted git repository.
markt 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 e76e9eaa24 Extend OCSP checks for OpenSSL to align with JSSE
e76e9eaa24 is described below
commit e76e9eaa246f30debd603726d9147a8a8cb5171c
Author: Mark Thomas <[email protected]>
AuthorDate: Thu Dec 4 17:18:13 2025 +0000
Extend OCSP checks for OpenSSL to align with JSSE
---
.../util/net/openssl/panama/OpenSSLContext.java | 15 +++++-
.../util/net/openssl/panama/OpenSSLEngine.java | 53 ++++++++++++++++++----
.../tomcat/util/net/ocsp/TestOcspIntegration.java | 7 +++
webapps/docs/changelog.xml | 3 +-
4 files changed, 67 insertions(+), 11 deletions(-)
diff --git a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
index 1abac2d005..7b732432ce 100644
--- a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
+++ b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
@@ -123,6 +123,8 @@ public class OpenSSLContext implements
org.apache.tomcat.util.net.SSLContext {
private boolean initialized = false;
private boolean noOcspCheck = false;
+ private int ocspTimeout = 15000;
+ private int ocspVerifyFlags = 0;
private X509TrustManager x509TrustManager;
private final ContextState state;
@@ -355,6 +357,10 @@ public class OpenSSLContext implements
org.apache.tomcat.util.net.SSLContext {
try (var localArena = Arena.ofConfined()) {
if (name.equals(OpenSSLConfCmd.NO_OCSP_CHECK)) {
ok = true;
+ } else if (name.equals("OCSP_TIMEOUT")) {
+ ok = true;
+ } else if (name.equals("OCSP_VERIFY_FLAGS")) {
+ ok = true;
} else {
int code = SSL_CONF_cmd_value_type(state.confCtx,
localArena.allocateFrom(name));
ok = true;
@@ -425,6 +431,12 @@ public class OpenSSLContext implements
org.apache.tomcat.util.net.SSLContext {
if (name.equals(OpenSSLConfCmd.NO_OCSP_CHECK)) {
noOcspCheck = Boolean.parseBoolean(value);
rc = 1;
+ } else if (name.equals("OCSP_TIMEOUT")) {
+ ocspTimeout = Integer.parseInt(value);
+ rc = 1;
+ } else if (name.equals("OCSP_VERIFY_FLAGS")) {
+ ocspVerifyFlags = Integer.parseInt(value);
+ rc = 1;
} else {
rc = SSL_CONF_cmd(state.confCtx,
localArena.allocateFrom(name), localArena.allocateFrom(value));
String errorMessage = OpenSSLLibrary.getLastError();
@@ -1349,7 +1361,8 @@ public class OpenSSLContext implements
org.apache.tomcat.util.net.SSLContext {
public SSLEngine createSSLEngine() {
return new OpenSSLEngine(cleaner, state.sslCtx, defaultProtocol,
false, sessionContext, alpn, initialized,
sslHostConfig.getCertificateVerificationDepth(),
- sslHostConfig.getCertificateVerification() ==
CertificateVerification.OPTIONAL_NO_CA, noOcspCheck);
+ sslHostConfig.getCertificateVerification() ==
CertificateVerification.OPTIONAL_NO_CA,
+ noOcspCheck, ocspTimeout, ocspVerifyFlags);
}
@Override
diff --git a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java
b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java
index 395e72ebd4..0e38a0e543 100644
--- a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java
+++ b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java
@@ -94,6 +94,8 @@ public final class OpenSSLEngine extends SSLEngine implements
SSLUtil.ProtocolIn
private static final int MAX_PLAINTEXT_LENGTH = 16 * 1024; // 2^14
private static final int MAX_COMPRESSED_LENGTH = MAX_PLAINTEXT_LENGTH +
1024;
private static final int MAX_CIPHERTEXT_LENGTH = MAX_COMPRESSED_LENGTH +
1024;
+ // 15 minutes aligns with JSSE
+ private static final int OCSP_MAX_SKEW = 60 * 15;
// Header (5) + Data (2^14) + Compression (1024) + Encryption (1024) + MAC
(20) + Padding (256)
private static final int MAX_ENCRYPTED_PACKET_LENGTH =
MAX_CIPHERTEXT_LENGTH + 5 + 20 + 256;
@@ -174,10 +176,13 @@ public final class OpenSSLEngine extends SSLEngine
implements SSLUtil.ProtocolIn
* verification from the
{@code SSL_CTX} {@code sslCtx}
* @param certificateVerificationDepth Certificate verification
depth
* @param certificateVerificationOptionalNoCA Skip CA verification in
optional mode
+ * @param noOcspCheck Enable OCSP if true
+ * @param ocspTimeout Timout in ms to use for OCSP
requests
+ * @param ocspVerifyFlags Verification flags for OCSP
*/
OpenSSLEngine(Cleaner cleaner, MemorySegment sslCtx, String
fallbackApplicationProtocol, boolean clientMode,
OpenSSLSessionContext sessionContext, boolean alpn, boolean
initialized, int certificateVerificationDepth,
- boolean certificateVerificationOptionalNoCA, boolean noOcspCheck) {
+ boolean certificateVerificationOptionalNoCA, boolean noOcspCheck,
int ocspTimeout, int ocspVerifyFlags) {
if (sslCtx == null) {
throw new
IllegalArgumentException(sm.getString("engine.noSSLContext"));
}
@@ -200,7 +205,7 @@ public final class OpenSSLEngine extends SSLEngine
implements SSLUtil.ProtocolIn
var internalBIO = internalBIOPointer.get(ValueLayout.ADDRESS, 0);
var networkBIO = networkBIOPointer.get(ValueLayout.ADDRESS, 0);
SSL_set_bio(ssl, internalBIO, internalBIO);
- state = new EngineState(ssl, networkBIO,
certificateVerificationDepth, noOcspCheck);
+ state = new EngineState(ssl, networkBIO,
certificateVerificationDepth, noOcspCheck, ocspTimeout, ocspVerifyFlags);
}
this.fallbackApplicationProtocol = fallbackApplicationProtocol;
this.clientMode = clientMode;
@@ -1167,7 +1172,7 @@ public final class OpenSSLEngine extends SSLEngine
implements SSLUtil.ProtocolIn
ok = 0;
}
} else {
- int ocspResponse = processOCSP(x509ctx);
+ int ocspResponse = processOCSP(state, x509ctx);
if (ocspResponse == V_OCSP_CERTSTATUS_REVOKED()) {
ok = 0;
errnum = X509_STORE_CTX_get_error(x509ctx);
@@ -1189,7 +1194,7 @@ public final class OpenSSLEngine extends SSLEngine
implements SSLUtil.ProtocolIn
}
}
- private static int processOCSP(MemorySegment /* X509_STORE_CTX */ x509ctx)
{
+ private static int processOCSP(EngineState state, MemorySegment /*
X509_STORE_CTX */ x509ctx) {
int ocspResponse = V_OCSP_CERTSTATUS_UNKNOWN();
MemorySegment x509 = X509_STORE_CTX_get_current_cert(x509ctx);
if (!MemorySegment.NULL.equals(x509)) {
@@ -1237,7 +1242,7 @@ public final class OpenSSLEngine extends SSLEngine
implements SSLUtil.ProtocolIn
for (String urlString : urls) {
try {
URL url = (new
URI(urlString)).toURL();
- ocspResponse =
processOCSPRequest(url, issuer, x509, x509ctx, localArena);
+ ocspResponse =
processOCSPRequest(state, url, issuer, x509, x509ctx, localArena);
if (log.isDebugEnabled()) {
log.debug(sm.getString("engine.ocspResponse", urlString,
Integer.toString(ocspResponse)));
@@ -1290,7 +1295,7 @@ public final class OpenSSLEngine extends SSLEngine
implements SSLUtil.ProtocolIn
}
}
- private static int processOCSPRequest(URL url, MemorySegment issuer,
MemorySegment x509,
+ private static int processOCSPRequest(EngineState state, URL url,
MemorySegment issuer, MemorySegment x509,
MemorySegment /* X509_STORE_CTX */ x509ctx, Arena localArena) {
if (openssl_h_Compatibility.BORINGSSL) {
return V_OCSP_CERTSTATUS_UNKNOWN();
@@ -1315,6 +1320,7 @@ public final class OpenSSLEngine extends SSLEngine
implements SSLUtil.ProtocolIn
if (MemorySegment.NULL.equals(ocspOneReq)) {
return V_OCSP_CERTSTATUS_UNKNOWN();
}
+ OCSP_request_add1_nonce(ocspRequest, (char) 0, -1);
MemorySegment bufPointer =
localArena.allocateFrom(ValueLayout.ADDRESS, MemorySegment.NULL);
int requestLength = i2d_OCSP_REQUEST(ocspRequest, bufPointer);
if (requestLength <= 0) {
@@ -1328,6 +1334,8 @@ public final class OpenSSLEngine extends SSLEngine
implements SSLUtil.ProtocolIn
// Content-Length: ocspRequestData.length
byte[] ocspRequestData = buf.reinterpret(requestLength,
localArena, null).toArray(ValueLayout.JAVA_BYTE);
connection = (HttpURLConnection) url.openConnection();
+ connection.setConnectTimeout(state.ocspTimeout);
+ connection.setReadTimeout(state.ocspTimeout);
connection.setRequestMethod(Method.POST);
connection.setDoInput(true);
connection.setDoOutput(true);
@@ -1352,15 +1360,38 @@ public final class OpenSSLEngine extends SSLEngine
implements SSLUtil.ProtocolIn
if (!MemorySegment.NULL.equals(ocspResponse)) {
if (OCSP_response_status(ocspResponse) ==
OCSP_RESPONSE_STATUS_SUCCESSFUL()) {
basicResponse = OCSP_response_get1_basic(ocspResponse);
+ if (OCSP_check_nonce(ocspRequest, basicResponse) == 0) {
+ X509_STORE_CTX_set_error(x509ctx,
X509_V_ERR_OCSP_RESP_INVALID());
+ return V_OCSP_CERTSTATUS_UNKNOWN();
+ }
+ MemorySegment certStack =
OCSP_resp_get0_certs(basicResponse);
+ if (OCSP_basic_verify(basicResponse, certStack,
X509_STORE_CTX_get0_store(x509ctx), state.ocspVerifyFlags) <= 0) {
+ X509_STORE_CTX_set_error(x509ctx,
X509_V_ERR_OCSP_SIGNATURE_FAILURE());
+ return V_OCSP_CERTSTATUS_UNKNOWN();
+ }
certId = OCSP_cert_to_id(MemorySegment.NULL, x509, issuer);
if (MemorySegment.NULL.equals(certId)) {
+ X509_STORE_CTX_set_error(x509ctx,
X509_V_ERR_OCSP_RESP_INVALID());
return V_OCSP_CERTSTATUS_UNKNOWN();
}
// Find by serial number and get the matching response
MemorySegment singleResponse =
OCSP_resp_get0(basicResponse,
OCSP_resp_find(basicResponse, certId, -1));
- return OCSP_single_get0_status(singleResponse,
MemorySegment.NULL, MemorySegment.NULL,
- MemorySegment.NULL, MemorySegment.NULL);
+ MemorySegment thisUpdatePointer =
localArena.allocateFrom(ValueLayout.ADDRESS, MemorySegment.NULL);
+ MemorySegment nextUpdatePointer =
localArena.allocateFrom(ValueLayout.ADDRESS, MemorySegment.NULL);
+ int status = OCSP_single_get0_status(singleResponse,
MemorySegment.NULL, MemorySegment.NULL,
+ thisUpdatePointer, nextUpdatePointer);
+ if
(OCSP_check_validity(thisUpdatePointer.get(ValueLayout.ADDRESS, 0),
+ nextUpdatePointer.get(ValueLayout.ADDRESS, 0),
OCSP_MAX_SKEW, -1) <= 0) {
+ X509_STORE_CTX_set_error(x509ctx,
X509_V_ERR_OCSP_NOT_YET_VALID());
+ return V_OCSP_CERTSTATUS_UNKNOWN();
+ }
+ if
(OCSP_check_validity(thisUpdatePointer.get(ValueLayout.ADDRESS, 0),
+ nextUpdatePointer.get(ValueLayout.ADDRESS, 0),
OCSP_MAX_SKEW, OCSP_MAX_SKEW) <= 0) {
+ X509_STORE_CTX_set_error(x509ctx,
X509_V_ERR_OCSP_HAS_EXPIRED());
+ return V_OCSP_CERTSTATUS_UNKNOWN();
+ }
+ return status;
}
}
} catch (Exception e) {
@@ -1673,16 +1704,20 @@ public final class OpenSSLEngine extends SSLEngine
implements SSLUtil.ProtocolIn
private final MemorySegment networkBIO;
private final int certificateVerificationDepth;
private final boolean noOcspCheck;
+ private final int ocspTimeout;
+ private final int ocspVerifyFlags;
private PHAState phaState = PHAState.NONE;
private int certificateVerifyMode = 0;
private int handshakeCount = 0;
private EngineState(MemorySegment ssl, MemorySegment networkBIO, int
certificateVerificationDepth,
- boolean noOcspCheck) {
+ boolean noOcspCheck, int ocspTimeout, int ocspVerifyFlags) {
states.put(Long.valueOf(ssl.address()), this);
this.certificateVerificationDepth = certificateVerificationDepth;
this.noOcspCheck = noOcspCheck;
+ this.ocspTimeout = ocspTimeout;
+ this.ocspVerifyFlags = ocspVerifyFlags;
// Use another arena to avoid keeping a reference through segments
// This also allows making further accesses to the main pointers
safer
this.ssl = ssl.reinterpret(ValueLayout.ADDRESS.byteSize(),
stateArena, openssl_h::SSL_free);
diff --git a/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java
b/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java
index d4d6eebab1..93a1ed6c03 100644
--- a/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java
+++ b/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java
@@ -74,6 +74,8 @@ 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.OpenSSLStatus;
import com.sun.net.httpserver.Headers;
@@ -368,6 +370,11 @@ public class TestOcspIntegration extends TomcatBaseTest {
if (useOpenSSLTrust) {
sslHostConfig.setCaCertificateFile(certificateChainFile.getAbsolutePath());
+ // Need to use OpenSSLConf settings
+ OpenSSLConf conf = new OpenSSLConf();
+ sslHostConfig.setOpenSslConf(conf);
+ // Verification
+ conf.addCmd(new OpenSSLConfCmd("OCSP_VERIFY_FLAGS", "16"));
} else {
sslHostConfig.setTruststoreType("PKCS12");
sslHostConfig.setTruststoreFile(truststoreFile.getAbsolutePath());
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 639645cee5..17ab442447 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -171,7 +171,8 @@
<add>
Add OCSP support to JSSE based TLS connectors and make the use of OCSP
configurable per connector for both JSSE and OpenSSL based TLS
- implementations. (markt)
+ implementations and align the checks performed by OpenSSL with those
+ performed by JSSE. (markt)
</add>
</changelog>
</subsection>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]