1
0
mirror of https://github.com/etesync/android synced 2024-11-26 09:58:11 +00:00

Use cert4android instead of MemorizingTrustManager

* use cert4android instead of MemorizingTrustManager
* new app setting: distrust system certificates
* add network security config to manifest so that user-installed CAs will be accepted in Android 7 again
* update gradle
This commit is contained in:
Ricki Hirner 2016-09-01 22:03:38 +02:00
parent ac940b3a12
commit 19ab4a14ce
18 changed files with 140 additions and 71 deletions

7
.gitmodules vendored
View File

@ -7,7 +7,6 @@
[submodule "vcard4android"] [submodule "vcard4android"]
path = vcard4android path = vcard4android
url = ../vcard4android.git url = ../vcard4android.git
[submodule "MemorizingTrustManager"] [submodule "cert4android"]
path = MemorizingTrustManager path = cert4android
url = https://github.com/ge0rg/MemorizingTrustManager url = ../cert4android.git
ignore = dirty

@ -1 +0,0 @@
Subproject commit b6a3d558e4b78cd9ad5e8ad5246e44f04c854137

View File

@ -17,15 +17,15 @@ android {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 24 targetSdkVersion 24
versionCode 112 versionCode 113
buildConfigField "long", "buildTime", System.currentTimeMillis() + "L" buildConfigField "long", "buildTime", System.currentTimeMillis() + "L"
buildConfigField "boolean", "useMTM", "true" buildConfigField "boolean", "customCerts", "true"
} }
productFlavors { productFlavors {
standard { standard {
versionName "1.2.3-ose" versionName "1.3-ose"
} }
} }
@ -72,7 +72,7 @@ dependencies {
compile 'com.android.support:preference-v14:24.+' compile 'com.android.support:preference-v14:24.+'
compile 'com.github.yukuku:ambilwarna:2.0.1' compile 'com.github.yukuku:ambilwarna:2.0.1'
compile project(':MemorizingTrustManager') compile project(':cert4android')
compile 'dnsjava:dnsjava:2.1.7' compile 'dnsjava:dnsjava:2.1.7'
compile 'org.apache.commons:commons-lang3:3.4' compile 'org.apache.commons:commons-lang3:3.4'

View File

@ -16,7 +16,7 @@ import java.net.Socket;
import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocket;
import de.duenndns.ssl.MemorizingTrustManager; import at.bitfire.cert4android.CustomCertManager;
import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.MockWebServer;
public class SSLSocketFactoryCompatTest extends InstrumentationTestCase { public class SSLSocketFactoryCompatTest extends InstrumentationTestCase {
@ -26,7 +26,7 @@ public class SSLSocketFactoryCompatTest extends InstrumentationTestCase {
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
factory = new SSLSocketFactoryCompat(new MemorizingTrustManager(getInstrumentation().getTargetContext().getApplicationContext())); factory = new SSLSocketFactoryCompat(new CustomCertManager(getInstrumentation().getTargetContext().getApplicationContext(), true));
server.start(); server.start();
} }

View File

@ -53,6 +53,7 @@
android:name=".App" android:name=".App"
android:allowBackup="true" android:allowBackup="true"
android:fullBackupContent="false" android:fullBackupContent="false"
android:networkSecurityConfig="@xml/network_security_config"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
@ -192,11 +193,6 @@
android:label="@string/debug_info_title"> android:label="@string/debug_info_title">
</activity> </activity>
<!-- MemorizingTrustManager -->
<activity
android:name="de.duenndns.ssl.MemorizingActivity"
android:theme="@android:style/Theme.Holo.Light.Dialog.NoActionBar"/>
</application> </application>
</manifest> </manifest>

View File

@ -34,11 +34,11 @@ import java.util.logging.Logger;
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HostnameVerifier;
import at.bitfire.cert4android.CustomCertManager;
import at.bitfire.davdroid.log.LogcatHandler; import at.bitfire.davdroid.log.LogcatHandler;
import at.bitfire.davdroid.log.PlainTextFormatter; import at.bitfire.davdroid.log.PlainTextFormatter;
import at.bitfire.davdroid.model.ServiceDB; import at.bitfire.davdroid.model.ServiceDB;
import at.bitfire.davdroid.model.Settings; import at.bitfire.davdroid.model.Settings;
import de.duenndns.ssl.MemorizingTrustManager;
import lombok.Cleanup; import lombok.Cleanup;
import lombok.Getter; import lombok.Getter;
import okhttp3.internal.tls.OkHostnameVerifier; import okhttp3.internal.tls.OkHostnameVerifier;
@ -46,10 +46,12 @@ import okhttp3.internal.tls.OkHostnameVerifier;
public class App extends Application { public class App extends Application {
public static final String FLAVOR_GOOGLE_PLAY = "gplay"; public static final String FLAVOR_GOOGLE_PLAY = "gplay";
public static final String LOG_TO_EXTERNAL_STORAGE = "logToExternalStorage"; public static final String
DISTRUST_SYSTEM_CERTIFICATES = "distrustSystemCerts",
LOG_TO_EXTERNAL_STORAGE = "logToExternalStorage";
@Getter @Getter
private static MemorizingTrustManager memorizingTrustManager; private static CustomCertManager certManager;
@Getter @Getter
private static SSLSocketFactoryCompat sslSocketFactoryCompat; private static SSLSocketFactoryCompat sslSocketFactoryCompat;
@ -60,21 +62,30 @@ public class App extends Application {
public final static Logger log = Logger.getLogger("davdroid"); public final static Logger log = Logger.getLogger("davdroid");
static { static {
at.bitfire.dav4android.Constants.log = Logger.getLogger("davdroid.dav4android"); at.bitfire.dav4android.Constants.log = Logger.getLogger("davdroid.dav4android");
at.bitfire.cert4android.Constants.log = Logger.getLogger("davdroid.cert4android");
} }
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
reinitCertManager();
// initialize MemorizingTrustManager
memorizingTrustManager = new MemorizingTrustManager(this);
sslSocketFactoryCompat = new SSLSocketFactoryCompat(memorizingTrustManager);
hostnameVerifier = memorizingTrustManager.wrapHostnameVerifier(OkHostnameVerifier.INSTANCE);
// initializer logger
reinitLogger(); reinitLogger();
} }
public void reinitCertManager() {
if (BuildConfig.customCerts) {
if (certManager != null)
certManager.close();
@Cleanup ServiceDB.OpenHelper dbHelper = new ServiceDB.OpenHelper(this);
Settings settings = new Settings(dbHelper.getReadableDatabase());
certManager = new CustomCertManager(this, !settings.getBoolean(DISTRUST_SYSTEM_CERTIFICATES, false));
sslSocketFactoryCompat = new SSLSocketFactoryCompat(certManager);
hostnameVerifier = certManager.hostnameVerifier(OkHostnameVerifier.INSTANCE);
}
}
public void reinitLogger() { public void reinitLogger() {
// don't use Android default logging, we have our own handlers // don't use Android default logging, we have our own handlers
log.setUseParentHandlers(false); log.setUseParentHandlers(false);

View File

@ -69,8 +69,8 @@ public class HttpClient {
OkHttpClient.Builder builder = client.newBuilder(); OkHttpClient.Builder builder = client.newBuilder();
// use MemorizingTrustManager to manage self-signed certificates // use MemorizingTrustManager to manage self-signed certificates
if (App.getSslSocketFactoryCompat() != null) if (App.getSslSocketFactoryCompat() != null && App.getCertManager() != null)
builder.sslSocketFactory(App.getSslSocketFactoryCompat(), App.getMemorizingTrustManager()); builder.sslSocketFactory(App.getSslSocketFactoryCompat(), App.getCertManager());
if (App.getHostnameVerifier() != null) if (App.getHostnameVerifier() != null)
builder.hostnameVerifier(App.getHostnameVerifier()); builder.hostnameVerifier(App.getHostnameVerifier());

View File

@ -27,7 +27,6 @@ import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import de.duenndns.ssl.MemorizingTrustManager;
import lombok.Cleanup; import lombok.Cleanup;
public class SSLSocketFactoryCompat extends SSLSocketFactory { public class SSLSocketFactoryCompat extends SSLSocketFactory {
@ -99,10 +98,10 @@ public class SSLSocketFactoryCompat extends SSLSocketFactory {
} }
} }
public SSLSocketFactoryCompat(@NonNull MemorizingTrustManager mtm) { public SSLSocketFactoryCompat(@NonNull X509TrustManager trustManager) {
try { try {
SSLContext sslContext = SSLContext.getInstance("TLS"); SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new X509TrustManager[] { mtm }, null); sslContext.init(null, new X509TrustManager[] { trustManager }, null);
delegate = sslContext.getSocketFactory(); delegate = sslContext.getSocketFactory();
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new AssertionError(); // The system has no TLS. Just give up. throw new AssertionError(); // The system has no TLS. Just give up.

View File

@ -62,6 +62,7 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import at.bitfire.cert4android.CustomCertManager;
import at.bitfire.davdroid.App; import at.bitfire.davdroid.App;
import at.bitfire.davdroid.DavService; import at.bitfire.davdroid.DavService;
import at.bitfire.davdroid.R; import at.bitfire.davdroid.R;
@ -110,6 +111,22 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
getLoaderManager().initLoader(0, getIntent().getExtras(), this); getLoaderManager().initLoader(0, getIntent().getExtras(), this);
} }
@Override
protected void onPause() {
super.onPause();
CustomCertManager certManager = App.getCertManager();
if (certManager != null)
certManager.appInForeground = false;
}
@Override
protected void onResume() {
super.onResume();
CustomCertManager certManager = App.getCertManager();
if (certManager != null)
certManager.appInForeground = true;
}
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_account, menu); getMenuInflater().inflate(R.menu.activity_account, menu);

View File

@ -16,15 +16,10 @@ import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat; import android.support.v7.preference.PreferenceFragmentCompat;
import android.support.v7.preference.SwitchPreferenceCompat; import android.support.v7.preference.SwitchPreferenceCompat;
import java.security.KeyStoreException;
import java.util.Enumeration;
import java.util.logging.Level;
import at.bitfire.davdroid.App; import at.bitfire.davdroid.App;
import at.bitfire.davdroid.R; import at.bitfire.davdroid.R;
import at.bitfire.davdroid.model.ServiceDB; import at.bitfire.davdroid.model.ServiceDB;
import at.bitfire.davdroid.model.Settings; import at.bitfire.davdroid.model.Settings;
import de.duenndns.ssl.MemorizingTrustManager;
import lombok.Cleanup; import lombok.Cleanup;
public class AppSettingsActivity extends AppCompatActivity { public class AppSettingsActivity extends AppCompatActivity {
@ -42,19 +37,43 @@ public class AppSettingsActivity extends AppCompatActivity {
public static class SettingsFragment extends PreferenceFragmentCompat { public static class SettingsFragment extends PreferenceFragmentCompat {
Preference prefResetHints, ServiceDB.OpenHelper dbHelper;
prefResetCertificates; Settings settings;
SwitchPreferenceCompat prefLogToExternalStorage;
Preference
prefResetHints,
prefResetCertificates;
SwitchPreferenceCompat
prefDistrustSystemCerts,
prefLogToExternalStorage;
@Override
public void onCreate(Bundle savedInstanceState) {
dbHelper = new ServiceDB.OpenHelper(getContext());
settings = new Settings(dbHelper.getReadableDatabase());
super.onCreate(savedInstanceState);
}
@Override
public void onDestroy() {
super.onDestroy();
dbHelper.close();
}
@Override @Override
public void onCreatePreferences(Bundle bundle, String s) { public void onCreatePreferences(Bundle bundle, String s) {
addPreferencesFromResource(R.xml.settings_app); addPreferencesFromResource(R.xml.settings_app);
prefResetHints = findPreference("reset_hints"); prefResetHints = findPreference("reset_hints");
prefResetCertificates = findPreference("reset_certificates");
@Cleanup ServiceDB.OpenHelper dbHelper = new ServiceDB.OpenHelper(getContext()); prefDistrustSystemCerts = (SwitchPreferenceCompat)findPreference("distrust_system_certs");
Settings settings = new Settings(dbHelper.getReadableDatabase()); prefDistrustSystemCerts.setChecked(settings.getBoolean(App.DISTRUST_SYSTEM_CERTIFICATES, false));
prefResetCertificates = findPreference("reset_certificates");
if (App.getCertManager() == null)
prefResetCertificates.setVisible(false);
prefLogToExternalStorage = (SwitchPreferenceCompat)findPreference("log_to_external_storage"); prefLogToExternalStorage = (SwitchPreferenceCompat)findPreference("log_to_external_storage");
prefLogToExternalStorage.setChecked(settings.getBoolean(App.LOG_TO_EXTERNAL_STORAGE, false)); prefLogToExternalStorage.setChecked(settings.getBoolean(App.LOG_TO_EXTERNAL_STORAGE, false));
} }
@ -63,6 +82,8 @@ public class AppSettingsActivity extends AppCompatActivity {
public boolean onPreferenceTreeClick(Preference preference) { public boolean onPreferenceTreeClick(Preference preference) {
if (preference == prefResetHints) if (preference == prefResetHints)
resetHints(); resetHints();
else if (preference == prefDistrustSystemCerts)
setDistrustSystemCerts(((SwitchPreferenceCompat)preference).isChecked());
else if (preference == prefResetCertificates) else if (preference == prefResetCertificates)
resetCertificates(); resetCertificates();
else if (preference == prefLogToExternalStorage) else if (preference == prefLogToExternalStorage)
@ -73,31 +94,25 @@ public class AppSettingsActivity extends AppCompatActivity {
} }
private void resetHints() { private void resetHints() {
@Cleanup ServiceDB.OpenHelper dbHelper = new ServiceDB.OpenHelper(getContext());
Settings settings = new Settings(dbHelper.getWritableDatabase());
settings.remove(StartupDialogFragment.HINT_BATTERY_OPTIMIZATIONS); settings.remove(StartupDialogFragment.HINT_BATTERY_OPTIMIZATIONS);
settings.remove(StartupDialogFragment.HINT_OPENTASKS_NOT_INSTALLED); settings.remove(StartupDialogFragment.HINT_OPENTASKS_NOT_INSTALLED);
Snackbar.make(getView(), R.string.app_settings_reset_hints_success, Snackbar.LENGTH_LONG).show(); Snackbar.make(getView(), R.string.app_settings_reset_hints_success, Snackbar.LENGTH_LONG).show();
} }
private void resetCertificates() { private void setDistrustSystemCerts(boolean distrust) {
MemorizingTrustManager mtm = App.getMemorizingTrustManager(); settings.putBoolean(App.DISTRUST_SYSTEM_CERTIFICATES, distrust);
int deleted = 0; // re-initialize certificate manager
Enumeration<String> iterator = mtm.getCertificates(); App app = (App)getContext().getApplicationContext();
while (iterator.hasMoreElements()) app.reinitCertManager();
try { }
mtm.deleteCertificate(iterator.nextElement());
deleted++; private void resetCertificates() {
} catch (KeyStoreException e) { App.getCertManager().resetCertificates();
App.log.log(Level.SEVERE, "Couldn't distrust certificate", e); Snackbar.make(getView(), getString(R.string.app_settings_reset_certificates_success), Snackbar.LENGTH_LONG).show();
}
Snackbar.make(getView(), getResources().getQuantityString(R.plurals.app_settings_reset_trusted_certificates_success, deleted, deleted), Snackbar.LENGTH_LONG).show();
} }
private void setExternalLogging(boolean externalLogging) { private void setExternalLogging(boolean externalLogging) {
@Cleanup ServiceDB.OpenHelper dbHelper = new ServiceDB.OpenHelper(getContext());
Settings settings = new Settings(dbHelper.getWritableDatabase());
settings.putBoolean(App.LOG_TO_EXTERNAL_STORAGE, externalLogging); settings.putBoolean(App.LOG_TO_EXTERNAL_STORAGE, externalLogging);
// reinitialize logger of default process // reinitialize logger of default process

View File

@ -14,6 +14,7 @@ import android.support.v7.app.AppCompatActivity;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.Constants; import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R; import at.bitfire.davdroid.R;
@ -53,6 +54,20 @@ public class LoginActivity extends AppCompatActivity {
} }
@Override
protected void onResume() {
super.onResume();
if (App.getCertManager() != null)
App.getCertManager().appInForeground = true;
}
@Override
protected void onPause() {
super.onPause();
if (App.getCertManager() != null)
App.getCertManager().appInForeground = false;
}
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_login, menu); getMenuInflater().inflate(R.menu.activity_login, menu);

View File

@ -73,12 +73,12 @@
<string name="app_settings_reset_hints_summary">Re-enables hints which have been dismissed previously</string> <string name="app_settings_reset_hints_summary">Re-enables hints which have been dismissed previously</string>
<string name="app_settings_reset_hints_success">All hints will be shown again</string> <string name="app_settings_reset_hints_success">All hints will be shown again</string>
<string name="app_settings_security">Security</string> <string name="app_settings_security">Security</string>
<string name="app_settings_reset_trusted_certificates">Reset trusted certificates</string> <string name="app_settings_distrust_system_certs">Distrust system certificates</string>
<string name="app_settings_reset_trusted_certificates_summary">Forgets all certificates which have been accepted previously</string> <string name="app_settings_distrust_system_certs_on">System and user-added CAs won\'t be trusted</string>
<plurals name="app_settings_reset_trusted_certificates_success"> <string name="app_settings_distrust_system_certs_off">System and user-added CAs will be trusted</string>
<item quantity="one">Distrusted one certificate</item> <string name="app_settings_reset_certificates">Reset (un)trusted certificates</string>
<item quantity="other">Distrusted %d certificates</item> <string name="app_settings_reset_certificates_summary">Resets trust of all custom certificates</string>
</plurals> <string name="app_settings_reset_certificates_success">All custom certificates have been cleared</string>
<string name="app_settings_debug">Debugging</string> <string name="app_settings_debug">Debugging</string>
<string name="app_settings_log_to_external_storage">Log to external file</string> <string name="app_settings_log_to_external_storage">Log to external file</string>
<string name="app_settings_log_to_external_storage_on">Logging to external storage (if available)</string> <string name="app_settings_log_to_external_storage_on">Logging to external storage (if available)</string>
@ -261,4 +261,8 @@
</string-array> </string-array>
<string name="sync_error_unauthorized">User name/password wrong</string> <string name="sync_error_unauthorized">User name/password wrong</string>
<!-- cert4android -->
<string name="certificate_notification_connection_security">DAVdroid: Connection security</string>
<string name="trust_certificate_unknown_certificate_found">DAVdroid has encountered an unknown certificate. Do you want to trust it?</string>
</resources> </resources>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="system"/>
<certificates src="user"/>
</trust-anchors>
</base-config>
</network-security-config>

View File

@ -17,10 +17,15 @@
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/app_settings_security"> <PreferenceCategory android:title="@string/app_settings_security">
<SwitchPreferenceCompat
android:key="distrust_system_certs"
android:title="@string/app_settings_distrust_system_certs"
android:summaryOn="@string/app_settings_distrust_system_certs_on"
android:summaryOff="@string/app_settings_distrust_system_certs_off"/>
<Preference <Preference
android:key="reset_certificates" android:key="reset_certificates"
android:title="@string/app_settings_reset_trusted_certificates" android:title="@string/app_settings_reset_certificates"
android:summary="@string/app_settings_reset_trusted_certificates_summary"/> android:summary="@string/app_settings_reset_certificates_summary"/>
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/app_settings_debug"> <PreferenceCategory android:title="@string/app_settings_debug">

View File

@ -12,7 +12,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:2.1.2' classpath 'com.android.tools.build:gradle:2.+'
} }
} }

1
cert4android Submodule

@ -0,0 +1 @@
Subproject commit c342cbd81185d7f6bd2cd25eea55bd0ecf94c1cc

View File

@ -1,6 +1,6 @@
#Sat Apr 09 22:18:11 CEST 2016 #Tue Aug 23 16:42:17 CEST 2016
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip

View File

@ -10,5 +10,4 @@ include ':app'
include ':dav4android' include ':dav4android'
include ':ical4android' include ':ical4android'
include ':vcard4android' include ':vcard4android'
include ':cert4android'
include ':MemorizingTrustManager'