Adding support for SPKI pins, trust-on-first-use of TLS certs.
In order to support F-droid repositories hosted with HTTPS using a self-signed certificate the f-droid client should prompt the user to trust or 'memorize' the certificate presented by a repository. The MemorizingTrustManager[0] project enables easy integration of a prompting activity and corresponding trust manager implementation. This behaviour is useful to projects such as Kerplapp[1] that boostrap an F-droid repository on a user's device where it isn't possible to acquire a long lived CA vetted TLS certificate. In addition to Trust-on-First-Use (TOFU) behaviour, this patch integrates the PinningTrustManager [2] project by Moxie Marlinspike to allow the FDroid client to ship a hardcoded set of Subject Public Key Identifier pins [3] for the official FDroid repository TLS certificate, and the Guardian Project TLS certificate. Additional pins can be added to the FDroidPins.java class. The upstream release of AndroidPinning by moxie0 uses a minsdk value of 8. The Fdroid client has a minsdk of 5, presenting compatibility issues using the AndroidPinning lib as a submodule. Fortunately it seems there is no technical reason preventing using a minSDK of 5 with AndroidPinning. I have created a fork with this change and submitted a pull req upstream. Until this pull is merged we can use my fork of AndroidPinning as the submodule. The new 'flow' for deciding if a repositories presented TLS certificate should be trusted is as follows: 1) If the certificate was previously trusted by a TOFU action, then the certificate is accepted as trusted 2) If the certificate wasn't previously trusted by a TOFU action but there is a matching SPKI pin then the certificate is accepted as trusted 3) If the certificate wasn't previously trusted by a TOFU action and there is no SPKI pin but the certificate is signed by a trusted Certificate Authority it is accepted as trusted (This is the behaviour of the FDroid client prior to this patch with all other conditions being a hard-fail). 4) If the certificate wasn't previously trusted by a TOFU action and there is no SPKI pin and the certificate is not signed by a trusted CA (i.e. self signed or otherwise) then the user is prompted to TOFU the certificate. The user may choose to trust the certificate for the current connection or forever. If the user chooses an option other than "deny" the certificate is accepted as trusted for the specified duration. Users currently using a TLS protected repository will see *no difference* in user experience after this patch is merged as the only TLS protected repositories that would function prior to this patch were providing certificates that match condition #3. [0] https://github.com/ge0rg/MemorizingTrustManager/wiki/Integration [1] https://github.com/guardianproject/kerplapp [2] https://github.com/moxie0/AndroidPinning [3] https://www.imperialviolet.org/2011/05/04/pinning.html
This commit is contained in:
parent
5f22877e1c
commit
254327f9a7
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -2,3 +2,9 @@
|
|||||||
path = extern/Universal-Image-Loader
|
path = extern/Universal-Image-Loader
|
||||||
url = https://github.com/nostra13/Android-Universal-Image-Loader
|
url = https://github.com/nostra13/Android-Universal-Image-Loader
|
||||||
ignore = dirty
|
ignore = dirty
|
||||||
|
[submodule "extern/MemorizingTrustManager"]
|
||||||
|
path = extern/MemorizingTrustManager
|
||||||
|
url = https://github.com/ge0rg/MemorizingTrustManager.git
|
||||||
|
[submodule "extern/AndroidPinning"]
|
||||||
|
path = extern/AndroidPinning
|
||||||
|
url = https://github.com/binaryparadox/AndroidPinning.git
|
||||||
|
@ -201,6 +201,9 @@
|
|||||||
android:resource="@xml/searchable" />
|
android:resource="@xml/searchable" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<!--Used for SSL TOFU, supported by extern/MemorizingTrustManager lib -->
|
||||||
|
<activity android:name="de.duenndns.ssl.MemorizingActivity" />
|
||||||
|
|
||||||
<receiver android:name=".StartupReceiver" >
|
<receiver android:name=".StartupReceiver" >
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
@ -12,8 +12,10 @@ The only required tools are the Android SDK and Apache Ant.
|
|||||||
|
|
||||||
```
|
```
|
||||||
git submodule update --init
|
git submodule update --init
|
||||||
android update project -p .
|
android update project -p . --name F-droid
|
||||||
android update project -p extern/Universal-Image-Loader/library
|
android update lib-project -p extern/Universal-Image-Loader/library
|
||||||
|
android update lib-project -p extern/AndroidPinning
|
||||||
|
android update lib-project -p extern/MemorizingTrustManager
|
||||||
ant clean release
|
ant clean release
|
||||||
```
|
```
|
||||||
|
|
||||||
|
1
extern/AndroidPinning
vendored
Submodule
1
extern/AndroidPinning
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 526654e1b9997b32e513d58d9094d4c1102a6cb3
|
1
extern/MemorizingTrustManager
vendored
Submodule
1
extern/MemorizingTrustManager
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 49452f67a760dfef77ddaa7e0b7d88c713c4a195
|
@ -3,3 +3,5 @@ proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.
|
|||||||
target=android-19
|
target=android-19
|
||||||
|
|
||||||
android.library.reference.1=extern/Universal-Image-Loader/library
|
android.library.reference.1=extern/Universal-Image-Loader/library
|
||||||
|
android.library.reference.2=extern/MemorizingTrustManager
|
||||||
|
android.library.reference.3=extern/AndroidPinning
|
||||||
|
@ -19,26 +19,45 @@
|
|||||||
package org.fdroid.fdroid;
|
package org.fdroid.fdroid;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.Runtime;
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
|
|
||||||
import android.app.Application;
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.preference.PreferenceManager;
|
import android.app.Application;
|
||||||
import android.util.Log;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.cache.disc.impl.LimitedAgeDiscCache;
|
import com.nostra13.universalimageloader.cache.disc.impl.LimitedAgeDiscCache;
|
||||||
|
import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiscCache;
|
||||||
import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
|
import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
|
||||||
|
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
|
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
|
||||||
|
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
|
||||||
import com.nostra13.universalimageloader.utils.StorageUtils;
|
import com.nostra13.universalimageloader.utils.StorageUtils;
|
||||||
|
|
||||||
|
import de.duenndns.ssl.MemorizingTrustManager;
|
||||||
|
|
||||||
|
import org.thoughtcrime.ssl.pinning.PinningTrustManager;
|
||||||
|
import org.thoughtcrime.ssl.pinning.SystemKeyStore;
|
||||||
|
|
||||||
public class FDroidApp extends Application {
|
public class FDroidApp extends Application {
|
||||||
|
|
||||||
private static enum Theme {
|
private static enum Theme {
|
||||||
@ -117,6 +136,44 @@ public class FDroidApp extends Application {
|
|||||||
.threadPoolSize(Runtime.getRuntime().availableProcessors() * 2)
|
.threadPoolSize(Runtime.getRuntime().availableProcessors() * 2)
|
||||||
.build();
|
.build();
|
||||||
ImageLoader.getInstance().init(config);
|
ImageLoader.getInstance().init(config);
|
||||||
|
|
||||||
|
try {
|
||||||
|
SSLContext sc = SSLContext.getInstance("TLS");
|
||||||
|
X509TrustManager defaultTrustManager = null;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* init a trust manager factory with a null keystore to access the system trust managers
|
||||||
|
*/
|
||||||
|
TrustManagerFactory tmf =
|
||||||
|
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||||
|
KeyStore ks = null;
|
||||||
|
tmf.init(ks);
|
||||||
|
TrustManager[] mgrs = tmf.getTrustManagers();
|
||||||
|
|
||||||
|
if(mgrs.length > 0 && mgrs[0] instanceof X509TrustManager)
|
||||||
|
defaultTrustManager = (X509TrustManager) mgrs[0];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* compose a chain of trust managers as follows:
|
||||||
|
* MemorizingTrustManager -> Pinning Trust Manager -> System Trust Manager
|
||||||
|
*/
|
||||||
|
PinningTrustManager pinMgr = new PinningTrustManager(SystemKeyStore.getInstance(ctx),FDroidCertPins.getPinList(), 0);
|
||||||
|
MemorizingTrustManager memMgr = new MemorizingTrustManager(ctx, pinMgr, defaultTrustManager);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* initialize a SSLContext with the outermost trust manager, use this
|
||||||
|
* context to set the default SSL socket factory for the HTTPSURLConnection
|
||||||
|
* class.
|
||||||
|
*/
|
||||||
|
sc.init(null, new TrustManager[] {memMgr}, new java.security.SecureRandom());
|
||||||
|
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
|
||||||
|
} catch (KeyManagementException e) {
|
||||||
|
Log.e("FDroid", "Unable to set up trust manager chain. KeyManagementException");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Log.e("FDroid", "Unable to set up trust manager chain. NoSuchAlgorithmException");
|
||||||
|
} catch (KeyStoreException e) {
|
||||||
|
Log.e("FDroid", "Unable to set up trust manager chain. KeyStoreException");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Context ctx;
|
private Context ctx;
|
||||||
|
56
src/org/fdroid/fdroid/FDroidCertPins.java
Normal file
56
src/org/fdroid/fdroid/FDroidCertPins.java
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010-12 Ciaran Gultnieks, ciaran@ciarang.com
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 3
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.fdroid.fdroid;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class FDroidCertPins {
|
||||||
|
public static final String[] DEFAULT_PINS =
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* SubjectDN: CN=f-droid.org, OU=PositiveSSL, OU=Domain Control Validated
|
||||||
|
* IssuerDN: CN=PositiveSSL CA 2, O=COMODO CA Limited, L=Salford, ST=Greater Manchester, C=GB
|
||||||
|
* Fingerprint: 84B91CDF2312CB9BA7F3BE803783302F8D8C299F
|
||||||
|
* SPKI Pin: 638F93856E1F5EDFCBD40C46D4160CFF21B0713A
|
||||||
|
*/
|
||||||
|
"638F93856E1F5EDFCBD40C46D4160CFF21B0713A",
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SubjectDN: CN=guardianproject.info, OU=Gandi Standard SSL, OU=Domain Control Validated
|
||||||
|
* IssuerDN: CN=Gandi Standard SSL CA, O=GANDI SAS, C=FR
|
||||||
|
* Fingerprint: 187C2573E924DFCBFF2A781A2F99D71C6E031828
|
||||||
|
* SPKI Pin: EB6BBC6C6BAEEA20CB0F3357720D86E0F3A526F4
|
||||||
|
*/
|
||||||
|
"EB6BBC6C6BAEEA20CB0F3357720D86E0F3A526F4",
|
||||||
|
};
|
||||||
|
|
||||||
|
public static ArrayList<String> PINLIST = null;
|
||||||
|
|
||||||
|
public static String[] getPinList()
|
||||||
|
{
|
||||||
|
if(PINLIST == null)
|
||||||
|
{
|
||||||
|
PINLIST = new ArrayList<String>();
|
||||||
|
PINLIST.addAll(Arrays.asList(DEFAULT_PINS));
|
||||||
|
}
|
||||||
|
|
||||||
|
return PINLIST.toArray(new String[PINLIST.size()]);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user