Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8228712

SSL Version fallback not happening when SSLv2Hello is specified at the Client

XMLWordPrintable

      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


            psonal Pallavi Sonal (Inactive)
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: