JNI: add support for Java Security properties:

- wolfssl.readWriteByteBufferPool.disabled
- wolfssl.readWriteByteBufferPool.size
- wolfssl.readWriteByteBufferPool.bufferSize
pull/268/head
Chris Conlon 2025-05-21 11:27:36 -06:00
parent 1c8963e4fe
commit 0c4b3190c7
4 changed files with 559 additions and 11 deletions

View File

@ -431,8 +431,15 @@ used as the default descriptor monitoring function.
wolfJSSE allows for some customization through the `java.security` file
and use of Security properties.
#### Pre-Existing Java Security Properties
Support is included for the following pre-existing Java Security properties.
| System Property | Default | To Enable | Description |
| --- | --- | --- | --- |
| keystore.type | JKS | String | Specifies the default KeyStore type |
| jdk.tls.disabledAlgorithms | | String | Disables algorithms, TLS protocol versions, and key lengths |
**keystore.type (String)** - Specifies the default KeyStore type. This defaults
to JKS, but could be set to something else if desired.
@ -446,12 +453,60 @@ minimum RSA/ECC/DH key sizes. An example of potential use:
jdk.tls.disabledAlgorithms=SSLv3, TLSv1.1, DH keySize < 1024, EC keySize < 224, RSA keySize < 1024
```
The following custom wolfJSSE-specific Security property settings are supported.
These can be placed into the `java.security` file and will be parsed and used
by wolfJSSE.
#### wolfSSL JNI/JSSE Specific Security Properties
The following custom wolfSSL JNI/JSSE specific Security property settings are
supported. These can be placed into the `java.security` file and will be parsed
and used by wolfSSL JNI/JSSE.
| System Property | Default | To Enable | Description |
| --- | --- | --- | --- |
| wolfssl.readWriteByteBufferPool.disabled | "false" | "true" | Disables the read/write ByteBuffer pool |
| wolfssl.readWriteByteBufferPool.size | 16 | Integer | Sets the read/write per-thread ByteBuffer pool size |
| wolfssl.readWriteByteBufferPool.bufferSize | 17408 | String | Sets the read/write per-thread ByteBuffer size |
| wolfjsse.enabledCipherSuites | | String | Restricts enabled cipher suites |
| wolfjsse.enabledSupportedCurves | | String | Restricts enabled ECC curves |
| wolfjsse.enabledSignatureAlgorithms | | String | Restricts enabled signature algorithms |
| wolfjsse.keystore.type.required | | String | Restricts KeyStore type |
| wolfjsse.clientSessionCache.disabled | | "true" | Disables client session cache |
**wolfssl.readWriteByteBufferPool.disabled (String)** - Can be used to disable
the static per-thread ByteBuffer pool used in com.wolfssl.WolfSSLSession
for native JNI wolfSS\_read() and wolfSSL\_write() calls. This pool is in place
to prevent unaligned memory access at the JNI level when using byte array
offsets. This pool is enabled by default unless explicitly disabled by setting
this property to "true":
```
wolfssl.readWriteByteBufferPool.disabled=true
```
**wolfssl.readWriteByteBufferPool.size (Integer)** - Can be used to set the
maximum per-thread ByteBuffer pool size. This is the maximum number of
direct ByteBuffer objects that will be allocated and added to the pool. The
pool starts at size 0, then grows as needed up to this maximum size. The
default is 16. This should be set to a positive integer value:
```
wolfssl.readWriteByteBufferPool.size=16
```
**wolfssl.readWriteByteBufferPool.bufferSize (String)** - Can be used to set
the size of each direct ByteBuffer in the static per-thread WolfSSLSession
pool. This is set to 17k (17 * 1024) by default which allows for the maximum
SSL/TLS record size of 2^14 (16k) plus some extra space for the record header
overhead. This should be set to a positive integer value. This can be used
to optimize performance if the size of data an application is reading/writing
is known. If sized properly, fewer read/write loops will need to be done
when calling native `wolfSSL_read()` and `wolfSSL_write()` inside
com.wolfssl.WolfSSLSession read() and write() methods.
```
wolfssl.readWriteByteBufferPool.bufferSize=17408
```
**wolfjsse.enabledCipherSuites (String)** - Allows restriction of the enabled
cipher suiets to those listed in this Security property. When set, applications
cipher suites to those listed in this Security property. When set, applications
wil not be able to override or add additional suites at runtime without
changing this property. This should be a comma-delimited String. Example use:

View File

@ -7,10 +7,6 @@
#ifdef __cplusplus
extern "C" {
#endif
#undef com_wolfssl_WolfSSLSession_MAX_POOL_SIZE
#define com_wolfssl_WolfSSLSession_MAX_POOL_SIZE 32L
#undef com_wolfssl_WolfSSLSession_BUFFER_SIZE
#define com_wolfssl_WolfSSLSession_BUFFER_SIZE 17408L
/*
* Class: com_wolfssl_WolfSSLSession
* Method: newSSL

View File

@ -31,6 +31,7 @@ import java.lang.StringBuilder;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.security.Security;
/**
* Wraps a native WolfSSL session object and contains methods directly related
@ -109,6 +110,9 @@ public class WolfSSLSession {
/* lock around native WOLFSSL pointer use */
private final Object sslLock = new Object();
/* Is static direct ByteBuffer pool enabled for read/write() calls */
private boolean byteBufferPoolEnabled = true;
/* Maximum direct ByteBuffer pool size */
private static int MAX_POOL_SIZE = 16;
@ -127,13 +131,134 @@ public class WolfSSLSession {
private static final ThreadLocal<ConcurrentLinkedQueue<ByteBuffer>> directBufferPool =
ThreadLocal.withInitial(() -> new ConcurrentLinkedQueue<>());
/**
* Check if static direct ByteBuffer pool has been disabled for
* use in read/write() methods.
*
* The pool is enabled by default, unless explicitly disabled by setting
* the "wolfssl.readWriteByteBufferPool.disabled" property to "true".
*
* @return true if disabled, otherwise false
*/
private boolean readWritePoolDisabled() {
String disabled =
Security.getProperty("wolfssl.readWriteByteBufferPool.disabled");
if (disabled == null || disabled.isEmpty()) {
return false;
}
if (disabled.equalsIgnoreCase("true")) {
return true;
}
return false;
}
/**
* Check if the maximum size of the static per-thread direct
* ByteBuffer pool has been adjusted by setting of the
* "wolfssl.readWriteByteBufferPool.size" Security property.
*
* @return the size set, or the current default
* (MAX_POOL_SIZE) if not.
*/
private int readWritePoolGetMaxSizeFromProperty() {
int maxSize = MAX_POOL_SIZE;
String sizeProp =
Security.getProperty("wolfssl.readWriteByteBufferPool.size");
if (sizeProp == null || sizeProp.isEmpty()) {
return maxSize;
}
try {
int size = Integer.parseInt(sizeProp);
if (size > 0) {
maxSize = size;
}
} catch (NumberFormatException e) {
WolfSSLDebug.log(getClass(),
WolfSSLDebug.Component.JNI, WolfSSLDebug.ERROR, 0,
() -> "Invalid value for " +
"wolfssl.readWriteByteBufferPool.size: " + sizeProp);
}
return maxSize;
}
/**
* Check if the size of the ByteBuffers in the static per-thread
* pool has been adjusted by setting of the
* "wolfssl.readWriteByteBufferPool.bufferSize" Security property.
*
* @return the size set, or the current default (BUFFER_SIZE) if not.
*/
private int readWritePoolGetBufferSizeFromProperty() {
int bufferSize = BUFFER_SIZE;
String sizeProp =
Security.getProperty("wolfssl.readWriteByteBufferPool.bufferSize");
if (sizeProp == null || sizeProp.isEmpty()) {
return bufferSize;
}
try {
int size = Integer.parseInt(sizeProp);
if (size > 0) {
bufferSize = size;
}
} catch (NumberFormatException e) {
WolfSSLDebug.log(getClass(),
WolfSSLDebug.Component.JNI, WolfSSLDebug.ERROR, 0,
() -> "Invalid value for " +
"wolfssl.readWriteByteBufferPool.bufferSize: " + sizeProp);
}
return bufferSize;
}
/**
* Read current values of relevant Security properties and set
* internal behavior.
*/
private void detectSecurityPropertySettings() {
/* Re-use sslLock for synchronization here */
synchronized (sslLock) {
/* Check if static direct ByteBuffer pool has been disabled
* with the "wolfssl.readWriteByteBufferPool.disabled"
* Security property. */
if (readWritePoolDisabled()) {
this.byteBufferPoolEnabled = false;
}
/* Check if the maximum size of the static per-thread direct
* ByteBuffer pool has been adjusted by setting of the
* "wolfssl.readWriteByteBufferPool.size" Security property. */
WolfSSLSession.MAX_POOL_SIZE =
readWritePoolGetMaxSizeFromProperty();
/* Check if the size of the ByteBuffers in the static per-thread
* pool has been adjusted by setting of the
* "wolfssl.readWriteByteBufferPool.bufferSize" Security property. */
WolfSSLSession.BUFFER_SIZE =
readWritePoolGetBufferSizeFromProperty();
}
}
/**
* Get a DirectByteBuffer from the thread-local pool or allocate a new one
* if the pool is empty.
*
* @return a direct ByteBuffer ready to use
*/
private static ByteBuffer acquireDirectBuffer() {
private static synchronized ByteBuffer acquireDirectBuffer() {
ConcurrentLinkedQueue<ByteBuffer> threadPool = directBufferPool.get();
ByteBuffer buffer = threadPool.poll();
if (buffer == null) {
@ -159,7 +284,7 @@ public class WolfSSLSession {
*
* @param buffer the buffer to return to the pool
*/
private static void releaseDirectBuffer(ByteBuffer buffer) {
private static synchronized void releaseDirectBuffer(ByteBuffer buffer) {
if (buffer != null && buffer.isDirect()) {
@ -207,6 +332,8 @@ public class WolfSSLSession {
WolfSSLDebug.INFO, sslPtr,
() -> "creating new WolfSSLSession (with I/O pipe)");
detectSecurityPropertySettings();
synchronized (stateLock) {
this.active = true;
}
@ -254,6 +381,8 @@ public class WolfSSLSession {
() -> "creating new WolfSSLSession (without I/O pipe)");
}
detectSecurityPropertySettings();
synchronized (stateLock) {
this.active = true;
}
@ -1111,6 +1240,12 @@ public class WolfSSLSession {
* "buffer + offset" and end up with unaligned memory which
* can be slow on some targets (ex: ARM/Aarch64) */
try {
if (!this.byteBufferPoolEnabled) {
/* Throw exception so we fall back to using byte[] in the
* catch block below */
throw new Exception("ByteBuffer pool not enabled");
}
/* Get a buffer from the pool */
directBuffer = acquireDirectBuffer();
@ -1137,6 +1272,7 @@ public class WolfSSLSession {
if (ret <= 0) {
/* Error occurred, break out of loop */
err = getError(ret);
break;
}
@ -1164,7 +1300,7 @@ public class WolfSSLSession {
/* Return total bytes written, or last error code */
final int finalRet = (totalWritten > 0) ? totalWritten : ret;
final int finalErr = getError(finalRet);
final int finalErr = err;
final int finalTotal = totalWritten;
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
@ -1331,6 +1467,12 @@ public class WolfSSLSession {
* do "buffer + offset" and end up with unaligned memory which
* can be slow on some targets (ex: ARM/Aarch64) */
try {
if (!this.byteBufferPoolEnabled) {
/* Throw exception so we fall back to using byte[] in the
* catch block below */
throw new Exception("ByteBuffer pool not enabled");
}
/* Get a buffer from the pool */
directBuffer = acquireDirectBuffer();

View File

@ -40,6 +40,7 @@ import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.CountDownLatch;
import java.nio.ByteBuffer;
import java.security.Security;
import com.wolfssl.WolfSSL;
import com.wolfssl.WolfSSLDebug;
@ -70,6 +71,10 @@ public class WolfSSLSessionTest {
private static WolfSSLContext ctx = null;
/* Lock around WolfSSLSession static per-thread ByteBuffer pool
* Security property use in this test class */
private static final Object byteBufferPoolPropertyLock = new Object();
@BeforeClass
public static void loadLibrary()
throws WolfSSLException{
@ -1412,6 +1417,356 @@ public class WolfSSLSessionTest {
System.out.println("\t... passed");
}
/**
* Internal method that connects a client to a server and does
* one resumption.
*
* @throws Exception on error
*/
private void runClientServerOneResumption() throws Exception {
int ret = 0;
int err = 0;
long sessionPtr = 0;
long sesDup = 0;
Socket cliSock = null;
WolfSSLSession cliSes = null;
/* Create client/server WolfSSLContext objects, Server context
* must be final since used inside inner class. */
final WolfSSLContext srvCtx;
WolfSSLContext cliCtx;
/* Create ServerSocket first to get ephemeral port */
final ServerSocket srvSocket = new ServerSocket(0);
srvCtx = createAndSetupWolfSSLContext(srvCert, srvKey,
WolfSSL.SSL_FILETYPE_PEM, cliCert,
WolfSSL.SSLv23_ServerMethod());
cliCtx = createAndSetupWolfSSLContext(cliCert, cliKey,
WolfSSL.SSL_FILETYPE_PEM, caCert,
WolfSSL.SSLv23_ClientMethod());
/* Start server, handles 1 resumption */
try {
ExecutorService es = Executors.newSingleThreadExecutor();
es.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
int ret;
int err;
Socket server = null;
WolfSSLSession srvSes = null;
try {
/* Loop twice to allow handle one resumption */
for (int i = 0; i < 2; i++) {
server = srvSocket.accept();
srvSes = new WolfSSLSession(srvCtx);
ret = srvSes.setFd(server);
if (ret != WolfSSL.SSL_SUCCESS) {
throw new Exception(
"WolfSSLSession.setFd() failed: " +
ret);
}
do {
ret = srvSes.accept();
err = srvSes.getError(ret);
} while (ret != WolfSSL.SSL_SUCCESS &&
(err == WolfSSL.SSL_ERROR_WANT_READ ||
err == WolfSSL.SSL_ERROR_WANT_WRITE));
if (ret != WolfSSL.SSL_SUCCESS) {
throw new Exception(
"WolfSSLSession.accept() failed: " +
ret);
}
srvSes.shutdownSSL();
srvSes.freeSSL();
srvSes = null;
}
} finally {
if (srvSes != null) {
srvSes.freeSSL();
}
if (server != null) {
server.close();
}
}
return null;
}
});
} catch (Exception e) {
System.out.println("\t... failed");
e.printStackTrace();
fail();
}
try {
/* ------------------------------------------------------ */
/* Client connection #1 */
/* ------------------------------------------------------ */
cliSock = new Socket(InetAddress.getLocalHost(),
srvSocket.getLocalPort());
cliSes = new WolfSSLSession(cliCtx);
ret = cliSes.setFd(cliSock);
if (ret != WolfSSL.SSL_SUCCESS) {
throw new Exception(
"WolfSSLSession.setFd() failed, ret = " + ret);
}
do {
ret = cliSes.connect();
err = cliSes.getError(ret);
} while (ret != WolfSSL.SSL_SUCCESS &&
(err == WolfSSL.SSL_ERROR_WANT_READ ||
err == WolfSSL.SSL_ERROR_WANT_WRITE));
if (ret != WolfSSL.SSL_SUCCESS) {
throw new Exception(
"WolfSSLSession.connect() failed: " + err);
}
/* Get WOLFSSL_SESSION pointer */
sessionPtr = cliSes.getSession();
if (sessionPtr == 0) {
throw new Exception(
"WolfSSLSession.getSession() failed, ptr == 0");
}
/* wolfSSL_SessionIsSetup() may not be available, don't
* treat NOT_COMPILED_IN as an error */
ret = WolfSSLSession.sessionIsSetup(sessionPtr);
if ((ret != 1) && (ret != WolfSSL.NOT_COMPILED_IN)) {
throw new Exception(
"WolfSSLSession.sessionIsSetup() did not " +
"return 1: " + ret);
}
/* Test duplicateSession(), wraps wolfSSL_SESSION_dup() */
sesDup = WolfSSLSession.duplicateSession(sessionPtr);
if (sesDup == 0) {
throw new Exception(
"WolfSSLSession.duplicateSession() returned 0");
}
if (sesDup == sessionPtr) {
throw new Exception(
"WolfSSLSession.duplicateSession() returned " +
"same pointer");
}
WolfSSLSession.freeSession(sesDup);
sesDup = 0;
cliSes.shutdownSSL();
cliSes.freeSSL();
cliSes = null;
cliSock.close();
cliSock = null;
/* ------------------------------------------------------ */
/* Client connection #2, set session and try resumption */
/* ------------------------------------------------------ */
cliSock = new Socket(InetAddress.getLocalHost(),
srvSocket.getLocalPort());
cliSes = new WolfSSLSession(cliCtx);
ret = cliSes.setFd(cliSock);
if (ret != WolfSSL.SSL_SUCCESS) {
throw new Exception(
"WolfSSLSession.setFd() failed, ret = " + ret);
}
/* Set session pointer from original connection */
ret = cliSes.setSession(sessionPtr);
if (ret != WolfSSL.SSL_SUCCESS) {
throw new Exception(
"WolfSSLSession.setSession() failed: " + ret);
}
do {
ret = cliSes.connect();
err = cliSes.getError(ret);
} while (ret != WolfSSL.SSL_SUCCESS &&
(err == WolfSSL.SSL_ERROR_WANT_READ ||
err == WolfSSL.SSL_ERROR_WANT_WRITE));
if (ret != WolfSSL.SSL_SUCCESS) {
throw new Exception(
"WolfSSLSession.connect() failed: " + err);
}
/* Get WOLFSSL_SESSION pointer, free original one first */
WolfSSLSession.freeSession(sessionPtr);
sessionPtr = cliSes.getSession();
if (sessionPtr == 0) {
throw new Exception(
"WolfSSLSession.getSession() failed, ptr == 0");
}
/* Free WOLFSSL_SESSION pointer */
WolfSSLSession.freeSession(sessionPtr);
sessionPtr = 0;
/* Session should be marked as resumed */
if (cliSes.sessionReused() == 0) {
throw new Exception(
"Second connection not resumed");
}
cliSes.shutdownSSL();
cliSes.freeSSL();
cliSes = null;
cliSock.close();
cliSock = null;
} catch (Exception e) {
System.out.println("\t... failed");
e.printStackTrace();
fail();
} finally {
/* Free resources */
if (sessionPtr != 0) {
WolfSSLSession.freeSession(sessionPtr);
}
if (sesDup != 0) {
WolfSSLSession.freeSession(sesDup);
}
if (cliSes != null) {
cliSes.freeSSL();
}
if (cliSock != null) {
cliSock.close();
}
if (srvSocket != null) {
srvSocket.close();
}
if (srvCtx != null) {
srvCtx.free();
}
}
}
@Test
public void test_WolfSSLSession_disableByteBufferPool() throws Exception {
System.out.print("\tByteBuffer pool disabled");
synchronized (byteBufferPoolPropertyLock) {
String originalProp =
Security.getProperty("wolfssl.readWriteByteBufferPool.disabled");
try {
/* Disable WolfSSLSession internal direct ByteBuffer pool
* for use with read/write() calls */
Security.setProperty("wolfssl.readWriteByteBufferPool.disabled",
"true");
runClientServerOneResumption();
} finally {
if (originalProp == null) {
originalProp = "";
}
/* restore system property */
Security.setProperty("wolfssl.readWriteByteBufferPool.disabled",
originalProp);
}
}
System.out.println("\t... passed");
}
@Test
public void test_WolfSSLSession_byteBufferPoolSize() throws Exception {
System.out.print("\tByteBuffer pool size changes");
synchronized (byteBufferPoolPropertyLock) {
String originalProp =
Security.getProperty("wolfssl.readWriteByteBufferPool.size");
try {
/* Pool size of 0 */
Security.setProperty("wolfssl.readWriteByteBufferPool.size",
"0");
runClientServerOneResumption();
/* Pool size of 1 */
Security.setProperty("wolfssl.readWriteByteBufferPool.size",
"1");
runClientServerOneResumption();
/* Pool size of 100 */
Security.setProperty("wolfssl.readWriteByteBufferPool.size",
"100");
runClientServerOneResumption();
} finally {
if (originalProp == null) {
originalProp = "";
}
/* restore system property */
Security.setProperty("wolfssl.readWriteByteBufferPool.size",
originalProp);
}
}
System.out.println("\t... passed");
}
@Test
public void test_WolfSSLSession_byteBufferPoolBufferSize() throws Exception {
System.out.print("\tByteBuffer pool buffer sizes");
synchronized (byteBufferPoolPropertyLock) {
String originalProp =
Security.getProperty(
"wolfssl.readWriteByteBufferPool.bufferSize");
try {
/* Tiny buffer size of 128 bytes, lots of looping */
Security.setProperty(
"wolfssl.readWriteByteBufferPool.bufferSize", "128");
runClientServerOneResumption();
/* Bigger buffer size than default (17k), try 32k */
Security.setProperty(
"wolfssl.readWriteByteBufferPool.bufferSize", "32768");
runClientServerOneResumption();
/* Bigger buffer size than default (17k), try 64k */
Security.setProperty(
"wolfssl.readWriteByteBufferPool.bufferSize", "65536");
runClientServerOneResumption();
} finally {
if (originalProp == null) {
originalProp = "";
}
/* restore system property */
Security.setProperty(
"wolfssl.readWriteByteBufferPool.bufferSize", originalProp);
}
}
System.out.println("\t... passed");
}
/**
* wolfSSL I/O context, is passed to I/O callbacks when called
* by native wolfSSL.