Change enabled protocols for Java SSL client connections, including wrapped case

Viewed 1578

I need to implement the Apache HttpClient SecureProtocolSocketFactory interface to create SSL sockets that do not use the SSLv2Hello protocol and make SSLv3 handshakes. From the documentation, the process to modify the protocol list is to call setEnabledProtocols on the SSLSocket. I believe I need to do this after creating it but before connecting it because connection initiates the SSL handshake and it's the SSL handshake protocol I'm trying to change.

This is mostly fine: rather than using the with-parameter context.getSocketFactory().createSocket(...) overloads which both create and connect the sockets then I can use the parameterless overload to create the socket, set it up and then connect it myself. The problem is there's another overload of createSocket() in SSLSocketFactory that wraps an existing socket with SSL, and that initiates the handshake immediately i.e. I do not have the opportunity to reconfigure it.

So to cover that case too I think I've got to throw away SSLSocket.setEnabledProtocols and instead do one of

  1. modify my SSLContext's set of protocols which is what SSLSocketImpl uses to configure itself. However I can't see a public interface to do this so I'd need to change this by reflection, which means assuming private members of the class and also getting access to the internal class ProtocolList.
  2. use my SSLContext only to get an SSLEngine which I can configure, and then implement my own SSL on the sockets using this.

I'm not very happy with either of these; I'd probably lean towards reflection since Java 7+ is dropping SSLv2Hello so if the classes change in the future and my reflection breaks then that's not actually a problem. I have a new instance of SSLContext already for a custom trust store.

Have I missed something - a public mechanism on SSLContext or SSLSocketFactory to set this up? Is there a better, cleaner way to do this? Thanks!


On further investigation, I don't think reflection is possible either. I was looking at the JDK8 source since that's what I had to hand, whereas in the JDK6 source SSLSocket initialises itself from a static list, not from an SSLContext or SSLContextSpi property:

enabledProtocols = ProtocolList.getDefault();

and the source field behind getDefault is static final so I can't modify that either :-(

So I'm running out of ideas. I'm still not keen on reimplementing SSLSocket (2000+ lines of it) so I think it's back to setEnabledProtocols and hope my HTTPS client never has to negotiate up a connection to SSL.

1 Answers

I ran into very similar problem. I think you can break most of the issues down to the question, why can we override enabled ciphers in a custom socket factory but we cannot set the procol versions returned by getEnabledProtocols(): String[]?

Or even more simple: If you could set enabled ciphers and protocol versions in SSLContext, you could use any HttpsConnection / SSLClient and SSLServerSocket and even libaries (Apache HttpClient, OKHttp, ...) and be sure that your security requirements in terms of SSL protocol versions and ciphers are both met.

Unfortunately most people stop at the point where a connection is established, security is just an announce to them.

Related