Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README_JCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,21 @@ programatically for JCE provider customization:
| System Property | Default | To Enable | Description |
| --- | --- | --- | --- |
| wolfjce.debug | "false" | "true" | Enable wolfJCE debug logging |
| wolfjce.ioTimeout | UNSET | Integer (seconds) | I/O timeout for OCSP and CRL HTTP operations (0-3600) |

**wolfjce.ioTimeout** - sets the I/O timeout (in seconds) used by native wolfSSL
for HTTP-based OCSP lookups and CRL fetching. Wraps native `wolfIO_SetTimeout()`.
Requires native wolfSSL to be compiled with `HAVE_IO_TIMEOUT`. Valid values are
0 to 3600 inclusive (1 hour). A value of 0 disables the timeout (default
behavior). If the property is not set, no timeout is applied. This
property is read during `PKIXRevocationChecker.init()`, which occurs at
certificate path validation time. This means the property can be set or changed
after provider registration and will be picked up on the next validation.
Invalid values (non-numeric, negative, exceeding 3600) will cause revocation
checker initialization to fail with `CertPathValidatorException`. This property
replaces the Sun-specific `com.sun.security.ocsp.timeout` and
`com.sun.security.crl.timeout` properties (which use milliseconds) with a single
wolfJCE-specific property in seconds that applies to both OCSP and CRL operations.

### Algorithm Support:
---------
Expand Down
18 changes: 18 additions & 0 deletions jni/include/com_wolfssl_wolfcrypt_WolfCrypt.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions jni/jni_wolfcrypt.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
#include <wolfssl/wolfcrypt/coding.h>
#include <wolfssl/wolfcrypt/asn_public.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#include <wolfssl/ssl.h>
#include <wolfssl/wolfio.h>
#include <com_wolfssl_wolfcrypt_WolfCrypt.h>
#include <wolfcrypt_jni_error.h>

Expand Down Expand Up @@ -612,3 +614,30 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_wcPubKeyPemToD
#endif /* !NO_ASN && !WOLFSSL_NO_PEM && !NO_CODING) */
}

JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_IoTimeoutEnabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;

#ifdef HAVE_IO_TIMEOUT
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}

JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_nativeSetIOTimeout
(JNIEnv* env, jclass jcl, jint timeoutSec)
{
(void)jcl;

#ifdef HAVE_IO_TIMEOUT
wolfIO_SetTimeout(timeoutSec);
(void)env;
#else
(void)timeoutSec;
throwNotCompiledInException(env);
#endif
}

Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ public class WolfCryptPKIXRevocationChecker extends PKIXRevocationChecker {
/* Trust anchors for determining if issuer is a trust anchor */
private Set<TrustAnchor> trustAnchors;

/* Last applied I/O timeout value from wolfjce.ioTimeout property.
* Used to skip redundant JNI calls when multiple checkers or
* repeated init() calls read the same property value. wolfIO_SetTimeout()
* sets a global value, so all checkers in the JVM share the same timeout.
* Integer.MIN_VALUE indicates no timeout has been applied yet. */
private static volatile int lastAppliedIOTimeout =
Integer.MIN_VALUE;

/**
* Create new WolfCryptPKIXRevocationChecker.
*/
Expand Down Expand Up @@ -147,6 +155,10 @@ public void init(boolean forward) throws CertPathValidatorException {
this.initialized = true;
this.softFailExceptions.clear();

/* Set wolfSSL I/O timeout for HTTP-based operations (OCSP lookups,
* CRL fetching) if 'wolfjce.ioTimeout' System property is set. */
setIOTimeoutFromProperty();

/* Verify we have OCSP support if needed */
if (!options.contains(Option.PREFER_CRLS)) {
if (!WolfCrypt.OcspEnabled()) {
Expand Down Expand Up @@ -591,6 +603,77 @@ public List<CertPathValidatorException> getSoftFailExceptions() {
return Collections.unmodifiableList(this.softFailExceptions);
}

/**
* Read and apply wolfjce.ioTimeout system property.
*
* Sets the native wolfSSL I/O timeout via wolfIO_SetTimeout()
* if the property is set and valid. If the property is set but
* contains an invalid value, throws CertPathValidatorException
* to fail revocation checker initialization.
*
* Note: The native timeout is a global (process-wide) setting
* shared by all threads and validations in the JVM. To reduce
* redundant JNI calls, the parsed value is compared against
* the last applied value and the native call is skipped if
* unchanged.
*
* @throws CertPathValidatorException if property value is
* invalid (not a number, negative, exceeds max, or
* HAVE_IO_TIMEOUT not compiled in)
*/
private void setIOTimeoutFromProperty() throws CertPathValidatorException {

int timeoutSec;
String ioTimeout;

try {
ioTimeout = System.getProperty("wolfjce.ioTimeout");
} catch (SecurityException e) {
/* SecurityManager blocked property access, treat as
* property not set and continue without timeout */
return;
}

if (ioTimeout == null) {
return;
}
final String trimmed = ioTimeout.trim();
if (trimmed.isEmpty()) {
return;
}

try {
timeoutSec = Integer.parseInt(trimmed);

/* Skip JNI call if value unchanged from last apply */
if (timeoutSec != lastAppliedIOTimeout) {
WolfCrypt.setIOTimeout(timeoutSec);
lastAppliedIOTimeout = timeoutSec;

WolfCryptDebug.log(
WolfCryptPKIXRevocationChecker.class,
WolfCryptDebug.INFO,
() -> "wolfjce.ioTimeout set to " +
trimmed + " seconds");
}

} catch (NumberFormatException e) {
throw new CertPathValidatorException(
"Invalid wolfjce.ioTimeout value: " + trimmed +
", must be integer seconds: " + e.getMessage(), e);

} catch (IllegalArgumentException e) {
throw new CertPathValidatorException(
"Invalid wolfjce.ioTimeout value: " + trimmed +
": " + e.getMessage(), e);

} catch (WolfCryptException e) {
throw new CertPathValidatorException(
"wolfjce.ioTimeout set but native wolfSSL not " +
"compiled with HAVE_IO_TIMEOUT: " + e.getMessage(), e);
}
}

/**
* Clone this revocation checker.
*
Expand Down
46 changes: 46 additions & 0 deletions src/main/java/com/wolfssl/wolfcrypt/WolfCrypt.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ public class WolfCrypt extends WolfObject {
private static native byte[] wcKeyPemToDer(byte[] pem, String password);
private static native byte[] wcCertPemToDer(byte[] pem);
private static native byte[] wcPubKeyPemToDer(byte[] pem);
private static native void nativeSetIOTimeout(int timeoutSec);

/* Public mappings of some SSL/TLS level enums/defines */
/** wolfSSL file type: PEM */
Expand Down Expand Up @@ -192,6 +193,51 @@ public class WolfCrypt extends WolfObject {
*/
public static native boolean Base16Enabled();

/**
* Tests if I/O timeout (HAVE_IO_TIMEOUT) has been enabled in wolfSSL.
*
* @return true if enabled, otherwise false if not compiled in
*/
public static native boolean IoTimeoutEnabled();

/** Maximum allowed I/O timeout value in seconds (1 hour) */
private static final int MAX_IO_TIMEOUT_SEC = 3600;

/**
* Set the I/O timeout used by native wolfSSL for HTTP-based operations
* including OCSP lookups and CRL fetching.
*
* Wraps native wolfIO_SetTimeout(). Requires native wolfSSL to be
* compiled with HAVE_IO_TIMEOUT.
*
* This sets a global (library-wide) timeout value in native
* wolfSSL. All threads and certificate validations in the same
* JVM share this single timeout setting.
*
* @param timeoutSec timeout value in seconds, 0 to 3600 inclusive.
* A value of 0 disables the timeout (default behavior).
*
* @throws WolfCryptException if HAVE_IO_TIMEOUT is not compiled
* into native wolfSSL
* @throws IllegalArgumentException if timeoutSec is negative or
* exceeds 3600 seconds
*/
public static void setIOTimeout(int timeoutSec) {

if (timeoutSec < 0) {
throw new IllegalArgumentException(
"Timeout value must not be negative");
}

if (timeoutSec > MAX_IO_TIMEOUT_SEC) {
throw new IllegalArgumentException(
"Timeout value must not exceed " +
MAX_IO_TIMEOUT_SEC + " seconds");
}

nativeSetIOTimeout(timeoutSec);
}

/**
* Constant time byte array comparison.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import static org.junit.Assert.*;

import org.junit.After;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Test;
Expand Down Expand Up @@ -68,6 +69,15 @@ public class WolfCryptPKIXRevocationCheckerTest {
@Rule(order = Integer.MIN_VALUE)
public TestRule testWatcher = TimedTestWatcher.create();

/**
* Clean up wolfjce.ioTimeout system property after each
* test to avoid affecting other tests.
*/
@After
public void clearIOTimeoutProperty() {
System.clearProperty("wolfjce.ioTimeout");
}

/**
* Test if this environment is Android.
* @return true if Android, otherwise false
Expand Down Expand Up @@ -1023,5 +1033,65 @@ public void testRevocationCheckerInitClearsExceptions() throws Exception {

cm.free();
}

@Test(timeout = 15000)
public void testRevocationCheckerIOTimeoutLowValue()
throws Exception {

if (!WolfCrypt.OcspEnabled()) {
/* Skip test if OCSP not compiled in */
return;
}

if (!WolfCrypt.IoTimeoutEnabled()) {
/* Skip test if HAVE_IO_TIMEOUT not compiled in */
return;
}

CertPathValidator cpv = CertPathValidator.getInstance("PKIX", provider);
WolfCryptPKIXRevocationChecker checker =
(WolfCryptPKIXRevocationChecker)cpv.getRevocationChecker();

/* Load certs */
FileInputStream fis = new FileInputStream(caCertDer);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate caCert = (X509Certificate)cf.generateCertificate(fis);
fis.close();

fis = new FileInputStream(serverCertDer);
X509Certificate serverCert =
(X509Certificate)cf.generateCertificate(fis);
fis.close();

/* Set 1 second I/O timeout via system property */
System.setProperty("wolfjce.ioTimeout", "1");

/* Set SOFT_FAIL and override OCSP URL to non-routable address.
* 198.51.100.1 (TEST-NET-2, RFC 5737) is not routable, so TCP connect
* will hang until timeout kicks in, rather than getting an immediate
* connection refused like localhost would. */
Set<Option> options = EnumSet.of(Option.SOFT_FAIL);
checker.setOptions(options);
checker.setOcspResponder(new URI("http://198.51.100.1:12345"));

/* Create CertManager, load CA, and init */
WolfSSLCertManager cm = new WolfSSLCertManager();
cm.CertManagerLoadCA(caCert);
checker.setCertManager(cm);
checker.init(false);

/* Time the check() call. With 1 second timeout and a non-routable OCSP
* URL, should complete quickly. */
long startMs = System.currentTimeMillis();
checker.check(serverCert, null);
long elapsedMs = System.currentTimeMillis() - startMs;

/* Verify check completed within reasonable time.
* Allow 10 sec margin for system overhead/etc. */
assertTrue("OCSP check with 1s timeout took " + elapsedMs +
"ms, expected < 10000ms", elapsedMs < 10000);

cm.free();
}
}

Loading
Loading