Okhttp: Websphere 8.5: SSL ClientHello with TLSv1 and only one cipher (SSL_RSA_WITH_3DES_EDE_CBC_SHA)

Created on 20 Feb 2017  Â·  16Comments  Â·  Source: square/okhttp

It seems that okhttp does not load a correct a SSL context configuration during the ClientHello phase during a SSL handshake, when deployed on Websphere 8.5 (tested on 8.5.5.0 and 8.5.5.10).

Here the code we use in our test servlet:

OkHttpClient client = new OkHttpClient();
X509TrustManager trustManager;
SSLSocketFactory sslSocketFactory;
try {

    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
            TrustManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init((KeyStore) null);
    TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
    if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
        throw new IllegalStateException("Unexpected default trust managers:"
                + Arrays.toString(trustManagers));
    }
    trustManager = (X509TrustManager) trustManagers[0];


    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, new TrustManager[] { trustManager }, null);
    sslSocketFactory = sslContext.getSocketFactory();
} catch (GeneralSecurityException e) {
    throw new RuntimeException(e);
}

client = new OkHttpClient.Builder()
        .sslSocketFactory(sslSocketFactory, trustManager)
        .build();


Request requestOkhttp = new Request.Builder()
        .url("https://ps.pndsn.com")
        .build();

Response responseOkhttp = client.newCall(requestOkhttp).execute();
if (!responseOkhttp.isSuccessful()) throw new IOException("Unexpected code " + responseOkhttp);

This code generates the following ClientHello logs:

*** ClientHello, TLSv1
RandomCookie:  GMT: 1470746692 bytes = { 13, 37, 250, 150, 220, 1, 92, 228, 78, 1, 121, 129, 39, 94, 206, 28, 248, 18, 97, 143, 169, 18, 61, 105, 150, 84, 46, 62 }
Session ID:  {}
Cipher Suites: [SSL_RSA_WITH_3DES_EDE_CBC_SHA]
Compression Methods:  { 0 }
Extension renegotiation_info, ri_length: 0, ri_connection_data: { null }
Extension server_name, server_name: [host_name: ps.pndsn.com]

The proposed protocol is TLSv1 and only one (old) cipher in present in the list: because of that sometimes the SSL handshake terminates with error when the called server does not support this single cipher.

But the Websphere server has full cipher suite support: in fact if we setup a connection using directly the javax.net API, the cipher list is complete.

The Java net connection code:

```java
try {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
trustManager = (X509TrustManager) trustManagers[0];
} catch (GeneralSecurityException e) {
throw new AssertionError(); // The system has no TLS. Just give up.
}

SSLContext sslContext;
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { trustManager }, null);

url = new URL("https://ps.pndsn.com");

HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setSSLSocketFactory(sslContext.getSocketFactory());

The corresponding ClientHello logs:

* ClientHello, TLSv1
RandomCookie: GMT: 1468749730 bytes = { 95, 206, 134, 169, 85, 159, 44, 73, 117, 200, 60, 67, 132, 132, 16, 172, 103, 107, 180, 190, 72, 136, 109, 177, 136, 182, 89, 192 }
Session ID: {}
Cipher Suites: [SSL_RSA_WITH_AES_256_CBC_SHA, SSL_DHE_RSA_WITH_AES_256_CBC_SHA, SSL_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_AES_128_CBC_SHA, SSL_DHE_RSA_WITH_AES_128_CBC_SHA, SSL_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA]
Compression Methods: { 0 }
Extension renegotiation_info, ri_length: 0, ri_connection_data: { null }
Extension server_name, server_name: [host_name: ps.pndsn.com]
```

It seems that okhttp is not working correctly on Websphere 8.5.

These tests are based on okhttp version 3.6.0.

What kind of issue is this?

  • [ ] Question. This issue tracker is not the place for questions. If you want to ask how to do
    something, or to understand why something isn't working the way you expect it to, use Stack
    Overflow. https://stackoverflow.com/questions/tagged/okhttp

  • [x] Bug report. If you’ve found a bug, spend the time to write a failing test. Bugs with tests
    get fixed. Here’s an example: https://gist.github.com/swankjesse/981fcae102f513eb13ed

  • [ ] Feature Request. Start by telling us what problem you’re trying to solve. Often a solution
    already exists! Don’t send pull requests to implement new features without first getting our
    support. Sometimes we leave features out on purpose to keep the project small.

Most helpful comment

@JakeWharton @swankjesse Adding to the request to see if you can accelerate 3.7.0 if your not already on the verge of releasing it. With the sweet32 vulnerability more and more people are removing 3DES as a supported suite on their servers - including us (IBM). I realize we apparently made a bad choice at some point to be different and unfortunately stuck with it are now suffering for it. Because Oracle still used SSL_ for older suites there have been suites available when using IBM Java until now when the last of these - the 3DES suites - are going away.

We did look into creating our own SslSocketFactory as a temporary solution with 3.6.0 but you don't just accept the suites configured in that factory - you require overlap with your hard-coded list.

All 16 comments

You'll need to configure your ConnectionSpec to enable obsolete cipher suites in OkHttp.
https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CustomCipherSuites.java

It seems that IBMJSSE2, the JSSE implementation used by Websphere, in not compatible with this solution.
The problem is that for this implementation, the cipher suite names may starts with SSL_ or TLS_ (IBM documentation), both accepted: but that is not true for OkHttp.

Example:
The cipher TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 is not legacy (like the SSL_RSA_WITH_3DES_EDE_CBC_SHA cipher) and its Java name is the same as its id in the CipherSuite class.

The socket implementation correctly registers the cipher
java socket.setEnabledCipherSuites(javaNames(spec.cipherSuites()));

but in the getEnabledCipherSuites list, the name is returned with the SSL_ prefix.
For that reason the following lines of code, in the method CipherSuite.isCompatible(SSLSocket socket), return false

java if (cipherSuites != null && !nonEmptyIntersection(cipherSuites, socket.getEnabledCipherSuites())) { return false; }

and the class ConnectionSpecSelector throws the "Unable to find acceptable protocols" exception.

This problem seems very similar to the one descripted in this page
https://github.com/cloudant/java-cloudant/issues/215

Is it possible that this is the real cause of the presence of the single cipher SSL_RSA_WITH_3DES_EDE_CBC_SHA in the client hello phase of my test servlet?

Oh interesting. We can probably fix to handle TLS_ or SSL_ prefixes

@swankjesse Our implementation would benefit greatly from this proposal, does the team have an estimated ETA on bringing this patch into a release?

I experienced the same problem today running an okhttp based client within IBM Bluemix. A solution would be appreciated!

@swankjesse
Hey, any update on an ETA?

No ETA. Any advice on reproducing this as a person who doesn’t use WebSphere? Ideally on a Mac?

This issue _probably_ does not need all of WebSphere to be seen. It is likely that only IBM Java is needed. IBM publishes Docker images with IBM Java in it

docker run -it --rm ibmcom/ibmjava:8-sdk java -version
java version "1.8.0"
Java(TM) SE Runtime Environment (build pxa6480sr4fp1-20170215_01(SR4 FP1))
IBM J9 VM (build 2.8, JRE 1.8.0 Linux amd64-64 Compressed References 20170209_336038 (JIT enabled, AOT enabled)
J9VM - R28_20170209_0201_B336038
JIT  - tr.r14.java.green_20170125_131456
GC   - R28_20170209_0201_B336038_CMPRSS
J9CL - 20170209_336038)
JCL - 20170215_01 based on Oracle jdk8u121-b13

Similar issue for Apache's HTTP client: https://issues.apache.org/jira/browse/HTTPCLIENT-1784

Some relevant IBM documentation: http://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.security.component.80.doc/security-component/jsse2Docs/matchsslcontext_tls.html

That helps a lot. Thanks @bruceadams !

Please note that the IBM doc @bruceadams linked to points out that OkHttpClient's usage of TLS in the creation of the default SSLContext will also cause problems for IBM Java users when attempting to connect to a server that does not offer TLS 1.0. Wanted to make sure that was clear given the initial focus was on 3DES being the only suite chosen.

I'm not a real coder these days - but I believe you could explicitly supply TLSv1,TLSv1.1,TLSv1.2 to get support for all 3 protocols consistently across Oracle and IBM. I do not know about other JDKs though.

Good news: I was able to establish connectivity with OkHttp and this particular server using the IBM docker image. That was a really good help.

But I need to make a code change. I need to change OkHttp’s CipherSuite class to ignore the TLS_ or SSL prefix when identifying a cipher suite. I didn’t realize that IBM and Oracle used different prefixes, and our code is too simple. This is ugly, but should be straightforward to overcome.

The other trick to connecting to this particular server is enabling TLS 1.2 on in the SSLSocket. This is awkward code. In my test I used DelegatingSSLSocketFactory configured like so:

      // Force TLS 1.2 on. This isn’t the on by default for IBM Java8.
      sslSocketFactory = new DelegatingSSLSocketFactory(sslSocketFactory) {
        @Override protected SSLSocket configureSocket(SSLSocket socket) throws IOException {
          socket.setEnabledProtocols(new String[] {
              TlsVersion.TLS_1_0.javaName(),
              TlsVersion.TLS_1_1.javaName(),
              TlsVersion.TLS_1_2.javaName()
          });
          return socket;
        }
      };

I’ll make the OkHttp change to ignore TLS_ vs. SSL_ prefixes.

For reference, here’s my updates to your test harness:
https://gist.github.com/swankjesse/d094cb17d0562520cdbf64254542694a

@JakeWharton @swankjesse, Thanks for fixing this. Are you guys planning on doing a release?

The reason I'm asking is because based on your CHANGELOG, it could be weeks before you release a new version.

In the meantime, I'm going to add a workaround similar to the one @swankjesse described in his gist.

Thanks again for fixing this.

@JakeWharton @swankjesse Adding to the request to see if you can accelerate 3.7.0 if your not already on the verge of releasing it. With the sweet32 vulnerability more and more people are removing 3DES as a supported suite on their servers - including us (IBM). I realize we apparently made a bad choice at some point to be different and unfortunately stuck with it are now suffering for it. Because Oracle still used SSL_ for older suites there have been suites available when using IBM Java until now when the last of these - the 3DES suites - are going away.

We did look into creating our own SslSocketFactory as a temporary solution with 3.6.0 but you don't just accept the suites configured in that factory - you require overlap with your hard-coded list.

@JakeWharton @swankjesse please ignore the entreaty above (which I think you already were 😄 ). The ability to simply turn off the cleansing of the default cipher suites provided by the socket by via providing a ConnectionSpec which has had .allEnabledCipherSuites() invoked on it was totally missed when looking at this.

Glad you're​ not blocked. Sorry for the radio silence; just lots going on at the moment.

Hi @swankjesse - Am using version 3.9.1 of the jar, but am still running into the handshake_failure exception.
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure at com.ibm.jsse2.j.a(j.java:23)

Am trying to communicate to APNS, using okhttp library. We are using IBM WAS 8.5.5 and Java 1.7. Can you pls let me know how can we work around this problem? thanks for your help.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

TheLester picture TheLester  Â·  3Comments

vanshg picture vanshg  Â·  3Comments

SandroMachado picture SandroMachado  Â·  3Comments

theotherp picture theotherp  Â·  3Comments

maoai-xianyu picture maoai-xianyu  Â·  3Comments