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 {