From 3bb66a2ab594aec3ace90f7b2a9b7debdac243ec Mon Sep 17 00:00:00 2001 From: Valerie Peng Date: Fri, 12 Jun 2020 02:34:44 +0000 Subject: [PATCH 1/2] 8246613: Choose the default SecureRandom algo based on registration ordering Fixed java.security.Provider and SecureRandom to use the 1st registered SecureRandom service Reviewed-by: weijun, mullan --- .../share/classes/java/security/Provider.java | 148 +++++++++++++----- .../classes/java/security/SecureRandom.java | 79 ++++------ .../security/SecureRandom/DefaultAlgo.java | 95 +++++++++-- 3 files changed, 220 insertions(+), 102 deletions(-) diff --git a/src/java.base/share/classes/java/security/Provider.java b/src/java.base/share/classes/java/security/Provider.java index d13d77c07d..ceabd44057 100644 --- a/src/java.base/share/classes/java/security/Provider.java +++ b/src/java.base/share/classes/java/security/Provider.java @@ -858,10 +858,18 @@ public abstract class Provider extends Properties { // serviceMap changed since last call to getServices() private volatile transient boolean servicesChanged; + // Map used to keep track of legacy registration + private transient Map legacyStrings; + // Map // used for services added via putService(), initialized on demand private transient Map serviceMap; + // For backward compatibility, the registration ordering of + // SecureRandom (RNG) algorithms needs to be preserved for + // "new SecureRandom()" calls when this provider is used + private transient Set prngServices; + // Map // used for services added via legacy methods, init on demand private transient Map legacyMap; @@ -913,12 +921,18 @@ public abstract class Provider extends Properties { putAll(copy); } - private static boolean isProviderInfo(Object key) { + // check whether to update 'legacyString' with the specified key + private boolean checkLegacy(Object key) { String keyString = (String)key; if (keyString.startsWith("Provider.")) { - return true; + return false; } - return false; + + legacyChanged = true; + if (legacyStrings == null) { + legacyStrings = new LinkedHashMap<>(); + } + return true; } /** @@ -934,20 +948,20 @@ public abstract class Provider extends Properties { private Object implRemove(Object key) { if (key instanceof String) { - if (isProviderInfo(key)) { + if (!checkLegacy(key)) { return null; } - legacyChanged = true; + legacyStrings.remove((String)key); } return super.remove(key); } private boolean implRemove(Object key, Object value) { if (key instanceof String && value instanceof String) { - if (isProviderInfo(key)) { + if (!checkLegacy(key)) { return false; } - legacyChanged = true; + legacyStrings.remove((String)key, (String)value); } return super.remove(key, value); } @@ -955,20 +969,21 @@ public abstract class Provider extends Properties { private boolean implReplace(Object key, Object oldValue, Object newValue) { if ((key instanceof String) && (oldValue instanceof String) && (newValue instanceof String)) { - if (isProviderInfo(key)) { + if (!checkLegacy(key)) { return false; } - legacyChanged = true; + legacyStrings.replace((String)key, (String)oldValue, + (String)newValue); } return super.replace(key, oldValue, newValue); } private Object implReplace(Object key, Object value) { if ((key instanceof String) && (value instanceof String)) { - if (isProviderInfo(key)) { + if (!checkLegacy(key)) { return null; } - legacyChanged = true; + legacyStrings.replace((String)key, (String)value); } return super.replace(key, value); } @@ -977,17 +992,26 @@ public abstract class Provider extends Properties { private void implReplaceAll(BiFunction function) { legacyChanged = true; + if (legacyStrings == null) { + legacyStrings = new LinkedHashMap<>(); + } else { + legacyStrings.replaceAll((BiFunction) function); + } super.replaceAll(function); } @SuppressWarnings("unchecked") // Function must actually operate over strings - private Object implMerge(Object key, Object value, BiFunction remappingFunction) { + private Object implMerge(Object key, Object value, + BiFunction + remappingFunction) { if ((key instanceof String) && (value instanceof String)) { - if (isProviderInfo(key)) { + if (!checkLegacy(key)) { return null; } - legacyChanged = true; + legacyStrings.merge((String)key, (String)value, + (BiFunction) remappingFunction); } return super.merge(key, value, remappingFunction); } @@ -996,10 +1020,12 @@ public abstract class Provider extends Properties { private Object implCompute(Object key, BiFunction remappingFunction) { if (key instanceof String) { - if (isProviderInfo(key)) { + if (!checkLegacy(key)) { return null; } - legacyChanged = true; + legacyStrings.compute((String) key, + (BiFunction) remappingFunction); } return super.compute(key, remappingFunction); } @@ -1008,10 +1034,12 @@ public abstract class Provider extends Properties { private Object implComputeIfAbsent(Object key, Function mappingFunction) { if (key instanceof String) { - if (isProviderInfo(key)) { + if (!checkLegacy(key)) { return null; } - legacyChanged = true; + legacyStrings.computeIfAbsent((String) key, + (Function) + mappingFunction); } return super.computeIfAbsent(key, mappingFunction); } @@ -1020,35 +1048,40 @@ public abstract class Provider extends Properties { private Object implComputeIfPresent(Object key, BiFunction remappingFunction) { if (key instanceof String) { - if (isProviderInfo(key)) { + if (!checkLegacy(key)) { return null; } - legacyChanged = true; + legacyStrings.computeIfPresent((String) key, + (BiFunction) remappingFunction); } return super.computeIfPresent(key, remappingFunction); } private Object implPut(Object key, Object value) { if ((key instanceof String) && (value instanceof String)) { - if (isProviderInfo(key)) { + if (!checkLegacy(key)) { return null; } - legacyChanged = true; + legacyStrings.put((String)key, (String)value); } return super.put(key, value); } private Object implPutIfAbsent(Object key, Object value) { if ((key instanceof String) && (value instanceof String)) { - if (isProviderInfo(key)) { + if (!checkLegacy(key)) { return null; } - legacyChanged = true; + legacyStrings.putIfAbsent((String)key, (String)value); } return super.putIfAbsent(key, value); } private void implClear() { + if (legacyStrings != null) { + legacyStrings.clear(); + } if (legacyMap != null) { legacyMap.clear(); } @@ -1056,6 +1089,7 @@ public abstract class Provider extends Properties { legacyChanged = false; servicesChanged = false; serviceSet = null; + prngServices = null; super.clear(); putId(); } @@ -1095,7 +1129,7 @@ public abstract class Provider extends Properties { * service objects. */ private void ensureLegacyParsed() { - if (legacyChanged == false) { + if (legacyChanged == false || (legacyStrings == null)) { return; } serviceSet = null; @@ -1104,7 +1138,7 @@ public abstract class Provider extends Properties { } else { legacyMap.clear(); } - for (Map.Entry entry : super.entrySet()) { + for (Map.Entry entry : legacyStrings.entrySet()) { parseLegacyPut(entry.getKey(), entry.getValue()); } removeInvalidServices(legacyMap); @@ -1125,12 +1159,12 @@ public abstract class Provider extends Properties { } } - private String[] getTypeAndAlgorithm(String key) { + private static String[] getTypeAndAlgorithm(String key) { int i = key.indexOf('.'); if (i < 1) { if (debug != null) { - debug.println("Ignoring invalid entry in provider " - + name + ":" + key); + debug.println("Ignoring invalid entry in provider: " + + key); } return null; } @@ -1143,15 +1177,7 @@ public abstract class Provider extends Properties { private static final String ALIAS_PREFIX_LOWER = "alg.alias."; private static final int ALIAS_LENGTH = ALIAS_PREFIX.length(); - private void parseLegacyPut(Object k, Object v) { - if (!(k instanceof String) || !(v instanceof String)) { - return; - } - String name = (String) k; - String value = (String) v; - if (isProviderInfo(name)) { - return; - } + private void parseLegacyPut(String name, String value) { if (name.toLowerCase(ENGLISH).startsWith(ALIAS_PREFIX_LOWER)) { // e.g. put("Alg.Alias.MessageDigest.SHA", "SHA-1"); // aliasKey ~ MessageDigest.SHA @@ -1193,6 +1219,10 @@ public abstract class Provider extends Properties { legacyMap.put(key, s); } s.className = className; + + if (type.equals("SecureRandom")) { + updateSecureRandomEntries(true, s); + } } else { // attribute // e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software"); String attributeValue = value; @@ -1352,9 +1382,46 @@ public abstract class Provider extends Properties { servicesChanged = true; synchronized (this) { putPropertyStrings(s); + if (type.equals("SecureRandom")) { + updateSecureRandomEntries(true, s); + } } } + private void updateSecureRandomEntries(boolean doAdd, Service s) { + Objects.requireNonNull(s); + if (doAdd) { + if (prngServices == null) { + prngServices = new LinkedHashSet(); + } + prngServices.add(s); + } else { + prngServices.remove(s); + } + + if (debug != null) { + debug.println((doAdd? "Add":"Remove") + " SecureRandom algo " + + s.getAlgorithm()); + } + } + + // used by new SecureRandom() to find out the default SecureRandom + // service for this provider + synchronized Service getDefaultSecureRandomService() { + checkInitialized(); + + if (legacyChanged) { + prngServices = null; + ensureLegacyParsed(); + } + + if (prngServices != null && !prngServices.isEmpty()) { + return prngServices.iterator().next(); + } + + return null; + } + /** * Put the string properties for this Service in this Provider's * Hashtable. @@ -1448,6 +1515,9 @@ public abstract class Provider extends Properties { } synchronized (this) { removePropertyStrings(s); + if (type.equals("SecureRandom")) { + updateSecureRandomEntries(false, s); + } } } diff --git a/src/java.base/share/classes/java/security/SecureRandom.java b/src/java.base/share/classes/java/security/SecureRandom.java index a454d47dd1..5a9ce618de 100644 --- a/src/java.base/share/classes/java/security/SecureRandom.java +++ b/src/java.base/share/classes/java/security/SecureRandom.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -259,35 +259,51 @@ public class SecureRandom extends java.util.Random { } private void getDefaultPRNG(boolean setSeed, byte[] seed) { - String prng = getPrngAlgorithm(); - if (prng == null) { - // bummer, get the SUN implementation - prng = "SHA1PRNG"; + Service prngService = null; + String prngAlgorithm = null; + for (Provider p : Providers.getProviderList().providers()) { + // SUN provider uses the SunEntries.DEF_SECURE_RANDOM_ALGO + // as the default SecureRandom algorithm; for other providers, + // Provider.getDefaultSecureRandom() will use the 1st + // registered SecureRandom algorithm + if (p.getName().equals("SUN")) { + prngAlgorithm = SunEntries.DEF_SECURE_RANDOM_ALGO; + prngService = p.getService("SecureRandom", prngAlgorithm); + break; + } else { + prngService = p.getDefaultSecureRandomService(); + if (prngService != null) { + prngAlgorithm = prngService.getAlgorithm(); + break; + } + } + } + // per javadoc, if none of the Providers support a RNG algorithm, + // then an implementation-specific default is returned. + if (prngService == null) { + prngAlgorithm = "SHA1PRNG"; this.secureRandomSpi = new sun.security.provider.SecureRandom(); this.provider = Providers.getSunProvider(); - if (setSeed) { - this.secureRandomSpi.engineSetSeed(seed); - } } else { try { - SecureRandom random = SecureRandom.getInstance(prng); - this.secureRandomSpi = random.getSecureRandomSpi(); - this.provider = random.getProvider(); - if (setSeed) { - this.secureRandomSpi.engineSetSeed(seed); - } + this.secureRandomSpi = (SecureRandomSpi) + prngService.newInstance(null); + this.provider = prngService.getProvider(); } catch (NoSuchAlgorithmException nsae) { - // never happens, because we made sure the algorithm exists + // should not happen throw new RuntimeException(nsae); } } + if (setSeed) { + this.secureRandomSpi.engineSetSeed(seed); + } // JDK 1.1 based implementations subclass SecureRandom instead of // SecureRandomSpi. They will also go through this code path because // they must call a SecureRandom constructor as it is their superclass. // If we are dealing with such an implementation, do not set the // algorithm value as it would be inaccurate. if (getClass() == SecureRandom.class) { - this.algorithm = prng; + this.algorithm = prngAlgorithm; } } @@ -620,13 +636,6 @@ public class SecureRandom extends java.util.Random { instance.provider, algorithm); } - /** - * Returns the {@code SecureRandomSpi} of this {@code SecureRandom} object. - */ - SecureRandomSpi getSecureRandomSpi() { - return secureRandomSpi; - } - /** * Returns the provider of this {@code SecureRandom} object. * @@ -868,30 +877,6 @@ public class SecureRandom extends java.util.Random { return retVal; } - /** - * Gets a default PRNG algorithm by looking through all registered - * providers. Returns the first PRNG algorithm of the first provider that - * has registered a {@code SecureRandom} implementation, or null if none of - * the registered providers supplies a {@code SecureRandom} implementation. - */ - private static String getPrngAlgorithm() { - for (Provider p : Providers.getProviderList().providers()) { - // For SUN provider, we use SunEntries.DEFF_SECURE_RANDOM_ALGO - // as the default SecureRandom algorithm; for other providers, - // we continue to iterate through to the 1st SecureRandom - // service - if (p.getName().equals("SUN")) { - return SunEntries.DEF_SECURE_RANDOM_ALGO; - } - for (Service s : p.getServices()) { - if (s.getType().equals("SecureRandom")) { - return s.getAlgorithm(); - } - } - } - return null; - } - /* * Lazily initialize since Pattern.compile() is heavy. * Effective Java (2nd Edition), Item 71. diff --git a/test/jdk/java/security/SecureRandom/DefaultAlgo.java b/test/jdk/java/security/SecureRandom/DefaultAlgo.java index d29aa87b20..d85d73b2cb 100644 --- a/test/jdk/java/security/SecureRandom/DefaultAlgo.java +++ b/test/jdk/java/security/SecureRandom/DefaultAlgo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,33 +22,96 @@ */ import static java.lang.System.out; +import java.security.Provider; +import java.security.Security; import java.security.SecureRandom; +import java.security.Provider.Service; +import java.util.Objects; +import java.util.Arrays; import sun.security.provider.SunEntries; /** * @test - * @bug 8228613 - * @summary Ensure that the default SecureRandom algo matches - * SunEntries.DEF_SECURE_RANDOM_ALGO when SUN provider is used + * @bug 8228613 8246613 + * @summary Ensure that the default SecureRandom algo used is based + * on the registration ordering, and falls to next provider + * if none are found * @modules java.base/sun.security.provider */ public class DefaultAlgo { public static void main(String[] args) throws Exception { - SecureRandom sr = new SecureRandom(); - String actualAlg = sr.getAlgorithm(); - out.println("Default SecureRandom algo: " + actualAlg); - if (sr.getProvider().getName().equals("SUN")) { - // when using Sun provider, compare and check if the algorithm - // matches SunEntries.DEF_SECURE_RANDOM_ALGO - if (actualAlg.equals(SunEntries.DEF_SECURE_RANDOM_ALGO)) { - out.println("Test Passed"); - } else { - throw new RuntimeException("Failed: Expected " + + String[] algos = { "A", "B", "C" }; + test3rdParty(algos); + // reverse the order and re-check + String[] algosReversed = { "C", "B", "A" }; + test3rdParty(algosReversed); + } + + private static void test3rdParty(String[] algos) { + Provider[] provs = { + new SampleLegacyProvider(algos), + new SampleServiceProvider(algos) + }; + for (Provider p : provs) { + checkDefault(p, algos); + } + } + + // validate the specified SecureRandom obj to be from the specified + // provider and matches the specified algorithm + private static void validate(SecureRandom sr, String pName, String algo) { + if (!sr.getProvider().getName().equals(pName)) { + throw new RuntimeException("Failed provider check, exp: " + + pName + ", got " + sr.getProvider().getName()); + } + if (!sr.getAlgorithm().equals(algo)) { + throw new RuntimeException("Failed algo check, exp: " + + algo + ", got " + sr.getAlgorithm()); + } + } + + private static void checkDefault(Provider p, String ... algos) { + out.println(p.getName() + " with " + Arrays.toString(algos)); + int pos = Security.insertProviderAt(p, 1); + String pName = p.getName(); + boolean isLegacy = pName.equals("SampleLegacy"); + try { + if (isLegacy) { + for (String s : algos) { + validate(new SecureRandom(), pName, s); + p.remove("SecureRandom." + s); + out.println("removed " + s); + } + validate(new SecureRandom(), "SUN", SunEntries.DEF_SECURE_RANDOM_ALGO); + } else { + validate(new SecureRandom(), pName, algos[0]); + } + out.println("=> Test Passed"); + } finally { + if (pos != -1) { + Security.removeProvider(p.getName()); + } + } + } + + private static class SampleLegacyProvider extends Provider { + SampleLegacyProvider(String[] listOfSupportedRNGs) { + super("SampleLegacy", "1.0", "test provider using legacy put"); + for (String s : listOfSupportedRNGs) { + put("SecureRandom." + s, "sun.security.provider.SecureRandom"); + } + } + } + + private static class SampleServiceProvider extends Provider { + SampleServiceProvider(String[] listOfSupportedRNGs) { + super("SampleService", "1.0", "test provider using putService"); + for (String s : listOfSupportedRNGs) { + putService(new Provider.Service(this, "SecureRandom", s, + "sun.security.provider.SecureRandom", null, null)); } - } else { - out.println("Skip test for non-Sun provider: " + sr.getProvider()); } } } From e23aaedacfdef53d097250c206f5c7f2babdcc7f Mon Sep 17 00:00:00 2001 From: Prasadrao Koppula Date: Thu, 11 Jun 2020 21:54:51 +0530 Subject: [PATCH 2/2] 8246031: SSLSocket.getSession() doesn't close connection for timeout/ interrupts Reviewed-by: xuelei, coffeys --- .../sun/security/ssl/SSLSocketImpl.java | 21 +- .../ssl/SSLSocketImpl/SetSoTimeout.java | 252 ++++++++++++++++++ 2 files changed, 267 insertions(+), 6 deletions(-) create mode 100644 test/jdk/sun/security/ssl/SSLSocketImpl/SetSoTimeout.java diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java index 74a55806a1..c8229ab77c 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java @@ -354,7 +354,7 @@ public final class SSLSocketImpl public SSLSession getSession() { try { // start handshaking, if failed, the connection will be closed. - ensureNegotiated(); + ensureNegotiated(false); } catch (IOException ioe) { if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { SSLLogger.severe("handshake failed", ioe); @@ -409,6 +409,10 @@ public final class SSLSocketImpl @Override public void startHandshake() throws IOException { + startHandshake(true); + } + + private void startHandshake(boolean resumable) throws IOException { if (!isConnected) { throw new SocketException("Socket is not connected"); } @@ -437,7 +441,12 @@ public final class SSLSocketImpl readHandshakeRecord(); } } catch (InterruptedIOException iioe) { - handleException(iioe); + if(resumable){ + handleException(iioe); + } else{ + throw conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Couldn't kickstart handshaking", iioe); + } } catch (IOException ioe) { throw conContext.fatal(Alert.HANDSHAKE_FAILURE, "Couldn't kickstart handshaking", ioe); @@ -862,7 +871,7 @@ public final class SSLSocketImpl } } - private void ensureNegotiated() throws IOException { + private void ensureNegotiated(boolean resumable) throws IOException { if (conContext.isNegotiated || conContext.isBroken || conContext.isInboundClosed() || conContext.isOutboundClosed()) { return; @@ -877,7 +886,7 @@ public final class SSLSocketImpl return; } - startHandshake(); + startHandshake(resumable); } finally { handshakeLock.unlock(); } @@ -968,7 +977,7 @@ public final class SSLSocketImpl if (!conContext.isNegotiated && !conContext.isBroken && !conContext.isInboundClosed() && !conContext.isOutboundClosed()) { - ensureNegotiated(); + ensureNegotiated(true); } // Check if the Socket is invalid (error or closed). @@ -1239,7 +1248,7 @@ public final class SSLSocketImpl if (!conContext.isNegotiated && !conContext.isBroken && !conContext.isInboundClosed() && !conContext.isOutboundClosed()) { - ensureNegotiated(); + ensureNegotiated(true); } // Check if the Socket is invalid (error or closed). diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/SetSoTimeout.java b/test/jdk/sun/security/ssl/SSLSocketImpl/SetSoTimeout.java new file mode 100644 index 0000000000..148a7712a9 --- /dev/null +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/SetSoTimeout.java @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. + +/* + * @test + * @bug 8246031 + * @summary Socket timeouts for getSession. + * @run main/othervm SetSoTimeout + */ + +import java.io.*; +import java.net.*; +import java.util.*; +import java.security.*; +import javax.net.ssl.*; + +public class SetSoTimeout { + + /* + * ============================================================= + * Set the various variables needed for the tests, then + * specify what tests to run on each side. + */ + + /* + * Should we run the client or server in a separate thread? + * Both sides can throw exceptions, but do you have a preference + * as to which side should be the main thread. + */ + static boolean separateServerThread = true; + + /* + * Where do we find the keystores? + */ + static String pathToStores = "../../../../javax/net/ssl/etc"; + static String keyStoreFile = "keystore"; + static String trustStoreFile = "truststore"; + static String passwd = "passphrase"; + + /* + * Is the server ready to serve? + */ + volatile static boolean serverReady = false; + + /* + * Turn on SSL debugging? + */ + static boolean debug = false; + + + /* + * Define the server side of the test. + * + * If the server prematurely exits, serverReady will be set to true + * to avoid infinite hangs. + */ + void doServerSide() throws Exception { + SSLServerSocketFactory sslssf = + (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + SSLServerSocket sslServerSocket = + (SSLServerSocket) sslssf.createServerSocket(serverPort); + + serverPort = sslServerSocket.getLocalPort(); + + /* + * Signal Client, we're ready for his connect. + */ + serverReady = true; + + SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); + + //Make server busy. + Thread.sleep(500); + + sslSocket.close(); + } + + /* + * Define the client side of the test. + * + * If the server prematurely exits, serverReady will be set to true + * to avoid infinite hangs. + */ + void doClientSide() throws Exception { + boolean caught = false; + + /* + * Wait for server to get started. + */ + while (!serverReady) { + Thread.sleep(50); + } + + SSLSocketFactory sslsf = (SSLSocketFactory)SSLSocketFactory.getDefault(); + SSLSocket sslSocket = (SSLSocket)sslsf.createSocket( + InetAddress.getLocalHost(), + serverPort ); + + sslSocket.setSoTimeout(100); // The stall timeout. + + sslSocket.getSession(); + //Check connection state. + if (!sslSocket.isClosed()) { + throw new RuntimeException("Expected connection to be closed"); + } + + sslSocket.close(); + + } + + + // use any free port by default + volatile int serverPort = 0; + + volatile Exception serverException = null; + volatile Exception clientException = null; + + + public static void main(String[] args) throws Exception { + String keyFilename = + System.getProperty("test.src", "./") + "/" + pathToStores + + "/" + keyStoreFile; + String trustFilename = + System.getProperty("test.src", "./") + "/" + pathToStores + + "/" + trustStoreFile; + + System.setProperty("javax.net.ssl.keyStore", keyFilename); + System.setProperty("javax.net.ssl.keyStorePassword", passwd); + System.setProperty("javax.net.ssl.trustStore", trustFilename); + System.setProperty("javax.net.ssl.trustStorePassword", passwd); + + if (debug) + System.setProperty("javax.net.debug", "all"); + + /* + * Start the tests. + */ + new SetSoTimeout(); + } + + Thread clientThread = null; + Thread serverThread = null; + + /* + * Primary constructor, used to drive remainder of the test. + * + * Fork off the other side, then do your work. + */ + SetSoTimeout() throws Exception { + if (separateServerThread) { + startServer(true); + startClient(false); + } else { + startClient(true); + startServer(false); + } + + /* + * Wait for other side to close down. + */ + if (separateServerThread) { + serverThread.join(); + } else { + clientThread.join(); + } + + /* + * When we get here, the test is pretty much over. + * + * If the main thread excepted, that propagates back + * immediately. If the other thread threw an exception, we + * should report back. + */ + if (serverException != null) { + System.out.print("Server Exception:"); + throw serverException; + } + if (clientException != null) { + System.out.print("Client Exception:"); + throw clientException; + } + } + + void startServer(boolean newThread) throws Exception { + if (newThread) { + serverThread = new Thread() { + public void run() { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + System.err.println("Server died..."); + System.err.println(e); + serverReady = true; + serverException = e; + } + } + }; + serverThread.start(); + } else { + doServerSide(); + } + } + + void startClient(boolean newThread) throws Exception { + if (newThread) { + clientThread = new Thread() { + public void run() { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + System.err.println("Client died..."); + clientException = e; + } + } + }; + clientThread.start(); + } else { + doClientSide(); + } + } +}