Skip to content

Commit

Permalink
8179502: Enhance OCSP, CRL and Certificate Fetch Timeouts
Browse files Browse the repository at this point in the history
Reviewed-by: yan
Backport-of: 2836c34
  • Loading branch information
Alexey Bakhtin committed Jan 17, 2025
1 parent df6014e commit 25c7a7b
Show file tree
Hide file tree
Showing 7 changed files with 885 additions and 116 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -162,6 +162,68 @@ public Properties run() {
}
}

/**
* Convenience method for fetching System property values that are timeouts.
* Accepted timeout values may be purely numeric, a numeric value
* followed by "s" (both interpreted as seconds), or a numeric value
* followed by "ms" (interpreted as milliseconds).
*
* @param prop the name of the System property
* @param def a default value (in milliseconds)
* @param dbg a Debug object, if null no debug messages will be sent
*
* @return an integer value corresponding to the timeout value in the System
* property in milliseconds. If the property value is empty, negative,
* or contains non-numeric characters (besides a trailing "s" or "ms")
* then the default value will be returned. If a negative value for
* the "def" parameter is supplied, zero will be returned if the
* property's value does not conform to the allowed syntax.
*/
public static int privilegedGetTimeoutProp(String prop, int def, Debug dbg) {
if (def < 0) {
def = 0;
}

String rawPropVal = privilegedGetProperty(prop, "").trim();
if (rawPropVal.length() == 0) {
return def;
}

// Determine if "ms" or just "s" is on the end of the string.
// We may do a little surgery on the value so we'll retain
// the original value in rawPropVal for debug messages.
boolean isMillis = false;
String propVal = rawPropVal;
if (rawPropVal.toLowerCase().endsWith("ms")) {
propVal = rawPropVal.substring(0, rawPropVal.length() - 2);
isMillis = true;
} else if (rawPropVal.toLowerCase().endsWith("s")) {
propVal = rawPropVal.substring(0, rawPropVal.length() - 1);
}

// Next check to make sure the string is built only from digits
if (propVal.matches("^\\d+$")) {
try {
int timeout = Integer.parseInt(propVal);
return isMillis ? timeout : timeout * 1000;
} catch (NumberFormatException nfe) {
if (dbg != null) {
dbg.println("Warning: Unexpected " + nfe +
" for timeout value " + rawPropVal +
". Using default value of " + def + " msec.");
}
return def;
}
} else {
if (dbg != null) {
dbg.println("Warning: Incorrect syntax for timeout value " +
rawPropVal + ". Using default value of " + def +
" msec.");
}
return def;
}
}

/**
* Convenience method for fetching System property values that are booleans.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@

import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.net.*;
import java.security.cert.CertificateException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertPathValidatorException.BasicReason;
Expand Down Expand Up @@ -74,11 +71,20 @@ public final class OCSP {
private static final int DEFAULT_CONNECT_TIMEOUT = 15000;

/**
* Integer value indicating the timeout length, in seconds, to be
* used for the OCSP check. A timeout of zero is interpreted as
* an infinite timeout.
* Integer value indicating the timeout length, in milliseconds, to be
* used for establishing a connection to an OCSP responder. A timeout of
* zero is interpreted as an infinite timeout.
*/
private static final int CONNECT_TIMEOUT = initializeTimeout();
private static final int CONNECT_TIMEOUT = initializeTimeout(
"com.sun.security.ocsp.timeout", DEFAULT_CONNECT_TIMEOUT);

/**
* Integer value indicating the timeout length, in milliseconds, to be
* used for reading an OCSP response from the responder. A timeout of
* zero is interpreted as an infinite timeout.
*/
private static final int READ_TIMEOUT = initializeTimeout(
"com.sun.security.ocsp.readtimeout", CONNECT_TIMEOUT);

