Skip to content

Commit bbca1e8

Browse files
committed
JNI/JCE: wrap wolfIO_SetTimeout(), add wolfjce.ioTimeout system property
1 parent ad62699 commit bbca1e8

9 files changed

Lines changed: 570 additions & 1 deletion

File tree

README_JCE.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,20 @@ programatically for JCE provider customization:
107107
| System Property | Default | To Enable | Description |
108108
| --- | --- | --- | --- |
109109
| wolfjce.debug | "false" | "true" | Enable wolfJCE debug logging |
110+
| wolfjce.ioTimeout | UNSET | Integer (seconds) | I/O timeout for OCSP and CRL HTTP operations (0-3600) |
111+
112+
**wolfjce.ioTimeout** - sets the I/O timeout (in seconds) used by native wolfSSL
113+
for HTTP-based OCSP lookups and CRL fetching. Wraps native `wolfIO_SetTimeout()`.
114+
Requires native wolfSSL to be compiled with `HAVE_IO_TIMEOUT`. Valid values are
115+
0 to 3600 (1 hour). A value of 0 disables the timeout (default behavior). This
116+
property is read during `PKIXRevocationChecker.init()`, which occurs at
117+
certificate path validation time. This means the property can be set or changed
118+
after provider registration and will be picked up on the next validation.
119+
Invalid values (non-numeric, negative, exceeding 3600) will cause revocation
120+
checker initialization to fail with `CertPathValidatorException`. This property
121+
replaces the Sun-specific `com.sun.security.ocsp.timeout` and
122+
`com.sun.security.crl.timeout` properties (which use milliseconds) with a single
123+
wolfJCE-specific property in seconds that applies to both OCSP and CRL operations.
110124

111125
### Algorithm Support:
112126
---------

jni/include/com_wolfssl_wolfcrypt_WolfCrypt.h

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jni/jni_wolfcrypt.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <wolfssl/wolfcrypt/coding.h>
3333
#include <wolfssl/wolfcrypt/asn_public.h>
3434
#include <wolfssl/wolfcrypt/error-crypt.h>
35+
#include <wolfssl/wolfio.h>
3536
#include <com_wolfssl_wolfcrypt_WolfCrypt.h>
3637
#include <wolfcrypt_jni_error.h>
3738

@@ -612,3 +613,30 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_wcPubKeyPemToD
612613
#endif /* !NO_ASN && !WOLFSSL_NO_PEM && !NO_CODING) */
613614
}
614615

616+
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_IoTimeoutEnabled
617+
(JNIEnv* env, jclass jcl)
618+
{
619+
(void)env;
620+
(void)jcl;
621+
622+
#ifdef HAVE_IO_TIMEOUT
623+
return JNI_TRUE;
624+
#else
625+
return JNI_FALSE;
626+
#endif
627+
}
628+
629+
JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_WolfCrypt_nativeSetIOTimeout
630+
(JNIEnv* env, jclass jcl, jint timeoutSec)
631+
{
632+
(void)jcl;
633+
634+
#ifdef HAVE_IO_TIMEOUT
635+
wolfIO_SetTimeout(timeoutSec);
636+
(void)env;
637+
#else
638+
(void)timeoutSec;
639+
throwNotCompiledInException(env);
640+
#endif
641+
}
642+

