diff --git a/native/com_wolfssl_WolfSSL.c b/native/com_wolfssl_WolfSSL.c index 33b66c62..1d46dc7b 100644 --- a/native/com_wolfssl_WolfSSL.c +++ b/native/com_wolfssl_WolfSSL.c @@ -2237,6 +2237,11 @@ JNIEXPORT jstring JNICALL Java_com_wolfssl_WolfSSL_getAvailableCipherSuitesIana } numCiphers = sk_num(supportedCiphers); + if (numCiphers == 0) { + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); + return NULL; + } for (i = 0; i < numCiphers; i++) { cipher = (const WOLFSSL_CIPHER*)sk_value(supportedCiphers, i); diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java b/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java index b4a4601f..85a0dd90 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java @@ -180,9 +180,9 @@ private void createCtx() throws WolfSSLException { if (WolfSSLUtil.isSecurityPropertyStringSet( "wolfjsse.enabledCipherSuites")) { /* User is overriding cipher suites, set CTX list */ - this.setCtxCiphers(WolfSSLUtil.sanitizeSuites(ciphersIana)); + this.setCtxCiphers(WolfSSLUtil.sanitizeSuites(ciphersIana, true)); } - params.setCipherSuites(WolfSSLUtil.sanitizeSuites(ciphersIana)); + params.setCipherSuites(WolfSSLUtil.sanitizeSuites(ciphersIana, true)); /* Auto-populate enabled protocols with supported ones. Protocols * which have been disabled via system property get filtered in diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java b/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java index 5693fc78..cb35d970 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java @@ -478,28 +478,33 @@ protected synchronized WolfSSLImplementSSLSession getSession() { /** * Get all supported cipher suites in native wolfSSL library, which * are also allowed by "wolfjsse.enabledCipherSuites" system Security - * property, if set. + * property, if set. Does not auto-filter out anon suites, since this + * returns all supported suites in native wolfSSL. * * @return String array of all supported cipher suites */ protected static synchronized String[] getAllCiphers() { - return WolfSSLUtil.sanitizeSuites(WolfSSL.getCiphersIana()); + return WolfSSLUtil.sanitizeSuites(WolfSSL.getCiphersIana(), false); } /** - * Get all enabled cipher suites, and allowed via - * wolfjsse.enabledCipherSuites system Security property (if set). + * Get all enabled cipher suites, filtered by + * wolfjsse.enabledCipherSuites system Security property (if set). Does + * not auto-filter out anon suites, since this returns all enabled suites in + * native wolfSSL. * * @return String array of all enabled cipher suites */ protected synchronized String[] getCiphers() { - return WolfSSLUtil.sanitizeSuites(this.params.getCipherSuites()); + return WolfSSLUtil.sanitizeSuites(this.params.getCipherSuites(), false); } /** - * Set cipher suites enabled in WolfSSLParameters + * Set cipher suites enabled in WolfSSLParameters. * - * Sanitizes input array for invalid suites + * Validates input array against supported cipher suites but does + * not filter anonymous suites, allowing applications to explicitly + * enable them if needed. * * @param suites String array of cipher suites to be enabled * @@ -528,7 +533,7 @@ protected synchronized void setCiphers(String[] suites) } } - this.params.setCipherSuites(WolfSSLUtil.sanitizeSuites(suites)); + this.params.setCipherSuites(WolfSSLUtil.sanitizeSuites(suites, false)); } /** @@ -1251,7 +1256,7 @@ private void setLocalParams(SSLSocket socket, SSLEngine engine) throws SSLException { this.setLocalCiphers( - WolfSSLUtil.sanitizeSuites(this.params.getCipherSuites())); + WolfSSLUtil.sanitizeSuites(this.params.getCipherSuites(), false)); this.setLocalProtocol( WolfSSLUtil.sanitizeProtocols( this.params.getProtocols(), WolfSSL.TLS_VERSION.INVALID)); diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLServerSocket.java b/src/java/com/wolfssl/provider/jsse/WolfSSLServerSocket.java index a712d17e..958e4446 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLServerSocket.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLServerSocket.java @@ -160,7 +160,7 @@ synchronized public String[] getEnabledCipherSuites() { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "entered getEnabledCipherSuites()"); - return WolfSSLUtil.sanitizeSuites(params.getCipherSuites()); + return WolfSSLUtil.sanitizeSuites(params.getCipherSuites(), false); } @Override @@ -178,9 +178,10 @@ synchronized public void setEnabledCipherSuites(String[] suites) throw new IllegalArgumentException("input array has length zero"); } - /* sanitize cipher array for unsupported strings */ + /* validate cipher array for unsupported strings. Not filtering + * anon suites in case user wants to explicitly set. */ List supported = Arrays.asList( - WolfSSLUtil.sanitizeSuites(WolfSSL.getCiphersIana())); + WolfSSLUtil.sanitizeSuites(WolfSSL.getCiphersIana(), false)); for (int i = 0; i < suites.length; i++) { if (!supported.contains(suites[i])) { throw new IllegalArgumentException("Unsupported CipherSuite: " + @@ -200,7 +201,7 @@ public String[] getSupportedCipherSuites() { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "entered getSupportedCipherSuites()"); - return WolfSSLUtil.sanitizeSuites(WolfSSL.getCiphersIana()); + return WolfSSLUtil.sanitizeSuites(WolfSSL.getCiphersIana(), false); } @Override diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLServerSocketFactory.java b/src/java/com/wolfssl/provider/jsse/WolfSSLServerSocketFactory.java index f9ae171a..21fda088 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLServerSocketFactory.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLServerSocketFactory.java @@ -62,6 +62,9 @@ public WolfSSLServerSocketFactory(com.wolfssl.WolfSSLContext ctx, /** * Returns the default cipher suite list for wolfJSSE. * + * Filters out anon suites by default. User can explicitly set + * if needed. + * * @return default array of cipher suite Strings for wolfSSL */ @Override @@ -70,7 +73,7 @@ public String[] getDefaultCipherSuites() { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "entered getDefaultCipherSuites()"); - return WolfSSLUtil.sanitizeSuites(WolfSSL.getCiphersIana()); + return WolfSSLUtil.sanitizeSuites(WolfSSL.getCiphersIana(), true); } /** @@ -84,7 +87,8 @@ public String[] getSupportedCipherSuites() { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "entered getSupportedCipherSuites()"); - return getDefaultCipherSuites(); + return WolfSSLUtil.sanitizeSuites( + WolfSSL.getCiphersIana(), false); } /** diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLSocketFactory.java b/src/java/com/wolfssl/provider/jsse/WolfSSLSocketFactory.java index 91488a94..9bd2d141 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLSocketFactory.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLSocketFactory.java @@ -118,6 +118,8 @@ private void initDefaultContext() throws WolfSSLException { /** * Returns the default cipher suite list for wolfJSSE. * + * Filters out anon suites by default. + * * @return default array of cipher suite Strings for wolfSSL */ @Override @@ -126,7 +128,7 @@ public String[] getDefaultCipherSuites() { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "entered getDefaultCipherSuites()"); - return WolfSSLUtil.sanitizeSuites(WolfSSL.getCiphersIana()); + return WolfSSLUtil.sanitizeSuites(WolfSSL.getCiphersIana(), true); } /** @@ -140,7 +142,7 @@ public String[] getSupportedCipherSuites() { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "entered getSupportedCipherSuites()"); - return getDefaultCipherSuites(); + return WolfSSLUtil.sanitizeSuites(WolfSSL.getCiphersIana(), false); } /** diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java b/src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java index ae2e9f27..90980270 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java @@ -125,37 +125,64 @@ protected static String[] sanitizeProtocols(String[] protocols, * Sanitize or filter SSL/TLS cipher suite list based on custom wolfJSSE * system property limitations. * - * Supported system Security properties which limit cipher suite list are: - * - wolfjsse.enabledCipherSuites + * When filterAnon is true, this method filters the default enabled cipher + * suite list, removing anonymous cipher suites to match SunJSSE behavior. + * When filterAnon is false, anonymous cipher suites are preserved, allowing + * applications to explicitly enable them via + * SSLEngine.setEnabledCipherSuites() or SSLSocket.setEnabledCipherSuites(). * - * This security property should contain a comma-separated list of - * values, for example: + * Filtering applied: + * + * 1. If filterAnon is true, anonymous cipher suites (containing + * "_anon_" in IANA name) are removed, matching SunJSSE behavior. + * + * 2. If the wolfjsse.enabledCipherSuites security property is set, + * the list is further filtered to only include suites in that + * property. This should contain a comma-separated list of values, + * for example: * * wolfjsse.enabledCipherSuites= * "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, \ * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" * - * Only the cipher suites included in this list will be allowed to be used - * in JSSE TLS connections. Applications can still set cipher suites, - * using for example SSLParameters, but the set cipher suite list will be - * filtered by this function to remove any suites not included in the - * system property mentioned here if it has been set. - * * @param suites Full list of TLS cipher suites to sanitize/filter, - * should be in format similar to: "SUITE1", "SUITE2", etc. - * - * @return New filtered String array of cipher suites. + * should be in format similar to: "SUITE1", "SUITE2", + * etc. + * @param filterAnon If true, anonymous cipher suites (containing "_anon_" + * in IANA name) will be filtered out. If false, anonymous + * suites are preserved. + * + * @return New filtered String array of cipher suites, or null if input + * is null. */ - protected static String[] sanitizeSuites(String[] suites) { + protected static String[] sanitizeSuites(String[] suites, + boolean filterAnon) { + ArrayList filtered = new ArrayList(); + if (suites == null) { + return null; + } + + /* Filter out anonymous cipher suites if requested. SunJSSE also + * excludes them from the default enabled list. Anonymous suites + * contain "_anon_" in IANA format. When filterAnon is false, all + * non-null suites pass through. */ + for (int i = 0; i < suites.length; i++) { + if (suites[i] != null) { + if (!filterAnon || !suites[i].contains("_anon_")) { + filtered.add(suites[i]); + } + } + } + String enabledSuites = Security.getProperty("wolfjsse.enabledCipherSuites"); List enabledList = null; - /* If system property not set, no filtering needed */ + /* If system property not set, return filtered list */ if (enabledSuites == null || enabledSuites.isEmpty()) { - return suites; + return filtered.toArray(new String[filtered.size()]); } final String tmpSuites = enabledSuites; @@ -168,13 +195,15 @@ protected static String[] sanitizeSuites(String[] suites) { enabledSuites = enabledSuites.replaceAll(", ",","); enabledList = Arrays.asList(enabledSuites.split(",")); - for (int i = 0; i < suites.length; i++) { - if (enabledList.contains(suites[i])) { - filtered.add(suites[i]); + /* Further filter by wolfjsse.enabledCipherSuites property */ + ArrayList propFiltered = new ArrayList(); + for (int i = 0; i < filtered.size(); i++) { + if (enabledList.contains(filtered.get(i))) { + propFiltered.add(filtered.get(i)); } } - return filtered.toArray(new String[filtered.size()]); + return propFiltered.toArray(new String[propFiltered.size()]); } /** diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLContextTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLContextTest.java index 6d1b6043..7f073b2f 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLContextTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLContextTest.java @@ -796,8 +796,19 @@ public void testWolfJSSEEnabledCipherSuites() fail("Invalid TLS version"); } - /* String[] of all available native wolfSSL suites for version */ - nativeSuites = WolfSSL.getCiphersAvailableIana(version); + /* String[] of all available native wolfSSL suites for version, + * filtered to remove anonymous cipher suites to match what + * SSLContext will return (wolfJSSE filters anon suites) */ + String[] rawSuites = WolfSSL.getCiphersAvailableIana(version); + ArrayList nonAnon = new ArrayList(); + if (rawSuites != null) { + for (String s : rawSuites) { + if (s != null && !s.contains("_anon_")) { + nonAnon.add(s); + } + } + } + nativeSuites = nonAnon.toArray(new String[nonAnon.size()]); /* ------------------------------------------------------------- */ @@ -991,12 +1002,57 @@ public void testSanitizeProtocolsNullInput() { return; } - System.out.println("\t\t... passed"); + System.out.println("\t... passed"); } catch (Exception e) { System.out.println("\t... failed"); fail("Exception during sanitizeProtocols test: " + e.getMessage()); } } + + /* Helper: check if array has any anon suite */ + private boolean arrayHasAnonSuite(String[] arr) { + if (arr == null) { + return false; + } + for (String s : arr) { + if (s != null && s.contains("_anon_")) { + return true; + } + } + return false; + } + + @Test + public void testContextDefaultParamsExcludeAnon() throws Exception { + + System.out.print("\tdefault params exclude anon"); + + String[] allCiphers = WolfSSL.getCiphersIana(); + boolean haveAnon = false; + for (String s : allCiphers) { + if (s != null && s.contains("_anon_")) { + haveAnon = true; + break; + } + } + + if (!haveAnon) { + System.out.println("\t... skipped (no anon suites)"); + return; + } + + Security.setProperty("wolfjsse.enabledCipherSuites", ""); + SSLContext ctx = SSLContext.getInstance("TLS", ctxProvider); + ctx.init(null, null, null); + + String[] defaults = + ctx.getDefaultSSLParameters().getCipherSuites(); + assertNotNull(defaults); + assertFalse("Default params should not contain anon", + arrayHasAnonSuite(defaults)); + + System.out.println("\t... passed"); + } } diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java index 1d2cba8e..29cd7755 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java @@ -40,6 +40,8 @@ import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; +import java.util.Arrays; +import java.util.List; import java.util.Random; import java.util.ArrayList; import java.net.Socket; @@ -62,6 +64,7 @@ import java.util.concurrent.CyclicBarrier; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.atomic.AtomicIntegerArray; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import org.junit.Rule; @@ -2557,5 +2560,77 @@ public void testGetPeerCertificateChainNoClientAuth() throws Exception { pass("\t... passed"); } + + @Test + public void testEngineAnonCipherSuiteFiltering() throws Exception { + + System.out.print("\tanon cipher suite filtering"); + + String[] allCiphers = WolfSSL.getCiphersIana(); + String anonSuite = null; + String nonAnonSuite = null; + for (String s : allCiphers) { + if (s == null) continue; + if (s.contains("_anon_") && anonSuite == null) { + anonSuite = s; + } + else if (!s.contains("_anon_") && nonAnonSuite == null) { + nonAnonSuite = s; + } + } + + if (anonSuite == null) { + pass("\t... skipped (no anon suites)"); + return; + } + + Security.setProperty("wolfjsse.enabledCipherSuites", ""); + this.ctx = tf.createSSLContext("TLS", engineProvider); + SSLEngine engine = this.ctx.createSSLEngine(); + + /* Default enabled should not include anon */ + String[] enabled = engine.getEnabledCipherSuites(); + assertNotNull(enabled); + for (String s : enabled) { + if (s != null && s.contains("_anon_")) { + error("\t... failed"); + fail("Default enabled should not contain anon suite: " + s); + } + } + + /* Supported should include anon */ + String[] supported = engine.getSupportedCipherSuites(); + assertNotNull(supported); + boolean foundAnon = false; + for (String s : supported) { + if (s != null && s.contains("_anon_")) { + foundAnon = true; + break; + } + } + assertTrue("Supported should include anon suites", foundAnon); + + /* Supported should be superset of enabled */ + List suppList = Arrays.asList(supported); + for (String s : enabled) { + assertTrue("Enabled suite not in supported: " + s, + suppList.contains(s)); + } + + /* Explicit set of anon suite should succeed */ + engine.setEnabledCipherSuites(new String[] { anonSuite }); + enabled = engine.getEnabledCipherSuites(); + assertNotNull(enabled); + assertEquals(1, enabled.length); + assertEquals(anonSuite, enabled[0]); + + /* Roundtrip: set both anon and non-anon, get both back */ + engine.setEnabledCipherSuites(new String[] { nonAnonSuite, anonSuite }); + enabled = engine.getEnabledCipherSuites(); + assertNotNull(enabled); + assertEquals(2, enabled.length); + + pass("\t... passed"); + } } diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLServerSocketFactoryTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLServerSocketFactoryTest.java index ed493437..61a74582 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLServerSocketFactoryTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLServerSocketFactoryTest.java @@ -25,7 +25,9 @@ import org.junit.BeforeClass; import static org.junit.Assert.*; +import java.util.Arrays; import java.util.ArrayList; +import java.util.List; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -46,6 +48,7 @@ import java.security.KeyManagementException; import java.security.KeyStoreException; +import com.wolfssl.WolfSSL; import com.wolfssl.WolfSSLException; import com.wolfssl.provider.jsse.WolfSSLProvider; @@ -247,5 +250,60 @@ public void testCreateSocket() System.out.println("\t\t\t... passed"); } -} + @Test + public void testServerFactoryAnonCipherSuiteFiltering() + throws Exception { + + System.out.print("\tanon filtering"); + + String[] allCiphers = WolfSSL.getCiphersIana(); + boolean haveAnon = false; + for (String s : allCiphers) { + if (s != null && s.contains("_anon_")) { + haveAnon = true; + break; + } + } + + if (!haveAnon) { + System.out.println("\t\t\t... skipped (no anon suites)"); + return; + } + + Security.setProperty("wolfjsse.enabledCipherSuites", ""); + SSLContext ctx = SSLContext.getInstance("TLS", "wolfJSSE"); + ctx.init(null, null, null); + SSLServerSocketFactory sf = ctx.getServerSocketFactory(); + + /* Default should not include anon */ + String[] defaults = sf.getDefaultCipherSuites(); + assertNotNull(defaults); + for (String s : defaults) { + if (s != null && s.contains("_anon_")) { + fail("Default should not contain anon: " + s); + } + } + + /* Supported should include anon */ + String[] supported = sf.getSupportedCipherSuites(); + assertNotNull(supported); + boolean foundAnon = false; + for (String s : supported) { + if (s != null && s.contains("_anon_")) { + foundAnon = true; + break; + } + } + assertTrue("Supported should include anon suites", foundAnon); + + /* Supported should be superset of default */ + List suppList = Arrays.asList(supported); + for (String s : defaults) { + assertTrue("Default suite not in supported: " + s, + suppList.contains(s)); + } + + System.out.println("\t\t\t... passed"); + } +} diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLServerSocketTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLServerSocketTest.java index 8a0d3ff1..95824a91 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLServerSocketTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLServerSocketTest.java @@ -25,7 +25,9 @@ import org.junit.BeforeClass; import static org.junit.Assert.*; +import java.util.Arrays; import java.util.ArrayList; +import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.Callable; @@ -57,6 +59,7 @@ import java.security.KeyManagementException; import java.security.KeyStoreException; +import com.wolfssl.WolfSSL; import com.wolfssl.WolfSSLException; import com.wolfssl.provider.jsse.WolfSSLProvider; @@ -882,5 +885,81 @@ public Void call() throws Exception { System.out.println("\t... passed"); } + + @Test + public void testServerSocketAnonCipherSuiteFiltering() + throws Exception { + + System.out.print("\tanon cipher filtering"); + + String[] allCiphers = WolfSSL.getCiphersIana(); + String anonSuite = null; + String nonAnonSuite = null; + for (String s : allCiphers) { + if (s == null) continue; + if (s.contains("_anon_") && anonSuite == null) { + anonSuite = s; + } + else if (!s.contains("_anon_") && nonAnonSuite == null) { + nonAnonSuite = s; + } + } + + if (anonSuite == null) { + System.out.println("\t\t... skipped (no anon suites)"); + return; + } + + Security.setProperty("wolfjsse.enabledCipherSuites", ""); + this.ctx = tf.createSSLContext("TLS", "wolfJSSE"); + SSLServerSocket ss = + (SSLServerSocket)ctx.getServerSocketFactory() + .createServerSocket(0); + + /* Default enabled should not include anon */ + String[] enabled = ss.getEnabledCipherSuites(); + assertNotNull(enabled); + for (String s : enabled) { + if (s != null && s.contains("_anon_")) { + ss.close(); + fail("Default enabled should not contain anon: " + s); + } + } + + /* Supported should include anon */ + String[] supported = ss.getSupportedCipherSuites(); + assertNotNull(supported); + boolean foundAnon = false; + for (String s : supported) { + if (s != null && s.contains("_anon_")) { + foundAnon = true; + break; + } + } + assertTrue("Supported should include anon suites", foundAnon); + + /* Supported superset of enabled */ + List suppList = Arrays.asList(supported); + for (String s : enabled) { + assertTrue("Enabled suite not in supported: " + s, + suppList.contains(s)); + } + + /* Explicit set of anon should succeed */ + ss.setEnabledCipherSuites(new String[] { anonSuite }); + enabled = ss.getEnabledCipherSuites(); + assertNotNull(enabled); + assertEquals(1, enabled.length); + assertEquals(anonSuite, enabled[0]); + + /* Roundtrip: set both, get both back */ + ss.setEnabledCipherSuites(new String[] { nonAnonSuite, anonSuite }); + enabled = ss.getEnabledCipherSuites(); + assertEquals(2, enabled.length); + + ss.close(); + + System.out.println("\t\t... passed"); + } } diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketFactoryTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketFactoryTest.java index 88044dc0..f3b4042a 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketFactoryTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketFactoryTest.java @@ -29,6 +29,8 @@ import com.wolfssl.provider.jsse.WolfSSLSocketFactory; +import java.util.Arrays; +import java.util.List; import java.io.FileInputStream; import java.io.InputStream; import javax.net.ssl.SSLSocket; @@ -52,6 +54,7 @@ import java.security.NoSuchProviderException; import java.security.NoSuchAlgorithmException; +import com.wolfssl.WolfSSL; import com.wolfssl.WolfSSLException; import com.wolfssl.provider.jsse.WolfSSLProvider; @@ -438,5 +441,60 @@ public void testCreateSocket() System.out.println("\t\t\t... passed"); } -} + @Test + public void testFactoryAnonCipherSuiteFiltering() + throws Exception { + + System.out.print("\tanon cipher filtering"); + + String[] allCiphers = WolfSSL.getCiphersIana(); + boolean haveAnon = false; + for (String s : allCiphers) { + if (s != null && s.contains("_anon_")) { + haveAnon = true; + break; + } + } + + if (!haveAnon) { + System.out.println("\t\t... skipped (no anon suites)"); + return; + } + + Security.setProperty("wolfjsse.enabledCipherSuites", ""); + SSLContext ctx = SSLContext.getInstance("TLS", ctxProvider); + ctx.init(null, null, null); + SSLSocketFactory sf = ctx.getSocketFactory(); + + /* Default should not include anon */ + String[] defaults = sf.getDefaultCipherSuites(); + assertNotNull(defaults); + for (String s : defaults) { + if (s != null && s.contains("_anon_")) { + fail("Default should not contain anon: " + s); + } + } + + /* Supported should include anon */ + String[] supported = sf.getSupportedCipherSuites(); + assertNotNull(supported); + boolean foundAnon = false; + for (String s : supported) { + if (s != null && s.contains("_anon_")) { + foundAnon = true; + break; + } + } + assertTrue("Supported should include anon suites", foundAnon); + + /* Supported should be superset of default */ + List suppList = Arrays.asList(supported); + for (String s : defaults) { + assertTrue("Default suite not in supported: " + s, + suppList.contains(s)); + } + + System.out.println("\t\t... passed"); + } +} diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java index 463ceef8..56759436 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java @@ -4321,5 +4321,77 @@ public Void call() throws Exception { System.out.println("\t\t... passed"); } + + @Test + public void testSocketAnonCipherSuiteFiltering() throws Exception { + + System.out.print("\tanon cipher suite filtering"); + + String[] allCiphers = WolfSSL.getCiphersIana(); + String anonSuite = null; + String nonAnonSuite = null; + for (String s : allCiphers) { + if (s == null) continue; + if (s.contains("_anon_") && anonSuite == null) { + anonSuite = s; + } + else if (!s.contains("_anon_") && nonAnonSuite == null) { + nonAnonSuite = s; + } + } + + if (anonSuite == null) { + System.out.println("\t... skipped (no anon suites)"); + return; + } + + Security.setProperty("wolfjsse.enabledCipherSuites", ""); + SSLContext ctx = tf.createSSLContext("TLS", "wolfJSSE"); + SSLSocket sock = (SSLSocket)ctx.getSocketFactory().createSocket(); + + /* Default enabled should not include anon */ + String[] enabled = sock.getEnabledCipherSuites(); + assertNotNull(enabled); + for (String s : enabled) { + if (s != null && s.contains("_anon_")) { + fail("Default enabled should not contain anon: " + s); + } + } + + /* Supported should include anon */ + String[] supported = sock.getSupportedCipherSuites(); + assertNotNull(supported); + boolean foundAnon = false; + for (String s : supported) { + if (s != null && s.contains("_anon_")) { + foundAnon = true; + break; + } + } + assertTrue("Supported should include anon suites", foundAnon); + + /* Supported superset of enabled */ + List suppList = Arrays.asList(supported); + for (String s : enabled) { + assertTrue("Enabled suite not in supported: " + s, + suppList.contains(s)); + } + + /* Explicit set of anon should succeed */ + sock.setEnabledCipherSuites(new String[] { anonSuite }); + enabled = sock.getEnabledCipherSuites(); + assertNotNull(enabled); + assertEquals(1, enabled.length); + assertEquals(anonSuite, enabled[0]); + + /* Roundtrip: set both, get both back */ + sock.setEnabledCipherSuites(new String[] { nonAnonSuite, anonSuite }); + enabled = sock.getEnabledCipherSuites(); + assertEquals(2, enabled.length); + + sock.close(); + + System.out.println("\t... passed"); + } } diff --git a/src/test/com/wolfssl/test/WolfSSLTest.java b/src/test/com/wolfssl/test/WolfSSLTest.java index 2efa0c14..a08e87bf 100644 --- a/src/test/com/wolfssl/test/WolfSSLTest.java +++ b/src/test/com/wolfssl/test/WolfSSLTest.java @@ -214,6 +214,32 @@ public void testGetCiphersAvailableIana() { fail("available ciphers array length was zero"); } + /* Test all protocol versions. For each, if a non-null list is returned + * it must not be empty and must not contain empty strings. A null + * return is acceptable for protocol versions not compiled into native + * wolfSSL. */ + for (WolfSSL.TLS_VERSION ver : WolfSSL.TLS_VERSION.values()) { + if (ver == WolfSSL.TLS_VERSION.INVALID) { + continue; + } + String[] verCiphers = WolfSSL.getCiphersAvailableIana(ver); + if (verCiphers != null) { + if (verCiphers.length == 0) { + System.out.println("\t... failed"); + fail("getCiphersAvailableIana(" + ver + + ") returned empty array"); + } + for (int i = 0; i < verCiphers.length; i++) { + if (verCiphers[i] == null || + verCiphers[i].isEmpty()) { + System.out.println("\t... failed"); + fail("getCiphersAvailableIana(" + ver + + ") contains null/empty cipher at index " + i); + } + } + } + } + System.out.println("\t... passed"); }