Search code examples
javasslhttpshandshakejsse

Is it possible to remove the extension part of SSL handshakes in Java?


There is a legacy Cisco IPS server which I am trying to connect to using https. The problem is this server only accepts handshakes with certain conditions:

The version must be TLSv1.0, the cipher suite must be SSL_RSA_WITH_RC4_128_MD5 or SSL_RSA_WITH_RC4_128_SHA and there mustn't be any extensions.

I implemented a hand-made "ClientHello" which sends the following info as handshake (wireshark output):

Secure Sockets Layer
TLSv1.2 Record Layer: Handshake Protocol: Client Hello
    Content Type: Handshake (22)
    Version: TLS 1.0 (0x0301)
    Length: 45
    Handshake Protocol: Client Hello
        Handshake Type: Client Hello (1)
        Length: 41
        Version: TLS 1.0 (0x0301)
        Random
        Session ID Length: 0
        Cipher Suites Length: 2
        Cipher Suites (1 suite)
        Compression Methods Length: 1
        Compression Methods (1 method)

The server sends back the ServerHello message.

Now I want to use Java's SSL implementation to send exactly the same ClientHello. The following code:

    System.setProperty("https.protocols", "TLSv1");
    System.setProperty("javax.net.debug", "ssl:handshake");

    SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
    SSLSocket socket = (SSLSocket) factory.createSocket("ips-server", 443);

    socket.setEnabledProtocols(new String[] {"TLSv1"});
    socket.setEnabledCipherSuites(new String[] {"SSL_RSA_WITH_RC4_128_MD5"});

    socket.startHandshake();

produces the following handshake:

Secure Sockets Layer
TLSv1.2 Record Layer: Handshake Protocol: Client Hello
    Content Type: Handshake (22)
    Version: TLS 1.0 (0x0301)
    Length: 52
    Handshake Protocol: Client Hello
        Handshake Type: Client Hello (1)
        Length: 48
        Version: TLS 1.0 (0x0301)
        Random
        Session ID Length: 0
        Cipher Suites Length: 2
        Cipher Suites (1 suite)
        Compression Methods Length: 1
        Compression Methods (1 method)
        Extensions Length: 5
        Extension: renegotiation_info
            Type: renegotiation_info (0xff01)
            Length: 1
            Renegotiation Info extension

This causes the server to send back the following packet:

TLSv1.2 Record Layer: Alert (Level: Fatal, Description: Handshake Failure)

Is it possible to make Java not send the "extension" part of the packet?


Solution

  • If anyone wants to know the answer, you need to use SSLv2Hello for https.protocols System property.

    System.setProperty("https.protocols", "TLSv1,SSLv2Hello");
    

    I used the following code to connect to the server:

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, new TrustManager[] { new X509TrustManager() {
        @Override
        public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
        }
    
        @Override
        public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
        }
    
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null; 
        }
    } }, new SecureRandom());
    
    SSLSocketFactory socketFactory = sslContext.getSocketFactory();
    HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory);
    
    HttpsURLConnection conn = (HttpsURLConnection) new URL(url).openConnection();
    conn.setHostnameVerifier(new HostnameVerifier() {
        @Override
        public boolean verify(String string, SSLSession ssls) {
            return true;
        }
    });
    

    EDIT More explanation - @EJP mentioned that this code does not answer the question in the title. I wrote a small test to show if the extension part of SSL client hello is removed as a side effect of changing the client hello to SSLv2.

        public static void main(String[] args) throws NoSuchAlgorithmException, KeyManagementException, MalformedURLException, IOException {
        System.setProperty("https.protocols", "TLSv1,SSLv2Hello");
    
        String url = "https://www.google.com";
    
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[]{new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] xcs, String string) {
            }
    
            @Override
            public void checkServerTrusted(X509Certificate[] xcs, String string) {
            }
    
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        }}, new SecureRandom());
    
        SSLSocketFactory socketFactory = sslContext.getSocketFactory();
        HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory);
    
        HttpsURLConnection conn = (HttpsURLConnection) new URL(url).openConnection();
        conn.setHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String string, SSLSession ssls) {
                return true;
            }
        });
    
        BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
    
        Stream<String> lines = br.lines();
        lines.forEach(l -> { System.out.println(l);});
    }
    

    This is what I get in wireshark if I run this program: enter image description here

    And if I comment out the SSLv2Client part, I'd get this: enter image description here

    So clearly this achieves what I was looking for: Removing the extension part of the SSL handshake. Maybe this is not the best way and as @EJP mentioned introduces a lot of security holes (not my concern here, as my CISCO IPS server is local and I just wanted to connect to it, secure or not), but I couldn't find any other way to do this.