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?
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:
And if I comment out the SSLv2Client part, I'd get this:
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.