4

I am trying to work out recipes for using SNI with major HTTP stacks on modern versions of Android. This includes Apache's separate HttpClient library (not the version baked into Android itself, which is dead and gone).

It appears that recent versions of HttpClient do not support SNI out of the box. When I use the 'cz.msebera.android:httpclient:4.4.1.1' artifact, I get:

javax.net.ssl.SSLPeerUnverifiedException: Host name '...' does not match the certificate subject provided by the peer (CN=...)
at cz.msebera.android.httpclient.conn.ssl.SSLConnectionSocketFactory.verifyHostname(SSLConnectionSocketFactory.java:466)
at cz.msebera.android.httpclient.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:395)

(host names redacted with ...)

This HttpClient issue has some code that purports to address this. However, it is not clear exactly how to use it. This Stack Overflow answer helps a bit with an implementation. However, that in turn crashes with an equivalent exception:

javax.net.ssl.SSLPeerUnverifiedException: Host name '' does not match the certificate subject provided by the peer (CN=...)
at cz.msebera.android.httpclient.conn.ssl.SSLConnectionSocketFactory.verifyHostname(SSLConnectionSocketFactory.java:466)
at cz.msebera.android.httpclient.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:395)

This does not surprise me. The proposed workaround (replace the real host name with the empty string) struck me as rather odd.

This Stack Overflow question-and-answer basically say "use Java 1.7", which is not a viable option for Android.

So, has anyone worked out a recipe for enabling SNI with an Android-compatible HttpClient 4.4+ environment?

Community
  • 1
  • 1
CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491

1 Answers1

4

Try using this version of SSLConnectionSocketFactory instead of the one shipped with Marek Sebera's fork of HttpClient. It contains this Andriod specific bit of code. Last time I tested SNI with the official Apache port of HttpClient 4.3 it worked just fine.

// Android specific code to enable SNI
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
    if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "Enabling SNI for " + target);
    }
    try {
        Method method = sslsock.getClass().getMethod("setHostname", String.class);
        method.invoke(sslsock, target);
    } catch (Exception ex) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "SNI configuration failed", ex);
        }
    }
}
// End of Android specific code
ok2c
  • 26,450
  • 5
  • 63
  • 71
  • Thanks! Unfortunately, this is from 4.3, and it depends on stuff that doesn't exist anymore. However, this *does* show me where I can introduce that `setHostname` reflection hack, which I am using successfully with `HttpURLConnection` (to the extent any reflection hack represents "success"). I already have a custom subclass of `SSLConnectionSocketFactory` with a custom `createLayeredSocket()`. Rather than chaining to the superclass implementation, I'll try cloning that bit of code and blending in the reflection hack. – CommonsWare Mar 04 '16 at 13:30
  • 1
    I wound up cloning [the 4.4.1 edition of `SSLConnectionSocketFactory`](http://svn.apache.org/repos/asf/httpcomponents/httpclient/tags/4.4.1/httpclient/src/main/java/org/apache/http/conn/ssl/SSLConnectionSocketFactory.java) and introducing the code from your answer into `createLayeredSocket()` in the same spot as in the `4.3.5.1` version that you linked to (after `prepareSocket()`, before `startHandshake()`). That seems to be working. I'm aiming to get this rolled into a library eventually. Thanks again! – CommonsWare Mar 04 '16 at 13:43
  • I am glad that helped. Please consider reporting this issue to Marek and asking him to integrate the fix into his fork. – ok2c Mar 04 '16 at 14:00
  • [Done](https://github.com/smarek/httpclient-android/issues/7), though I'll admit that I am not especially hopeful. – CommonsWare Mar 04 '16 at 14:11
  • I cannot get your connection Factory working. And i think your bugfix is not even called for android lower than API LV 17 because the wher you call this code has a "@TargetApi(17)" – Radon8472 Jul 28 '17 at 16:26