photo / mobile-android

Trovebox mobile application for Android
Apache License 2.0
120 stars 64 forks source link

SNI support #467

Closed jpkrohling closed 7 years ago

jpkrohling commented 10 years ago

When trying to connect the mobile application with a self-hosted instance of Trovebox served via SSL with SNI, a SSLException happens. As an example, please find below the exception thrown when trying to load the albums list. The solution is to either make a custom SSLVerifier (for Apache's HTTP client, but it's tricky to get it right securely) or better, to use Android's HttpUrlConnection.

10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047): Could not load next albums in list
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047): javax.net.ssl.SSLException: hostname in certificate didn't match: <photos.kroehling.de> != <app.cascaio.com> OR <app.cascaio.com> OR <cascaio.com>
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:185)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at org.apache.http.conn.ssl.BrowserCompatHostnameVerifier.verify(BrowserCompatHostnameVerifier.java:54)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:114)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:95)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at org.apache.http.conn.ssl.SSLSocketFactory.createSocket(SSLSocketFactory.java:388)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:165)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:360)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at com.trovebox.android.app.net.ApiBase.execute(ApiBase.java:162)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at com.trovebox.android.app.net.ApiBase.execute(ApiBase.java:113)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at com.trovebox.android.app.net.ApiBase.execute(ApiBase.java:79)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at com.trovebox.android.app.net.ApiBase.execute(ApiBase.java:65)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at com.trovebox.android.app.net.TroveboxApi.getAlbums(TroveboxApi.java:63)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at com.trovebox.android.app.ui.adapter.AlbumsEndlessAdapter.loadItems(AlbumsEndlessAdapter.java:51)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at com.trovebox.android.app.ui.adapter.EndlessAdapter$LoadNextTask.doInBackground(EndlessAdapter.java:193)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at com.trovebox.android.app.ui.adapter.EndlessAdapter$LoadNextTask.doInBackground(EndlessAdapter.java:1)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at com.trovebox.android.app.util.concurrent.AsyncTaskEx$2.call(AsyncTaskEx.java:292)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at java.util.concurrent.FutureTask.run(FutureTask.java:234)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at com.trovebox.android.app.util.concurrent.SerialExecutor$1.run(SerialExecutor.java:21)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
10-22 08:24:52.832: E/AlbumsEndlessAdapter(2047):   at java.lang.Thread.run(Thread.java:841)

PS: I have a fix for this already, but I'm unable to build the APK locally. Would be perfect to have a README file describing the setup of a developer environment.

httpdispatch commented 10 years ago

@jpkrohling so you have certificate for one domain but use it for another <photos.kroehling.de> != <app.cascaio.com>? Not sure why should it be fixed if it will cause security vulnerability.

jpkrohling commented 10 years ago

@httpdispatch No, that would be wrong :-) I have an Apache server with one IP, serving several websites, all of them with SSL. For HTTP clients without support for SNI, the wrong certificate is sent to the client, which happens to be "app.cascaio.com" in this case. For clients with SNI, the client send the hostname on the first request, and the HTTP server sends the proper certificate (photos.kroehling.de , in this case). You can check that by opening https://photos.kroehling.de in a browser: for all current browsers, you'll see the proper certificate, for old browsers, you'd be served with a different one.

The latest version of Apache's HttpClient (4.3) have support for this, but the version from Android doesn't. The link I've sent with the blog post on Android's Developer blog has some more information on that, but basically, Android's UrlConnection (and HttpUrlConnection) also supports SNI.

httpdispatch commented 10 years ago

@jpkrohling ok, i will investigate this more in such case. Once i will be able to fix it, i will need your help to test the build.

jpkrohling commented 10 years ago

@httpdispatch Sure, no problem. I actually have played with the code yesterday, and I think I have a proposal patch, but I'm unable to build the APKs due to some other reasons. Is there a document somewhere with instructions on how to setup a developer environment?

httpdispatch commented 10 years ago

On the main page you can find some information. You need to fetch project source including submodule and create library projects for each used submodule

I've started to review problem and seems that there are no simple solution for it. There are 2 options: first you migrate to own port on your hosting, second we migrate all the api calls to HttpUrlConnection. Last option is a very complex update. I need to find some replacement for apache httpclient where i can use post requests, upload files, track upload progress and sign requests with OAuth credentials.

jpkrohling commented 10 years ago

Indeed, there's no simple solution for this :-) What I've done in the past, when I really needed to use HttpClient from Apache was to repackage it under a different package name and ship it with the application. This increases the size of the APK, but it's a "solution". Of course, the proper one is to use HttpUrlConnection, but as you found out, this is a low level class and you'd need to perform pretty much everything manually.

As I mentioned, I have a proposed patch, mostly around the ApiBase.execute method, but I have to get my environment working before I submit it. Unfortunately, the information on the main README file is not very comprehensive. I'll try this night to get it working as per your instructions.

httpdispatch commented 10 years ago

I hope to prepare eclipse environment setup instructions in few days so you can create your own build easily

httpdispatch commented 10 years ago

@jpkrohling i've prepared instructions for building an app. You can find it here https://github.com/photo/mobile-android/wiki/Eclipse-build-environment-setup

patricksan commented 10 years ago

Thanks a lot @httpdispatch for the wiki.

patricksan commented 10 years ago

@jpkrohling could you test the app and see if your changes would solve the problem, please? Thanks a lot.

egon0 commented 9 years ago

+1