Enable TLS for the WebGUI

This commit is contained in:
Lode Hoste 2015-04-09 20:03:50 +02:00
parent c7cdfa1ff2
commit 8f2fe9955a
3 changed files with 91 additions and 2 deletions

View File

@ -1,3 +1,8 @@
# Fix appcompat-v7 v21.0.0 causing crash on Samsung devices with Android v4.2.2 (https://code.google.com/p/android/issues/detail?id=78377) # Fix appcompat-v7 v21.0.0 causing crash on Samsung devices with Android v4.2.2 (https://code.google.com/p/android/issues/detail?id=78377)
-keep class !android.support.v7.internal.view.menu.MenuBuilder, !android.support.v7.internal.view.menu.SubMenuBuilder, android.support.v7.** { *; } -keep class !android.support.v7.internal.view.menu.MenuBuilder, !android.support.v7.internal.view.menu.SubMenuBuilder, android.support.v7.** { *; }
-keep interface android.support.v7.** { *; } -keep interface android.support.v7.** { *; }
# Enable reflective access to mX509Certificate
-keepclassmembers class android.net.http.SslCertificate {
private final X509Certificate mX509Certificate;
}

View File

@ -2,30 +2,82 @@ package com.nutomic.syncthingandroid.activities;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.ComponentName; import android.content.ComponentName;
import android.graphics.Bitmap;
import android.net.http.SslCertificate;
import android.net.http.SslError;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log;
import android.view.View; import android.view.View;
import android.webkit.SslErrorHandler;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewClient; import android.webkit.WebViewClient;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.syncthing.RestApi;
import com.nutomic.syncthingandroid.syncthing.SyncthingService; import com.nutomic.syncthingandroid.syncthing.SyncthingService;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
/** /**
* Holds a WebView that shows the web ui of the local syncthing instance. * Holds a WebView that shows the web ui of the local syncthing instance.
*/ */
public class WebGuiActivity extends SyncthingActivity implements SyncthingService.OnWebGuiAvailableListener { public class WebGuiActivity extends SyncthingActivity
implements SyncthingService.OnWebGuiAvailableListener {
private static final String TAG = "WebGuiActivity";
private WebView mWebView; private WebView mWebView;
private View mLoadingView; private View mLoadingView;
private X509Certificate mCaCert;
/** /**
* Hides the loading screen and shows the WebView once it is fully loaded. * Hides the loading screen and shows the WebView once it is fully loaded.
*/ */
private final WebViewClient mWebViewClient = new WebViewClient() { private final WebViewClient mWebViewClient = new WebViewClient() {
/**
* Catch (self-signed) SSL errors and test if they correspond to Syncthing's certificate.
*/
@Override
public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) {
try {
// Uses reflection to access the private mX509Certificate field of SslCertificate
SslCertificate sslCert = error.getCertificate();
Field f = sslCert.getClass().getDeclaredField("mX509Certificate");
f.setAccessible(true);
X509Certificate cert = (X509Certificate)f.get(sslCert);
if (cert == null) {
Log.w(TAG, "X509Certificate reference invalid");
handler.cancel();
return;
}
cert.verify(mCaCert.getPublicKey());
handler.proceed();
} catch (NoSuchFieldException|IllegalAccessException|CertificateException|
NoSuchAlgorithmException|InvalidKeyException|NoSuchProviderException|
SignatureException e) {
Log.w(TAG, e);
handler.cancel();
}
}
@Override @Override
public void onPageFinished(WebView view, String url) { public void onPageFinished(WebView view, String url) {
mWebView.setVisibility(View.VISIBLE); mWebView.setVisibility(View.VISIBLE);
@ -50,6 +102,8 @@ public class WebGuiActivity extends SyncthingActivity implements SyncthingServic
ProgressBar pb = (ProgressBar) mLoadingView.findViewById(R.id.progress); ProgressBar pb = (ProgressBar) mLoadingView.findViewById(R.id.progress);
pb.setIndeterminate(true); pb.setIndeterminate(true);
loadCaCert();
mWebView = (WebView) findViewById(R.id.webview); mWebView = (WebView) findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true); mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.setWebViewClient(mWebViewClient); mWebView.setWebViewClient(mWebViewClient);
@ -63,10 +117,36 @@ public class WebGuiActivity extends SyncthingActivity implements SyncthingServic
/** /**
* Loads and shows WebView, hides loading view. * Loads and shows WebView, hides loading view.
*
* Sets the X-API-Key (HEADER_API_KEY) header for authorization
*/ */
@Override @Override
public void onWebGuiAvailable() { public void onWebGuiAvailable() {
mWebView.loadUrl(getService().getWebGuiUrl()); Map<String, String> extraHeaders = new HashMap<>();
extraHeaders.put(RestApi.HEADER_API_KEY, getService().getApi().getApiKey());
mWebView.loadUrl(getService().getWebGuiUrl(), extraHeaders);
} }
/**
* Reads the SyncthingService.HTTPS_CERT_FILE Ca Cert key and loads it in memory
*/
private void loadCaCert() {
InputStream inStream = null;
try {
String httpsCertPath = getFilesDir() + "/" + SyncthingService.HTTPS_CERT_FILE;
inStream = new FileInputStream(httpsCertPath);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
mCaCert = (X509Certificate)
cf.generateCertificate(inStream);
} catch (FileNotFoundException|CertificateException e) {
throw new IllegalArgumentException("Untrusted Certificate");
} finally {
try {
if (inStream != null)
inStream.close();
} catch (IOException e) {
Log.w(TAG, e);
}
}
}
} }

View File

@ -953,4 +953,8 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
return new Device(); return new Device();
} }
public String getApiKey() {
return mApiKey;
}
} }