Reputation: 251
I have implemented a JAX-WS client by using ApacheCXF (v3.0.4) and everything works successfully but the problem comes when I want to use a secure connection (SSL/TLS) with java 8 (jdk1.8.0_25).
I see the following exception in log (-Djavax.net.debug=all):
main, handling exception: java.net.SocketException: Connection reset
main, SEND TLSv1.2 ALERT: fatal, description = unexpected_message
main, WRITE: TLSv1.2 Alert, length = 2
main, Exception sending alert: java.net.SocketException: Connection reset by peer: socket write error
After a depeer analysis I have observed the problem is caused because the with Java 8 the server_name (SNI) is not sent but with Java 7 it is sent and the web service invocation works successfully.
Java 8 log (-Djavax.net.debug=all): Missing "Extension server_name"
[...]
Compression Methods: { 0 }
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1, secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA
***
[...]
Java 7 log (-Djavax.net.debug=all) (works): "Extension server_name" is set
[...]
Compression Methods: { 0 }
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1, secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA
Extension server_name, server_name: [host_name: testeo.hostname.es]
***
[...]
It is observed that with Java 7 the Extension server_name, server_name: [host_name: testeo.hostname.es] is set and then the web service invocation works successfully.
Why didn't Java 8 set the server_name as Java 7 did? Is it a Java configuration issue?
Upvotes: 25
Views: 26864
Reputation: 3922
I tried the solution provided by Benjamin Parry, but it did not work for me. After some digging around, I also found this solution which looks very similar, however the SSLSocketFactoryFacade manually inserts the correct SSL header instead of being a pure pass-though. Providing my final code below which is slightly different, but credit to be given to Girish Kamath at javabreaks for the basic idea:
private static class SSLSocketFactoryFacade extends SSLSocketFactory {
private SSLSocketFactory sslsf;
private SSLParameters sslParameters;
public SSLSocketFactoryFacade(String hostName) {
sslParameters = new SSLParameters();
sslParameters.setServerNames(Arrays.asList(new SNIHostName(hostName)));
sslsf = (SSLSocketFactory) SSLSocketFactory.getDefault();;
}
public Socket createSocket() throws IOException {
Socket socket = sslsf.createSocket();
((SSLSocket) socket).setSSLParameters(sslParameters);
return socket;
}
public Socket createSocket(InetAddress arg0, int arg1, InetAddress arg2, int arg3) throws IOException {
Socket socket = sslsf.createSocket(arg0, arg1, arg2, arg3);
((SSLSocket) socket).setSSLParameters(sslParameters);
return socket;
}
public Socket createSocket(InetAddress arg0, int arg1) throws IOException {
Socket socket = sslsf.createSocket(arg0, arg1);
((SSLSocket) socket).setSSLParameters(sslParameters);
return socket;
}
public Socket createSocket(Socket arg0, InputStream arg1, boolean arg2) throws IOException {
Socket socket = sslsf.createSocket(arg0, arg1, arg2);
((SSLSocket) socket).setSSLParameters(sslParameters);
return socket;
}
public Socket createSocket(Socket arg0, String arg1, int arg2, boolean arg3) throws IOException {
Socket socket = sslsf.createSocket(arg0, arg1, arg2, arg3);
((SSLSocket) socket).setSSLParameters(sslParameters);
return socket;
}
public Socket createSocket(String arg0, int arg1, InetAddress arg2, int arg3)
throws IOException, UnknownHostException {
Socket socket = sslsf.createSocket(arg0, arg1, arg2, arg3);
((SSLSocket) socket).setSSLParameters(sslParameters);
return socket;
}
public Socket createSocket(String arg0, int arg1) throws IOException, UnknownHostException {
Socket socket = sslsf.createSocket(arg0, arg1);
((SSLSocket) socket).setSSLParameters(sslParameters);
return socket;
}
public String[] getDefaultCipherSuites() {
return sslsf.getDefaultCipherSuites();
}
public String[] getSupportedCipherSuites() {
return sslsf.getSupportedCipherSuites();
}
}
And then I can call
sslConnection.setSSLSocketFactory(new SSLSocketFactoryFacade(sslConnection.getURL().getHost()));
where sslConnection
is the HttpsURLConnection
.
Upvotes: 1
Reputation: 351
Please use JDK version 8u141 and above where this issue has been fixed. Please review the JDK 8u141 Bugs Fixes page for more details
Upvotes: 10
Reputation: 161
As mentioned, the cause is related to the JDK bug where using setHostnameVerifier() breaks SNI (Extension server_name). https://bugs.openjdk.java.net/browse/JDK-8144566
Our workaround: After testing we found that setting a connection's SSLSocketFactory to just about anything from the default seems to fix the issue.
This does not work:
HttpsURLConnection.setSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault());
This does work:
HttpsURLConnection.setSSLSocketFactory(new SSLSocketFactoryFacade());
So, to fix it for a JAX-WS client, you could do something like this:
bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", new SSLSocketFactoryFacade());
Our SSLSocketFactory facade: (Note that it really doesn't do anything)
public class SSLSocketFactoryFacade extends SSLSocketFactory {
SSLSocketFactory sslsf;
public SSLSocketFactoryFacade() {
sslsf = (SSLSocketFactory) SSLSocketFactory.getDefault();;
}
@Override
public String[] getDefaultCipherSuites() {
return sslsf.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return sslsf.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket socket, String s, int i, boolean b) throws IOException {
return sslsf.createSocket(socket, s, i, b);
}
@Override
public Socket createSocket(String s, int i) throws IOException, UnknownHostException {
return sslsf.createSocket(s, i);
}
@Override
public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) throws IOException, UnknownHostException {
return sslsf.createSocket(s, i, inetAddress, i1);
}
@Override
public Socket createSocket(InetAddress inetAddress, int i) throws IOException {
return createSocket(inetAddress, i);
}
@Override
public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) throws IOException {
return createSocket(inetAddress, i, inetAddress1, i1);
}
}
Upvotes: 16
Reputation: 2505
You, or the underlying libs (the WS lib does it) might be using setHostnameVerifier(..)
There is a bug in java8, where if setHostnameVerifier(..) is used the SNI is not done from the client side.
https://bugs.openjdk.java.net/browse/JDK-8072464
Upvotes: 9