/**
* Boolean value indicating whether OCSP client can use GET for OCSP
Expand Down Expand Up @@ -107,16 +113,13 @@ public final class OCSP {
* system property. If the property has not been set, or if its
* value is negative, set the timeout length to the default.
*/
private static int initializeTimeout() {
@SuppressWarnings("removal")
Integer tmp = java.security.AccessController.doPrivileged(
new GetIntegerAction("com.sun.security.ocsp.timeout"));
if (tmp == null || tmp < 0) {
return DEFAULT_CONNECT_TIMEOUT;
private static int initializeTimeout(String prop, int def) {
int timeoutVal =
GetPropertyAction.privilegedGetTimeoutProp(prop, def, debug);
if (debug != null) {
debug.println(prop + " set to " + timeoutVal + " milliseconds");
}
// Convert to milliseconds, as the system property will be
// specified in seconds
return tmp * 1000;
return timeoutVal;
}

private static boolean initializeBoolean(String prop, boolean def) {
Expand Down Expand Up @@ -277,16 +280,18 @@ public static byte[] getOCSPBytes(List<CertId> certIds, URI responderURI,
Base64.getEncoder().encodeToString(bytes), "UTF-8"));

if (USE_GET && encodedGetReq.length() <= 255) {
url = new URL(encodedGetReq.toString());
url = new URI(encodedGetReq.toString()).toURL();
con = (HttpURLConnection)url.openConnection();
con.setConnectTimeout(CONNECT_TIMEOUT);
con.setReadTimeout(READ_TIMEOUT);
con.setDoOutput(true);
con.setDoInput(true);
con.setRequestMethod("GET");
} else {
url = responderURI.toURL();
con = (HttpURLConnection)url.openConnection();
con.setConnectTimeout(CONNECT_TIMEOUT);
con.setReadTimeout(CONNECT_TIMEOUT);
con.setReadTimeout(READ_TIMEOUT);
con.setDoOutput(true);
con.setDoInput(true);
con.setRequestMethod("POST");
Expand Down Expand Up @@ -316,6 +321,8 @@ public static byte[] getOCSPBytes(List<CertId> certIds, URI responderURI,
return (contentLength == -1) ? con.getInputStream().readAllBytes() :
IOUtils.readExactlyNBytes(con.getInputStream(),
contentLength);
} catch (URISyntaxException urise) {
throw new IOException(urise);
} finally {
if (con != null) {
con.disconnect();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2006, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -50,7 +50,8 @@
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import sun.security.action.GetIntegerAction;

import sun.security.action.GetPropertyAction;
import sun.security.x509.AccessDescription;
import sun.security.x509.GeneralNameInterface;
import sun.security.x509.URIName;
Expand Down Expand Up @@ -127,8 +128,12 @@ class URICertStore extends CertStoreSpi {
// allowed when downloading CRLs
private static final int DEFAULT_CRL_READ_TIMEOUT = 15000;

// Default connect and read timeouts for CA certificate fetching (15 sec)
private static final int DEFAULT_CACERT_CONNECT_TIMEOUT = 15000;
private static final int DEFAULT_CACERT_READ_TIMEOUT = 15000;

/**
* Integer value indicating the connect timeout, in seconds, to be
* Integer value indicating the connect timeout, in milliseconds, to be
* used for the CRL download. A timeout of zero is interpreted as
* an infinite timeout.
*/
Expand All @@ -137,30 +142,44 @@ class URICertStore extends CertStoreSpi {
DEFAULT_CRL_CONNECT_TIMEOUT);

/**
* Integer value indicating the read timeout, in seconds, to be
* Integer value indicating the read timeout, in milliseconds, to be
* used for the CRL download. A timeout of zero is interpreted as
* an infinite timeout.
*/
private static final int CRL_READ_TIMEOUT =
initializeTimeout("com.sun.security.crl.readtimeout",
DEFAULT_CRL_READ_TIMEOUT);

/**
* Integer value indicating the connect timeout, in milliseconds, to be
* used for the CA certificate download. A timeout of zero is interpreted
* as an infinite timeout.
*/
private static final int CACERT_CONNECT_TIMEOUT =
initializeTimeout("com.sun.security.cert.timeout",
DEFAULT_CACERT_CONNECT_TIMEOUT);

/**
* Integer value indicating the read timeout, in milliseconds, to be
* used for the CA certificate download. A timeout of zero is interpreted
* as an infinite timeout.
*/
private static final int CACERT_READ_TIMEOUT =
initializeTimeout("com.sun.security.cert.readtimeout",
DEFAULT_CACERT_READ_TIMEOUT);

/**
* Initialize the timeout length by getting the specified CRL timeout
* system property. If the property has not been set, or if its
* value is negative, set the timeout length to the specified default.
*/
private static int initializeTimeout(String prop, int def) {
Integer tmp = GetIntegerAction.privilegedGetProperty(prop);
if (tmp == null || tmp < 0) {
return def;
}
int timeoutVal =
GetPropertyAction.privilegedGetTimeoutProp(prop, def, debug);
if (debug != null) {
debug.println(prop + " set to " + tmp + " seconds");
debug.println(prop + " set to " + timeoutVal + " milliseconds");
}
// Convert to milliseconds, as the system property will be
// specified in seconds
return tmp * 1000;
return timeoutVal;
}

/**
Expand Down Expand Up @@ -276,6 +295,8 @@ static CertStore getInstance(AccessDescription ad) {
connection.setIfModifiedSince(lastModified);
}
long oldLastModified = lastModified;
connection.setConnectTimeout(CACERT_CONNECT_TIMEOUT);
connection.setReadTimeout(CACERT_READ_TIMEOUT);
try (InputStream in = connection.getInputStream()) {
lastModified = connection.getLastModified();
if (oldLastModified != 0) {
Expand Down
Loading

0 comments on commit 25c7a7b

Please sign in to comment.