diff --git a/native/com_wolfssl_WolfSSLSession.c b/native/com_wolfssl_WolfSSLSession.c
index 165038de..5b5ee1ba 100644
--- a/native/com_wolfssl_WolfSSLSession.c
+++ b/native/com_wolfssl_WolfSSLSession.c
@@ -5874,6 +5874,17 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_hasTicket
#endif
}
+JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_useClientSuites
+ (JNIEnv* jenv, jobject jcl, jlong sslPtr)
+{
+ (void)jcl;
+ WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
+ if (jenv == NULL || ssl == NULL) {
+ return WOLFSSL_FAILURE;
+ }
+ return (jint)wolfSSL_UseClientSuites(ssl);
+}
+
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_interruptBlockedIO
(JNIEnv* jenv, jobject jcl, jlong sslPtr)
{
diff --git a/native/com_wolfssl_WolfSSLSession.h b/native/com_wolfssl_WolfSSLSession.h
index 12fb1168..a23ce534 100644
--- a/native/com_wolfssl_WolfSSLSession.h
+++ b/native/com_wolfssl_WolfSSLSession.h
@@ -959,6 +959,14 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_disableExtendedMasterSecr
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_hasTicket
(JNIEnv *, jobject, jlong);
+/*
+ * Class: com_wolfssl_WolfSSLSession
+ * Method: useClientSuites
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_useClientSuites
+ (JNIEnv *, jobject, jlong);
+
/*
* Class: com_wolfssl_WolfSSLSession
* Method: interruptBlockedIO
diff --git a/src/java/com/wolfssl/WolfSSLSession.java b/src/java/com/wolfssl/WolfSSLSession.java
index 4e31ce30..ad0ea938 100644
--- a/src/java/com/wolfssl/WolfSSLSession.java
+++ b/src/java/com/wolfssl/WolfSSLSession.java
@@ -693,6 +693,7 @@ private native int setTlsHmacInner(long ssl, byte[] inner, long sz,
private native int useSupportedCurve(long ssl, int name);
private native int disableExtendedMasterSecret(long ssl);
private native int hasTicket(long session);
+ private native int useClientSuites(long ssl);
private native int interruptBlockedIO(long ssl);
private native int getThreadsBlockedInPoll(long ssl);
private native int setMTU(long ssl, int mtu);
@@ -2476,6 +2477,26 @@ public long getTimeout() throws IllegalStateException {
}
}
+ /**
+ * Set SSL session to use Client cipher suite order preference.
+ * @return SSL_SUCCESS upon success.
+ * SSL_FAILURE upon failure.
+ * @throws IllegalStateException WolfSSLSession has been freed
+ *
+ */
+ public int useClientSuites() throws IllegalStateException {
+
+ confirmObjectIsActive();
+
+ synchronized (sslLock) {
+ WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
+ WolfSSLDebug.INFO, this.sslPtr,
+ () -> "entered useClientSuites()");
+
+ return useClientSuites(this.sslPtr);
+ }
+ }
+
/**
* Sets the cipher suite list for a given SSL session.
* The ciphers in the list should be sorted in order of preference from
diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java b/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java
index 044da0eb..37b46f8a 100644
--- a/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java
+++ b/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java
@@ -759,6 +759,11 @@ private void setLocalCiphers(String[] suites)
}
}
+ if (this.ssl.getSide() == WolfSSL.WOLFSSL_SERVER_END &&
+ !this.params.getUseCipherSuitesOrder()) {
+ this.ssl.useClientSuites();
+ }
+
} catch (IllegalStateException e) {
throw new IllegalArgumentException(e);
}
diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLParametersHelper.java b/src/java/com/wolfssl/provider/jsse/WolfSSLParametersHelper.java
index 2a39368d..46be1e14 100644
--- a/src/java/com/wolfssl/provider/jsse/WolfSSLParametersHelper.java
+++ b/src/java/com/wolfssl/provider/jsse/WolfSSLParametersHelper.java
@@ -43,6 +43,8 @@ public class WolfSSLParametersHelper
private static Method setSNIMatchers = null;
private static Method getMaximumPacketSize = null;
private static Method setMaximumPacketSize = null;
+ private static Method setUseCipherSuitesOrder = null;
+ private static Method getUseCipherSuitesOrder = null;
/** Default WolfSSLParametersHelper constructor */
public WolfSSLParametersHelper() { }
@@ -91,6 +93,12 @@ public Object run() {
case "setMaximumPacketSize":
setMaximumPacketSize = m;
continue;
+ case "setUseCipherSuitesOrder":
+ setUseCipherSuitesOrder = m;
+ continue;
+ case "getUseCipherSuitesOrder":
+ getUseCipherSuitesOrder = m;
+ continue;
default:
continue;
}
@@ -134,7 +142,8 @@ protected static SSLParameters decoupleParams(WolfSSLParameters in) {
* do not existing in older JDKs. Since older JDKs will not have them,
* use Java reflection to detect availability in helper class. */
if (setServerNames != null || setApplicationProtocols != null ||
- setEndpointIdentificationAlgorithm != null || setSNIMatchers != null) {
+ setEndpointIdentificationAlgorithm != null ||
+ setSNIMatchers != null || setUseCipherSuitesOrder != null) {
try {
/* load WolfSSLJDK8Helper at runtime, not compiled
@@ -167,6 +176,11 @@ protected static SSLParameters decoupleParams(WolfSSLParameters in) {
mth = cls.getDeclaredMethod("setSNIMatchers", paramList);
mth.invoke(obj, ret, setSNIMatchers, in);
}
+ if (setUseCipherSuitesOrder != null) {
+ mth = cls.getDeclaredMethod("setUseCipherSuitesOrder",
+ paramList);
+ mth.invoke(obj, ret, setUseCipherSuitesOrder, in);
+ }
} catch (Exception e) {
/* ignore, class not found */
@@ -193,15 +207,20 @@ protected static SSLParameters decoupleParams(WolfSSLParameters in) {
/* Not available, just ignore and continue */
}
+ try {
+ if (setUseCipherSuitesOrder != null) {
+ ret.setUseCipherSuitesOrder(in.getUseCipherSuitesOrder());
+ }
+ } catch (Exception e) {
+ /* Not available, just ignore and continue */
+ }
+
/* The following SSLParameters features are not yet supported
* by wolfJSSE (see Android API 23 note above). They are supported
* with newer versions of SSLParameters, but will need to be added
* conditionally to wolfJSSE when supported. */
/*ret.setAlgorithmConstraints(in.getAlgorithmConstraints());
ret.setEnableRetransmissions(in.getEnableRetransmissions());
- ret.setMaximumPacketSize(in.getMaximumPacketSize());
- ret.setSNIMatchers(in.getSNIMatchers());
- ret.setUseCipherSuitesOrder(in.getUseCipherSuitesOrder());
*/
return ret;
@@ -242,7 +261,8 @@ protected static void importParams(SSLParameters in,
* do not existing in older JDKs. Since older JDKs will not have them,
* use Java reflection to detect availability in helper class. */
if (getServerNames != null || getApplicationProtocols != null ||
- getEndpointIdentificationAlgorithm != null || getSNIMatchers != null) {
+ getEndpointIdentificationAlgorithm != null ||
+ getSNIMatchers != null || getUseCipherSuitesOrder != null) {
try {
/* load WolfSSLJDK8Helper at runtime, not compiled on older JDKs */
Class> cls = Class.forName(
@@ -267,10 +287,15 @@ protected static void importParams(SSLParameters in,
"getEndpointIdentificationAlgorithm", paramList);
mth.invoke(obj, in, out);
}
- if (getSNIMatchers != null){
+ if (getSNIMatchers != null) {
mth = cls.getDeclaredMethod("getSNIMatchers", paramList);
mth.invoke(obj, in, out);
}
+ if (getUseCipherSuitesOrder != null) {
+ mth = cls.getDeclaredMethod("getUseCipherSuitesOrder",
+ paramList);
+ mth.invoke(obj, in, out);
+ }
} catch (Exception e) {
/* ignore, class not found */
@@ -296,12 +321,21 @@ protected static void importParams(SSLParameters in,
* conditionally to wolfJSSE when supported. */
/*out.setAlgorithmConstraints(in.getAlgorithmConstraints());
out.setEnableRetransmissions(in.getEnableRetransmissions());
- out.setMaximumPacketSize(in.getMaximumPacketSize());
- out.setSNIMatchers(in.getSNIMatchers());
- out.setUseCipherSuitesOrder(in.getUseCipherSuitesOrder());
*/
- out.setSNIMatchers(in.getSNIMatchers());
+ try {
+ out.setSNIMatchers(in.getSNIMatchers());
+ } catch (Exception e) {
+ /* Not available, just ignore and continue */
+ }
+
+ try {
+ if (getUseCipherSuitesOrder != null) {
+ out.setUseCipherSuitesOrder(in.getUseCipherSuitesOrder());
+ }
+ } catch (Exception e) {
+ /* Not available, just ignore and continue */
+ }
}
}
diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java
index c4534618..c570d29b 100644
--- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java
+++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java
@@ -179,7 +179,7 @@ public static void testSetupSocketFactory() throws NoSuchProviderException,
try {
tf = new WolfSSLTestFactory();
} catch (WolfSSLException e) {
- // TODO Auto-generated catch block
+ /* TODO Auto-generated catch block */
e.printStackTrace();
}
@@ -321,6 +321,99 @@ public void testGetSetEnabledCipherSuites()
System.out.println("\t... passed");
}
+ @Test
+ public void testServerUsesClientCipherSuitePreference() throws Exception {
+
+ System.out.print("\tTesting client suite preference");
+
+ this.ctx = tf.createSSLContext("TLS", "wolfJSSE");
+
+ String[] serverSuites = {
+ "TLS_AES_256_GCM_SHA384",
+ "TLS_AES_128_GCM_SHA256"
+ };
+
+ String[] clientSuites = {
+ "TLS_AES_128_GCM_SHA256",
+ "TLS_AES_256_GCM_SHA384"
+ };
+
+ /* --- Case 1: default (server order) --- */
+ SSLServerSocket ss1 = (SSLServerSocket)ctx.getServerSocketFactory()
+ .createServerSocket(0);
+ ss1.setEnabledCipherSuites(serverSuites);
+
+ SSLSocket cs1 = (SSLSocket)ctx.getSocketFactory().createSocket();
+ cs1.setEnabledCipherSuites(clientSuites);
+ cs1.connect(new InetSocketAddress(ss1.getLocalPort()));
+
+ final SSLSocket server1 = (SSLSocket)ss1.accept();
+
+ ExecutorService es = Executors.newSingleThreadExecutor();
+ Future f1 = es.submit(() -> {
+ server1.startHandshake();
+ return null;
+ });
+
+ cs1.startHandshake();
+ f1.get();
+
+ String chosen1 = cs1.getSession().getCipherSuite();
+ /* Note: WolfSSL may report TLS 1.3 ciphers in IANA standard or wolfSSL
+ * alias depending on wolfSSL configuration. */
+ if (!"TLS_AES_256_GCM_SHA384".equals(chosen1) &&
+ !"TLS13-AES256-GCM-SHA384".equals(chosen1)) {
+ System.out.println("\t... failed");
+ fail("Expected server preference cipher (AES_256), got "
+ + chosen1);
+ }
+
+ cs1.close();
+ server1.close();
+ ss1.close();
+
+ /* --- Case 2: server honors client order --- */
+ SSLServerSocket ss2 = (SSLServerSocket)ctx.getServerSocketFactory()
+ .createServerSocket(0);
+ ss2.setEnabledCipherSuites(serverSuites);
+
+ /* Do not honor local cipher suites preference */
+ SSLParameters ss2Params = ss2.getSSLParameters();
+ ss2Params.setUseCipherSuitesOrder(false);
+ ss2.setSSLParameters(ss2Params);
+
+ SSLSocket cs2 = (SSLSocket)ctx.getSocketFactory().createSocket();
+ cs2.setEnabledCipherSuites(clientSuites);
+ cs2.connect(new InetSocketAddress(ss2.getLocalPort()));
+
+ final SSLSocket server2 = (SSLSocket)ss2.accept();
+
+ Future f2 = es.submit(() -> {
+ server2.startHandshake();
+ return null;
+ });
+
+ cs2.startHandshake();
+ f2.get();
+
+ String chosen2 = cs2.getSession().getCipherSuite();
+ /* Note: WolfSSL may report TLS 1.3 ciphers in IANA standard or wolfSSL
+ * alias depending on wolfSSL configuration. */
+ if (!"TLS_AES_128_GCM_SHA256".equals(chosen2) &&
+ !"TLS13-AES128-GCM-SHA256".equals(chosen2)) {
+ System.out.println("\t... failed");
+ fail("Expected client preference cipher (AES_128), got "
+ + chosen2);
+ }
+
+ cs2.close();
+ server2.close();
+ ss2.close();
+ es.shutdown();
+
+ System.out.println("\t... passed");
+ }
+
@Test
public void testGetSupportedProtocols()
throws NoSuchProviderException, NoSuchAlgorithmException {
@@ -659,7 +752,7 @@ public void testEnabledSupportedCurvesProperty() throws Exception {
try {
client.join(1000);
- //server.join(1000);
+ /* server.join(1000); */
} catch (InterruptedException e) {
System.out.println("interrupt happened");
@@ -3402,14 +3495,14 @@ public void testAutoSNIProperty() throws Exception {
public void testSNIMatchers() throws Exception {
System.out.print("\tTesting SNI Matchers");
-
+
/* create new CTX */
this.ctx = tf.createSSLContext("TLS", ctxProvider);
-
+
/* create SSLServerSocket first to get ephemeral port */
final SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
.createServerSocket(0);
-
+
/* Configure SNI matcher for server*/
SNIMatcher matcher = SNIHostName.createSNIMatcher("www\\.example\\.com");
Collection matchers = new ArrayList<>();
@@ -3436,7 +3529,7 @@ public void testSNIMatchers() throws Exception {
cs.setSSLParameters(cp);
final SSLSocket serverMatched = (SSLSocket)ss.accept();
-
+
ExecutorService es = Executors.newSingleThreadExecutor();
Future serverFuture = es.submit(new Callable() {
@Override
@@ -3451,10 +3544,10 @@ public Void call() throws Exception {
return null;
}
});
-
+
cs.startHandshake();
cs.close();
-
+
es.shutdown();
serverFuture.get();
@@ -3473,7 +3566,7 @@ public Void call() throws Exception {
cs.setSSLParameters(cp);
final SSLSocket serverUnmatched = (SSLSocket)ss.accept();
-
+
es = Executors.newSingleThreadExecutor();
serverFuture = es.submit(() -> {
try {