We have a small group of Tomcat servers running OpenJDK v1.7.0_111. We have plans to upgrade them and migrate them this summer but we've found that a client API we interact with is moving to require TLSv1.2 in the near term. My ultimate desire is to find a configuration change to allow for this.
The application hosted there creates it's SSL context in a pretty straight forward way:
SSLContext sslContext = SSLContexts.createDefault()
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
SSLContexts
is from Apache's httpclient library (version 4.4.1) and is also pretty straight forward with how it creates the SSL context:
public static SSLContext createDefault() throws SSLInitializationException {
try {
SSLContext ex = SSLContext.getInstance("TLS");
ex.init((KeyManager[])null, (TrustManager[])null, (SecureRandom)null);
return ex;
} catch (NoSuchAlgorithmException var1) {
throw new SSLInitializationException(var1.getMessage(), var1);
} catch (KeyManagementException var2) {
throw new SSLInitializationException(var2.getMessage(), var2);
}
}
And digging through the SSLConnectionSocketFactory
class, it appears that it's simply using the SSLSocket.getEnabledProtocols()
method to determine which protocols are available for use. Note that this.supportedProtocols
is null in my case.
public Socket createLayeredSocket(Socket socket, String target, int port, HttpContext context) throws IOException {
SSLSocket sslsock = (SSLSocket)this.socketfactory.createSocket(socket, target, port, true);
if(this.supportedProtocols != null) {
sslsock.setEnabledProtocols(this.supportedProtocols);
} else {
String[] allProtocols = sslsock.getEnabledProtocols();
ArrayList enabledProtocols = new ArrayList(allProtocols.length);
String[] arr$ = allProtocols;
int len$ = allProtocols.length;
for(int i$ = 0; i$ < len$; ++i$) {
String protocol = arr$[i$];
if(!protocol.startsWith("SSL")) {
enabledProtocols.add(protocol);
}
}
if(!enabledProtocols.isEmpty()) {
sslsock.setEnabledProtocols((String[])enabledProtocols.toArray(new String[enabledProtocols.size()]));
}
}
The problem I'm having is that while running a few preliminary tests I'm unable to get these clients to connect to an API requiring TLSv1.2.
In the following example I can get the URLConnection
code to complete by including the -Dhttps.protocols=TLSv1.2
parameter, but I cannot get the Apache connection to connect.
public static void main(String[] args) throws Exception{
String testURL = "https://testapi.com";
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, null, null);
try {
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslcontext);
CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
HttpGet httpget = new HttpGet(testURL);
CloseableHttpResponse response = client.execute(httpget);
System.out.println("Response Code (Apache): " + response.getStatusLine().getStatusCode());
}
catch (Exception e){
System.err.println("Apache HTTP Client Failed");
e.printStackTrace();
}
try {
HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(testURL).openConnection();
urlConnection.setSSLSocketFactory(sslcontext.getSocketFactory());
urlConnection.connect();
System.out.println("Response Code (URLConnection): " + urlConnection.getResponseCode());
}
catch (Exception e){
System.err.println("HttpsURLConnection Failed");
e.printStackTrace();
}
}
Along with the -Dhttps.protocols=TLSv1.2
I've tried the -Djdk.tls.client.protocols=TLSv1.2
and the -Ddeployment.security.TLSv1.2=true
JVM parameters without any luck.
Does anyone have thoughts to how to enable TLSv1.2 in this configuration without upgrading to v8 or changing the application to specifically request an instance of TLSv1.2?