src/main/java/com/wolfssl/provider/jce/WolfCryptPKIXRevocationChecker.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ public void init(boolean forward) throws CertPathValidatorException {
147147
this.initialized = true;
148148
this.softFailExceptions.clear();
149149

150+
/* Set wolfSSL I/O timeout for HTTP-based operations (OCSP lookups,
151+
* CRL fetching) if 'wolfjce.ioTimeout' System property is set. */
152+
setIOTimeoutFromProperty();
153+
150154
/* Verify we have OCSP support if needed */
151155
if (!options.contains(Option.PREFER_CRLS)) {
152156
if (!WolfCrypt.OcspEnabled()) {
@@ -591,6 +595,52 @@ public List<CertPathValidatorException> getSoftFailExceptions() {
591595
return Collections.unmodifiableList(this.softFailExceptions);
592596
}
593597

598+
/**
599+
* Read and apply wolfjce.ioTimeout system property.
600+
*
601+
* Sets the native wolfSSL I/O timeout via wolfIO_SetTimeout()
602+
* if the property is set and valid. If the property is set but
603+
* contains an invalid value, throws CertPathValidatorException
604+
* to fail revocation checker initialization.
605+
*
606+
* @throws CertPathValidatorException if property value is
607+
* invalid (not a number, negative, exceeds max, or
608+
* HAVE_IO_TIMEOUT not compiled in)
609+
*/
610+
private void setIOTimeoutFromProperty() throws CertPathValidatorException {
611+
612+
int timeoutSec;
613+
String ioTimeout = System.getProperty("wolfjce.ioTimeout");
614+
615+
if (ioTimeout == null || ioTimeout.isEmpty()) {
616+
return;
617+
}
618+
619+
try {
620+
timeoutSec = Integer.parseInt(ioTimeout);
621+
WolfCrypt.setIOTimeout(timeoutSec);
622+
623+
WolfCryptDebug.log(WolfCryptPKIXRevocationChecker.class,
624+
WolfCryptDebug.INFO, () -> "wolfjce.ioTimeout set to " +
625+
ioTimeout + " seconds");
626+
627+
} catch (NumberFormatException e) {
628+
throw new CertPathValidatorException(
629+
"Invalid wolfjce.ioTimeout value: " + ioTimeout +
630+
", must be integer seconds: " + e.getMessage(), e);
631+
632+
} catch (IllegalArgumentException e) {
633+
throw new CertPathValidatorException(
634+
"Invalid wolfjce.ioTimeout value: " + ioTimeout + ": " +
635+
e.getMessage(), e);
636+
637+
} catch (WolfCryptException e) {
638+
throw new CertPathValidatorException(
639+
"wolfjce.ioTimeout set but native wolfSSL not compiled " +
640+
"with HAVE_IO_TIMEOUT: " + e.getMessage(), e);
641+
}
642+
}
643+
594644
/**
595645
* Clone this revocation checker.
596646
*

src/main/java/com/wolfssl/wolfcrypt/WolfCrypt.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ public class WolfCrypt extends WolfObject {
134134
private static native byte[] wcKeyPemToDer(byte[] pem, String password);
135135
private static native byte[] wcCertPemToDer(byte[] pem);
136136
private static native byte[] wcPubKeyPemToDer(byte[] pem);
137+
private static native void nativeSetIOTimeout(int timeoutSec);
137138

138139
/* Public mappings of some SSL/TLS level enums/defines */
139140
/** wolfSSL file type: PEM */
@@ -192,6 +193,47 @@ public class WolfCrypt extends WolfObject {
192193
*/
193194
public static native boolean Base16Enabled();
194195

196+
/**
197+
* Tests if I/O timeout (HAVE_IO_TIMEOUT) has been enabled in wolfSSL.
198+
*
199+
* @return true if enabled, otherwise false if not compiled in
200+
*/
201+
public static native boolean IoTimeoutEnabled();
202+
203+
/** Maximum allowed I/O timeout value in seconds (1 hour) */
204+
private static final int MAX_IO_TIMEOUT_SEC = 3600;
205+
206+
/**
207+
* Set the I/O timeout used by native wolfSSL for HTTP-based operations
208+
* including OCSP lookups and CRL fetching.
209+
*
210+
* Wraps native wolfIO_SetTimeout(). Requires native wolfSSL to be
211+
* compiled with HAVE_IO_TIMEOUT.
212+
*
213+
* @param timeoutSec timeout value in seconds, between 0 and 3600.
214+
* A value of 0 disables the timeout (default behavior).
215+
*
216+
* @throws WolfCryptException if HAVE_IO_TIMEOUT is not compiled
217+
* into native wolfSSL
218+
* @throws IllegalArgumentException if timeoutSec is negative or
219+
* exceeds 3600 seconds
220+
*/
221+
public static void setIOTimeout(int timeoutSec) {
222+
223+
if (timeoutSec < 0) {
224+
throw new IllegalArgumentException(
225+
"Timeout value must not be negative");
226+
}
227+
228+
if (timeoutSec > MAX_IO_TIMEOUT_SEC) {
229+
throw new IllegalArgumentException(
230+
"Timeout value must not exceed " +
231+
MAX_IO_TIMEOUT_SEC + " seconds");
232+
}
233+
234+
nativeSetIOTimeout(timeoutSec);
235+
}
236+
195237
/**
196238
* Constant time byte array comparison.
197239
*

src/test/java/com/wolfssl/provider/jce/test/WolfCryptPKIXRevocationCheckerTest.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import static org.junit.Assert.*;
2525

26+
import org.junit.After;
2627
import org.junit.Assume;
2728
import org.junit.BeforeClass;
2829
import org.junit.Test;
@@ -68,6 +69,15 @@ public class WolfCryptPKIXRevocationCheckerTest {
6869
@Rule(order = Integer.MIN_VALUE)
6970
public TestRule testWatcher = TimedTestWatcher.create();
7071

72+
/**
73+
* Clean up wolfjce.ioTimeout system property after each
74+
* test to avoid affecting other tests.
75+
*/
76+
@After
77+
public void clearIOTimeoutProperty() {
78+
System.clearProperty("wolfjce.ioTimeout");
79+
}
80+
7181
/**
7282
* Test if this environment is Android.
7383
* @return true if Android, otherwise false
@@ -1023,5 +1033,65 @@ public void testRevocationCheckerInitClearsExceptions() throws Exception {
10231033

10241034
cm.free();
10251035
}
1036+
1037+
@Test
1038+
public void testRevocationCheckerIOTimeoutLowValue()
1039+
throws Exception {
1040+
1041+
if (!WolfCrypt.OcspEnabled()) {
1042+
/* Skip test if OCSP not compiled in */
1043+
return;
1044+
}
1045+
1046+
if (!WolfCrypt.IoTimeoutEnabled()) {
1047+
/* Skip test if HAVE_IO_TIMEOUT not compiled in */
1048+
return;
1049+
}
1050+
1051+
CertPathValidator cpv = CertPathValidator.getInstance("PKIX", provider);
1052+
WolfCryptPKIXRevocationChecker checker =
1053+
(WolfCryptPKIXRevocationChecker)cpv.getRevocationChecker();
1054+
1055+
/* Load certs */
1056+
FileInputStream fis = new FileInputStream(caCertDer);
1057+
CertificateFactory cf = CertificateFactory.getInstance("X.509");
1058+
X509Certificate caCert = (X509Certificate)cf.generateCertificate(fis);
1059+
fis.close();
1060+
1061+
fis = new FileInputStream(serverCertDer);
1062+
X509Certificate serverCert =
1063+
(X509Certificate)cf.generateCertificate(fis);
1064+
fis.close();
1065+
1066+
/* Set 1 second I/O timeout via system property */
1067+
System.setProperty("wolfjce.ioTimeout", "1");
1068+
1069+
/* Set SOFT_FAIL and override OCSP URL to non-routable address.
1070+
* 198.51.100.1 (TEST-NET-2, RFC 5737) is not routable, so TCP connect
1071+
* will hang until timeout kicks in, rather than getting an immediate
1072+
* connection refused like localhost would. */
1073+
Set<Option> options = EnumSet.of(Option.SOFT_FAIL);
1074+
checker.setOptions(options);
1075+
checker.setOcspResponder(new URI("http://198.51.100.1:12345"));
1076+
1077+
/* Create CertManager, load CA, and init */
1078+
WolfSSLCertManager cm = new WolfSSLCertManager();
1079+
cm.CertManagerLoadCA(caCert);
1080+
checker.setCertManager(cm);
1081+
checker.init(false);
1082+
1083+
/* Time the check() call. With 1 second timeout and a non-routable OCSP
1084+
* URL, should complete quickly. */
1085+
long startMs = System.currentTimeMillis();
1086+
checker.check(serverCert, null);
1087+
long elapsedMs = System.currentTimeMillis() - startMs;
1088+
1089+
/* Verify check completed within reasonable time.
1090+
* Allow 10 sec margin for system overhead/etc. */
1091+
assertTrue("OCSP check with 1s timeout took " + elapsedMs +
1092+
"ms, expected < 10000ms", elapsedMs < 10000);
1093+
1094+
cm.free();
1095+
}
10261096
}
10271097

0 commit comments

Comments
 (0)