ADDITIONAL SYSTEM INFORMATION :
Mac OS 10.11.6
A DESCRIPTION OF THE PROBLEM :
I have a simple program which creates an SSL-protected connection between a client thread and a server thread. The program succeeds on Java 9 and earlier. However, the program fails with the following error when run on Java 11:
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
REGRESSION : Last worked in version 10
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
To reproduce the issue, download the sslHandshake.tar file attached to https://issues.apache.org/jira/browse/DERBY-6998, unpack the tarball, and follow the instructions in its README file.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
[java] Startup args = [startServer, startClient]
[java] ServerThread starting...
[java] Host address = localhost/127.0.0.1
[java] ClientThread starting...
[java] Host address = localhost/127.0.0.1
[java] ServerThread successfully accepted connection request 0
[java] ClientThread initiating handshake...
[java] Hooray! Successfully shook the server's hand!
[java] ClientThread exiting...
[java] ServerThread read 'Hello, world!' from the socket!
[java] ServerThread exiting...
ACTUAL -
[java] Startup args = [startServer, startClient]
[java] ServerThread starting...
[java] Host address = localhost/127.0.0.1
[java] ClientThread starting...
[java] Host address = localhost/127.0.0.1
[java] ServerThread successfully accepted connection request 0
[java] ClientThread initiating handshake...
[java] Oops! Handshake failed!
[java] Oops! Socket failed!javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
[java]
[java] ClientThread exiting...
[java] at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:128)
[java] ServerThread exiting...
[java] at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)
[java] at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:308)
[java] at java.base/sun.security.ssl.Alert$AlertConsumer.consume(Alert.java:279)
[java] at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:181)
[java] at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:164)
[java] at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1152)
[java] at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1063)
[java] at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:402)
[java] at tests.Test$ClientThread.run(Test.java:141)
[java] javax.net.ssl.SSLHandshakeException: No available authentication scheme
[java] at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:128)
[java] at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)
[java] at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:308)
[java] at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:264)
[java] at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:255)
[java] at java.base/sun.security.ssl.CertificateMessage$T13CertificateProducer.onProduceCertificate(CertificateMessage.java:945)
[java] at java.base/sun.security.ssl.CertificateMessage$T13CertificateProducer.produce(CertificateMessage.java:934)
[java] at java.base/sun.security.ssl.SSLHandshake.produce(SSLHandshake.java:436)
[java] at java.base/sun.security.ssl.ClientHello$T13ClientHelloConsumer.goServerHello(ClientHello.java:1189)
[java] at java.base/sun.security.ssl.ClientHello$T13ClientHelloConsumer.consume(ClientHello.java:1125)
[java] at java.base/sun.security.ssl.ClientHello$ClientHelloConsumer.onClientHello(ClientHello.java:831)
[java] at java.base/sun.security.ssl.ClientHello$ClientHelloConsumer.consume(ClientHello.java:792)
[java] at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
[java] at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444)
[java] at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421)
[java] at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:178)
[java] at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:164)
[java] at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1152)
[java] at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1063)
[java] at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:402)
[java] at java.base/sun.security.ssl.SSLSocketImpl.ensureNegotiated(SSLSocketImpl.java:716)
[java] at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:799)
[java] at tests.Test$ServerThread.run(Test.java:105)
---------- BEGIN SOURCE ----------
package tests;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Properties;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import javax.net.SocketFactory;
import java.net.UnknownHostException;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
public class Test
{
public static final String SSL_KEYSTORE = "javax.net.ssl.keyStore";
public static final String KEYSTORE_LOCATION = "keystore/SSLTestServerKey.key";
public static final String SSL_KEYSTORE_PASSWORD = "javax.net.ssl.keyStorePassword";
public static final String KEYSTORE_PASSWORD = "qwerty";
public static final String HOST_NAME = "localhost";
public static final int PORT_NUMBER = 8246;
public static final long SLEEP_TIME = 1000L;
public static final int SOCKET_TIMEOUT = 20_000; // 20 seconds
public static final long TRACE_WAIT = 2000L;
public static final int MAX_CONNECTIONS = 1;
private static final byte[] HELLO_WORLD = "Hello, world!".getBytes();
public static final int READ_BUFFER_SIZE = HELLO_WORLD.length;
private static AtomicBoolean _serverIsRunning = new AtomicBoolean();
public static void main(String... args) throws Exception
{
String rawArg0 = args[0].toLowerCase();
String rawArg1 = args[1].toLowerCase();
println("Startup args = " + stringify(args));
boolean startServer = rawArg0.equals("startserver");
boolean startClient = rawArg1.equals("startclient");
Thread serverThread = startServer ? new ServerThread() : null;
Thread clientThread = startClient ? new ClientThread() : null;
if (startServer)
{
serverThread.start();
while(!_serverIsRunning.get()) { Thread.sleep(SLEEP_TIME); }
}
if (startClient)
{
clientThread.start();
clientThread.join();
}
if (startServer) { serverThread.join(); }
Thread.sleep(TRACE_WAIT);
}
public static class ServerThread extends Thread
{
public String name() { return "ServerThread"; }
public void run()
{
println(name() + " starting...");
try
{
SSLServerSocket serverSocket = createServerSocket();
serverSocket.setSoTimeout(SOCKET_TIMEOUT);
_serverIsRunning.set(true);
for (int connectionCount = 0; connectionCount < MAX_CONNECTIONS; connectionCount++)
{
SSLSocket clientSocket = (SSLSocket) serverSocket.accept();
println
(
name() + " successfully accepted connection request " + connectionCount
);
InputStream socketInputStream = clientSocket.getInputStream();
byte[] readBuffer = new byte[READ_BUFFER_SIZE];
int bytesRead = socketInputStream.read(readBuffer, 0, READ_BUFFER_SIZE);
println(name() + " read '" + (new String(readBuffer)) + "' from the socket!");
}
}
catch (Exception ex)
{
println("Oops! Socket failed!");
ex.printStackTrace();
}
println(name() + " exiting...");
}
}
public static class ClientThread extends Thread
{
public String name() { return "ClientThread"; }
public void run()
{
println(name() + " starting...");
try
{
Properties sslProperties = getSSLProperties();
SSLSocket s1 = (SSLSocket) NaiveTrustManager
.getSocketFactory(sslProperties)
.createSocket(host(), PORT_NUMBER);
String[] removeTwoProtocols = removeSSLv3andSSLv2Hello(s1.getEnabledProtocols());
s1.setEnabledProtocols(removeTwoProtocols);
Thread.sleep(SLEEP_TIME);
println(name() + " initiating handshake...");
s1.setSoTimeout(SOCKET_TIMEOUT);
s1.startHandshake();
println("Hooray! Successfully shook the server's hand!");
s1.getOutputStream().write(HELLO_WORLD);
}
catch (Exception ex)
{
println("Oops! Handshake failed!");
ex.printStackTrace();
}
println(name() + " exiting...");
}
}
public static class NaiveTrustManager
implements X509TrustManager
{
/**
* We don't want more than one instence of this TrustManager
*/
private NaiveTrustManager()
{
}
static private TrustManager[] thisManager = null;
/**
* Generate a socket factory with this trust manager. Derby
* Utility routine which is not part of the X509TrustManager
* interface.
*
* @param sslProperties Configuration settings for the socket factory
*
* @return a socket factory
*
* @throws java.security.NoSuchAlgorithmException on error
* @throws java.security.KeyManagementException on error
* @throws java.security.NoSuchProviderException on error
* @throws java.security.KeyStoreException on error
* @throws java.security.UnrecoverableKeyException on error
* @throws java.security.cert.CertificateException on error
* @throws java.io.IOException on error
**/
public static SocketFactory getSocketFactory(Properties sslProperties)
throws java.security.NoSuchAlgorithmException,
java.security.KeyManagementException,
java.security.NoSuchProviderException,
java.security.KeyStoreException,
java.security.UnrecoverableKeyException,
java.security.cert.CertificateException,
java.io.IOException
{
if (thisManager == null)
{
thisManager = new TrustManager [] {new NaiveTrustManager()};
}
SSLContext ctx = SSLContext.getInstance("TLS");
String keyStore = sslProperties.getProperty(SSL_KEYSTORE);
String keyStorePassword = sslProperties.getProperty(SSL_KEYSTORE_PASSWORD);
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(keyStore), keyStorePassword.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509", "SunJSSE");
kmf.init(ks, keyStorePassword.toCharArray());
ctx.init(kmf.getKeyManagers(), thisManager, null); // Use default random source
return ctx.getSocketFactory();
}
/**
* Checks whether the we trust the client. Since this trust manager
* is just for the Derby clients, this routine is actually never
* called, but need to be here when we implement X509TrustManager.
* @param chain The client's certificate chain
* @param authType authorization type (e.g. "RSA" or "DHE_DSS")
**/
public void checkClientTrusted(X509Certificate[] chain,
String authType)
throws CertificateException
{
// Reject all attemtpts to trust a client. We should never end
// up here.
throw new CertificateException();
}
/**
* Checks wether the we trust the server, which we allways will.
* @param chain The server's certificate chain
* @param authType authorization type (e.g. "RSA" or "DHE_DSS")
**/
public void checkServerTrusted(X509Certificate[] chain,
String authType)
throws CertificateException
{
// Do nothing. We trust everyone.
}
/**
* Return an array of certificate authority certificates which are
* trusted for authenticating peers. Not relevant for this trust
* manager.
*/
public X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[0];
}
}
private static SSLServerSocket createServerSocket()
throws IOException
{
SSLServerSocketFactory ssf =
(SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
SSLServerSocket sss1=
(SSLServerSocket)ssf.createServerSocket(PORT_NUMBER, 0, host());
String[] removeTwoProtocols = removeSSLv3andSSLv2Hello(sss1.getEnabledProtocols());
sss1.setEnabledProtocols(removeTwoProtocols);
return sss1;
}
private static Properties getSSLProperties()
{
Properties retval = new Properties();
retval.setProperty(SSL_KEYSTORE, System.getProperty("user.dir") + "/" + KEYSTORE_LOCATION);
retval.setProperty(SSL_KEYSTORE_PASSWORD, KEYSTORE_PASSWORD);
return retval;
}
private static InetAddress host()
throws UnknownHostException
{
InetAddress retval = InetAddress.getByName(HOST_NAME);
println("Host address = " + retval);
return retval;
}
//Remove SSLv3 and SSLv2Hello protocols from list of enabled protocols
private static String[] removeSSLv3andSSLv2Hello(String[] enabledProtocols) {
//If SSLv3 and SSLv2Hello are one of the enabled protocols, then
// remove them from the list of enabled protocols because of the
// possible security breach.
String[] supportedProtocols = new String[enabledProtocols.length];
int supportedProtocolsCount = 0;
for ( int i = 0; i < enabledProtocols.length; i++ )
{
if (!(enabledProtocols[i].toUpperCase().contains("SSLV3") ||
enabledProtocols[i].toUpperCase().contains("SSLV2HELLO"))) {
supportedProtocols[supportedProtocolsCount] = enabledProtocols[i];
supportedProtocolsCount++;
}
}
if(supportedProtocolsCount < enabledProtocols.length) {
//We found SSLv3 and/or SSLv2Hello as one of the enabled
// protocols for this jvm. Following code will remove them from
// enabled list.
String[] newEnabledProtocolsList = null;
newEnabledProtocolsList =
new String[supportedProtocolsCount];
System.arraycopy(supportedProtocols, 0,
newEnabledProtocolsList, 0,
supportedProtocolsCount);
return(newEnabledProtocolsList);
} else
return(enabledProtocols);
}
private static String stringify(String[] array) { return Arrays.toString(array); }
private static void println(String text) { System.out.println(text); }
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
None.
FREQUENCY : always