-
Bug
-
Resolution: Not an Issue
-
P4
-
None
-
8u201
-
x86_64
-
windows_7
ADDITIONAL SYSTEM INFORMATION :
OS is Windows 7 64 Bit
Java Version is
java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)
A DESCRIPTION OF THE PROBLEM :
I have a requirement where Java SSL client needs to connect to different SSL Servers supporting different SSL versions (SSLv2Hello, SSLv3, TLS1, TLSv1.1, TLSv1.2).
So, I have enabled the all the protocols in the client side e.g.
String[] protocols = new String[]{"SSLv2Hello","SSLv3","TLSv1","TLSv1.1","TLSv1.2"};
gSocket.setEnabledProtocols(protocols);
And when I make a connection to SSL server where the enabled protocols are
String[] protocols = new String[]{"SSLv3","TLSv1","TLSv1.1"};
serverSocket.setEnabledProtocols(protocols);
Then SSL handshake fails with below exceptions
SSL Server log:
---------------------------------
Thread-0, handling exception: javax.net.ssl.SSLHandshakeException: SSLv2Hello is disabled
Thread-0, SEND TLSv1.2 ALERT: fatal, description = handshake_failure
SSL Client Log:
-------------------------
%% No cached client session
update handshake state: client_hello[1]
upcoming handshake states: server_hello[2]
*** ClientHello, TLSv1.2
RandomCookie: GMT: 1563768310 bytes = { 5, 83, 254, 42, 33, 63, 9, 221, 119, 151, 39, 103, 54, 221, 196, 244, 107, 15, 222, 174, 127, 79, 223, 4, 12, 142, 215, 148 }
Session ID: {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_RSA_WITH_AES_2
Compression Methods: { 0 }
Extension elliptic_curves, curve names: {secp256r1, secp384r1, secp521r1, sect283k1, sect283r1, sect409k1, sect409r1, sect571k1, sect571r1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA256withDSA, SHA1withECDSA, SHA1withRSA,
Extension extended_master_secret
***
Thread-0, WRITE: TLSv1.2 Handshake, length = 193
Thread-0, WRITE: SSLv2 client hello message, length = 179
Thread-0, READ: TLSv1.2 Alert, length = 2
Thread-0, RECV TLSv1.2 ALERT: fatal, handshake_failure
Thread-0, called closeSocket()
Thread-0, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
My question, is why Java SSL Client is sending SSLv2 Client Hello message after TLS1.2 Client Hello, If I understood the JSSE reference guide, the fallback should be next higher SSL version i.e TLS 1.1 that is supported by both Client and the Server.
Note: I have removed the SSLv3 from the jdk.tls.disabledAlgorithms also.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Server Side
1. Create a SSL Server Socket
2. Enable "SSLv3, TLSv1, TLSv1.1" protocols
3. Start the SSL Server Socket
Client Side:
1. Create a SSL Socket
2. Enable the SSL Protocols (SSLv2Hello, SSLv3, TLSv1, TLSv1.1, TLSv1.2)
3. Connect to the server socket
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
SSL handshake should fallback to TLSv1.1 that is the highest version supported by both client and server
ACTUAL -
SSL Handshake fails as Client is sending SSLv2 Client Hello message after TLSv1.2 Client Hello message.
---------- BEGIN SOURCE ----------
import java.io.*;
import java.net.*;
import java.security.*;
import java.util.*;
import javax.net.*;
import javax.net.ssl.*;
public class SSLClient implements Runnable
{
private int port;
private KeyStore _truststore;
private KeyStore _keystore;
private SSLContext sslContext;
static private final String passphrase = "manage";
static private SecureRandom secureRandom;
public SSLClient(int port ) {
this.port = port;
new Thread( this ).start();
}
private void setupClientTrustStore() throws GeneralSecurityException, IOException {
_truststore = KeyStore.getInstance( "JKS" );
_truststore.load( new FileInputStream( "C:\\TestTS.jks" ),passphrase.toCharArray() );
}
private void setupClientKeystore() throws GeneralSecurityException, IOException {
_keystore = KeyStore.getInstance( "JKS" );
_keystore.load( new FileInputStream( "C:\\TestKS.jks" ),passphrase.toCharArray() );
}
private void setupSSLContext() throws GeneralSecurityException, IOException {
TrustManagerFactory tmf = TrustManagerFactory.getInstance( "SunX509" );
tmf.init( _truststore );
KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
kmf.init( _keystore, passphrase.toCharArray() );
sslContext = SSLContext.getInstance( "TLSv1.2" );
sslContext.init( kmf.getKeyManagers(),tmf.getTrustManagers(),secureRandom );
}
public void run() {
try {
// System.setProperty("jdk.tls.client.protocols","SSLv2Hello,SSLv3,TLSv1,TLSv1.1,TLSv1.2");
System.setProperty("javax.net.debug", "ssl");
setupClientTrustStore();
setupClientKeystore();
setupSSLContext();
System.out.println("Connecting to localhost - " + port);
SSLSocket gSocket = (SSLSocket)sslContext.getSocketFactory().createSocket("localhost", port);
// String[] protocols = gSocket.getEnabledProtocols();
String[] protocols = new String[]{"SSLv2Hello","SSLv3","TLSv1","TLSv1.1","TLSv1.2"};
gSocket.setEnabledProtocols(protocols);
protocols = gSocket.getEnabledProtocols();
for (String p : protocols) {
System.out.println("Protocol - " + p);
}
gSocket.startHandshake();
System.out.println("Connected..");
} catch( GeneralSecurityException gse ) {
gse.printStackTrace();
} catch( IOException ie ) {
ie.printStackTrace();
}
}
/**
* Create and start a Server. The port number must
* be provided on the command line
*/
static public void main( String args[] ) {
if (args.length != 1) {
System.err.println( "Usage: java SSLClient [port number]" );
System.exit( 1 );
}
int port = Integer.parseInt( args[0] );
System.out.println( "Wait while secure random numbers are initialized...." );
secureRandom = new SecureRandom();
secureRandom.nextInt();
System.out.println( "Done." );
new SSLClient(port);
}
}
import java.io.*;
import java.net.*;
import java.security.*;
import java.util.*;
import javax.net.*;
import javax.net.ssl.*;
public class SSLServer implements Runnable
{
private int port;
private KeyStore _truststore;
private KeyStore _keystore;
private SSLContext sslContext;
static private final String passphrase = "manage";
static private SecureRandom secureRandom;
/**
* Create a Server that listens on the given port.
* Start the background listening thread
*/
public SSLServer(int port ) {
this.port = port;
new Thread( this ).start();
}
private void setupServerTrustStore() throws GeneralSecurityException, IOException {
_truststore = KeyStore.getInstance( "JKS" );
_truststore.load( new FileInputStream( "C:\\TestTS.jks" ),passphrase.toCharArray() );
}
private void setupServerKeystore() throws GeneralSecurityException, IOException {
_keystore = KeyStore.getInstance( "JKS" );
_keystore.load( new FileInputStream( "C:\\TestKS.jks" ),passphrase.toCharArray() );
}
private void setupSSLContext() throws GeneralSecurityException, IOException {
TrustManagerFactory tmf = TrustManagerFactory.getInstance( "SunX509" );
tmf.init( _truststore );
KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
kmf.init( _keystore, passphrase.toCharArray() );
sslContext = SSLContext.getInstance( "TLS" );
sslContext.init( kmf.getKeyManagers(),tmf.getTrustManagers(),secureRandom );
}
/**
* Background thread: accept new connections
*/
public void run() {
try {
// System.setProperty("jdk.tls.client.protocols","SSLv2Hello,SSLv3,TLSv1,TLSv1.1,TLSv1.2");
System.setProperty("javax.net.debug", "ssl");
setupServerTrustStore();
setupServerKeystore();
setupSSLContext();
SSLServerSocketFactory sf = sslContext.getServerSocketFactory();
SSLServerSocket ss = (SSLServerSocket)sf.createServerSocket( port );
String[] protocols = new String[]{"SSLv3","TLSv1","TLSv1.1"};
ss.setEnabledProtocols(protocols);
// Require client authorization
ss.setNeedClientAuth( false );
System.out.println( "Listening on port "+ port +"..." );
while (true) {
SSLSocket socket = (SSLSocket)ss.accept();
System.out.println( "Got connection from "+socket );
socket.startHandshake();
socket.getInputStream();
System.out.println( "Connected " );
socket.close();
}
} catch( GeneralSecurityException gse ) {
gse.printStackTrace();
} catch( IOException ie ) {
ie.printStackTrace();
}
}
/**
* Create and start a Server. The port number must
* be provided on the command line
*/
static public void main( String args[] ) {
if (args.length != 1) {
System.err.println( "Usage: java Server [port number]" );
System.exit( 1 );
}
int port = Integer.parseInt( args[0] );
System.out.println( "Wait while secure random numbers are initialized...." );
secureRandom = new SecureRandom();
secureRandom.nextInt();
System.out.println( "Done." );
new SSLServer(port);
}
}
---------- END SOURCE ----------
FREQUENCY : always
OS is Windows 7 64 Bit
Java Version is
java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)
A DESCRIPTION OF THE PROBLEM :
I have a requirement where Java SSL client needs to connect to different SSL Servers supporting different SSL versions (SSLv2Hello, SSLv3, TLS1, TLSv1.1, TLSv1.2).
So, I have enabled the all the protocols in the client side e.g.
String[] protocols = new String[]{"SSLv2Hello","SSLv3","TLSv1","TLSv1.1","TLSv1.2"};
gSocket.setEnabledProtocols(protocols);
And when I make a connection to SSL server where the enabled protocols are
String[] protocols = new String[]{"SSLv3","TLSv1","TLSv1.1"};
serverSocket.setEnabledProtocols(protocols);
Then SSL handshake fails with below exceptions
SSL Server log:
---------------------------------
Thread-0, handling exception: javax.net.ssl.SSLHandshakeException: SSLv2Hello is disabled
Thread-0, SEND TLSv1.2 ALERT: fatal, description = handshake_failure
SSL Client Log:
-------------------------
%% No cached client session
update handshake state: client_hello[1]
upcoming handshake states: server_hello[2]
*** ClientHello, TLSv1.2
RandomCookie: GMT: 1563768310 bytes = { 5, 83, 254, 42, 33, 63, 9, 221, 119, 151, 39, 103, 54, 221, 196, 244, 107, 15, 222, 174, 127, 79, 223, 4, 12, 142, 215, 148 }
Session ID: {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_RSA_WITH_AES_2
Compression Methods: { 0 }
Extension elliptic_curves, curve names: {secp256r1, secp384r1, secp521r1, sect283k1, sect283r1, sect409k1, sect409r1, sect571k1, sect571r1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA256withDSA, SHA1withECDSA, SHA1withRSA,
Extension extended_master_secret
***
Thread-0, WRITE: TLSv1.2 Handshake, length = 193
Thread-0, WRITE: SSLv2 client hello message, length = 179
Thread-0, READ: TLSv1.2 Alert, length = 2
Thread-0, RECV TLSv1.2 ALERT: fatal, handshake_failure
Thread-0, called closeSocket()
Thread-0, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
My question, is why Java SSL Client is sending SSLv2 Client Hello message after TLS1.2 Client Hello, If I understood the JSSE reference guide, the fallback should be next higher SSL version i.e TLS 1.1 that is supported by both Client and the Server.
Note: I have removed the SSLv3 from the jdk.tls.disabledAlgorithms also.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Server Side
1. Create a SSL Server Socket
2. Enable "SSLv3, TLSv1, TLSv1.1" protocols
3. Start the SSL Server Socket
Client Side:
1. Create a SSL Socket
2. Enable the SSL Protocols (SSLv2Hello, SSLv3, TLSv1, TLSv1.1, TLSv1.2)
3. Connect to the server socket
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
SSL handshake should fallback to TLSv1.1 that is the highest version supported by both client and server
ACTUAL -
SSL Handshake fails as Client is sending SSLv2 Client Hello message after TLSv1.2 Client Hello message.
---------- BEGIN SOURCE ----------
import java.io.*;
import java.net.*;
import java.security.*;
import java.util.*;
import javax.net.*;
import javax.net.ssl.*;
public class SSLClient implements Runnable
{
private int port;
private KeyStore _truststore;
private KeyStore _keystore;
private SSLContext sslContext;
static private final String passphrase = "manage";
static private SecureRandom secureRandom;
public SSLClient(int port ) {
this.port = port;
new Thread( this ).start();
}
private void setupClientTrustStore() throws GeneralSecurityException, IOException {
_truststore = KeyStore.getInstance( "JKS" );
_truststore.load( new FileInputStream( "C:\\TestTS.jks" ),passphrase.toCharArray() );
}
private void setupClientKeystore() throws GeneralSecurityException, IOException {
_keystore = KeyStore.getInstance( "JKS" );
_keystore.load( new FileInputStream( "C:\\TestKS.jks" ),passphrase.toCharArray() );
}
private void setupSSLContext() throws GeneralSecurityException, IOException {
TrustManagerFactory tmf = TrustManagerFactory.getInstance( "SunX509" );
tmf.init( _truststore );
KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
kmf.init( _keystore, passphrase.toCharArray() );
sslContext = SSLContext.getInstance( "TLSv1.2" );
sslContext.init( kmf.getKeyManagers(),tmf.getTrustManagers(),secureRandom );
}
public void run() {
try {
// System.setProperty("jdk.tls.client.protocols","SSLv2Hello,SSLv3,TLSv1,TLSv1.1,TLSv1.2");
System.setProperty("javax.net.debug", "ssl");
setupClientTrustStore();
setupClientKeystore();
setupSSLContext();
System.out.println("Connecting to localhost - " + port);
SSLSocket gSocket = (SSLSocket)sslContext.getSocketFactory().createSocket("localhost", port);
// String[] protocols = gSocket.getEnabledProtocols();
String[] protocols = new String[]{"SSLv2Hello","SSLv3","TLSv1","TLSv1.1","TLSv1.2"};
gSocket.setEnabledProtocols(protocols);
protocols = gSocket.getEnabledProtocols();
for (String p : protocols) {
System.out.println("Protocol - " + p);
}
gSocket.startHandshake();
System.out.println("Connected..");
} catch( GeneralSecurityException gse ) {
gse.printStackTrace();
} catch( IOException ie ) {
ie.printStackTrace();
}
}
/**
* Create and start a Server. The port number must
* be provided on the command line
*/
static public void main( String args[] ) {
if (args.length != 1) {
System.err.println( "Usage: java SSLClient [port number]" );
System.exit( 1 );
}
int port = Integer.parseInt( args[0] );
System.out.println( "Wait while secure random numbers are initialized...." );
secureRandom = new SecureRandom();
secureRandom.nextInt();
System.out.println( "Done." );
new SSLClient(port);
}
}
import java.io.*;
import java.net.*;
import java.security.*;
import java.util.*;
import javax.net.*;
import javax.net.ssl.*;
public class SSLServer implements Runnable
{
private int port;
private KeyStore _truststore;
private KeyStore _keystore;
private SSLContext sslContext;
static private final String passphrase = "manage";
static private SecureRandom secureRandom;
/**
* Create a Server that listens on the given port.
* Start the background listening thread
*/
public SSLServer(int port ) {
this.port = port;
new Thread( this ).start();
}
private void setupServerTrustStore() throws GeneralSecurityException, IOException {
_truststore = KeyStore.getInstance( "JKS" );
_truststore.load( new FileInputStream( "C:\\TestTS.jks" ),passphrase.toCharArray() );
}
private void setupServerKeystore() throws GeneralSecurityException, IOException {
_keystore = KeyStore.getInstance( "JKS" );
_keystore.load( new FileInputStream( "C:\\TestKS.jks" ),passphrase.toCharArray() );
}
private void setupSSLContext() throws GeneralSecurityException, IOException {
TrustManagerFactory tmf = TrustManagerFactory.getInstance( "SunX509" );
tmf.init( _truststore );
KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
kmf.init( _keystore, passphrase.toCharArray() );
sslContext = SSLContext.getInstance( "TLS" );
sslContext.init( kmf.getKeyManagers(),tmf.getTrustManagers(),secureRandom );
}
/**
* Background thread: accept new connections
*/
public void run() {
try {
// System.setProperty("jdk.tls.client.protocols","SSLv2Hello,SSLv3,TLSv1,TLSv1.1,TLSv1.2");
System.setProperty("javax.net.debug", "ssl");
setupServerTrustStore();
setupServerKeystore();
setupSSLContext();
SSLServerSocketFactory sf = sslContext.getServerSocketFactory();
SSLServerSocket ss = (SSLServerSocket)sf.createServerSocket( port );
String[] protocols = new String[]{"SSLv3","TLSv1","TLSv1.1"};
ss.setEnabledProtocols(protocols);
// Require client authorization
ss.setNeedClientAuth( false );
System.out.println( "Listening on port "+ port +"..." );
while (true) {
SSLSocket socket = (SSLSocket)ss.accept();
System.out.println( "Got connection from "+socket );
socket.startHandshake();
socket.getInputStream();
System.out.println( "Connected " );
socket.close();
}
} catch( GeneralSecurityException gse ) {
gse.printStackTrace();
} catch( IOException ie ) {
ie.printStackTrace();
}
}
/**
* Create and start a Server. The port number must
* be provided on the command line
*/
static public void main( String args[] ) {
if (args.length != 1) {
System.err.println( "Usage: java Server [port number]" );
System.exit( 1 );
}
int port = Integer.parseInt( args[0] );
System.out.println( "Wait while secure random numbers are initialized...." );
secureRandom = new SecureRandom();
secureRandom.nextInt();
System.out.println( "Done." );
new SSLServer(port);
}
}
---------- END SOURCE ----------
FREQUENCY : always