mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-30 05:02:26 -05:00
Add unit tests for TrustManagerFactory
This commit is contained in:
parent
48d11fd386
commit
c5c195d243
@ -27,11 +27,13 @@ android {
|
|||||||
manifest.srcFile 'AndroidManifest.xml'
|
manifest.srcFile 'AndroidManifest.xml'
|
||||||
java.srcDirs = ['src']
|
java.srcDirs = ['src']
|
||||||
res.srcDirs = ['res']
|
res.srcDirs = ['res']
|
||||||
|
assets.srcDirs = ['assets']
|
||||||
}
|
}
|
||||||
|
|
||||||
instrumentTest {
|
instrumentTest {
|
||||||
manifest.srcFile 'tests/AndroidManifest.xml'
|
manifest.srcFile 'tests/AndroidManifest.xml'
|
||||||
java.srcDirs = ['tests/src']
|
java.srcDirs = ['tests/src']
|
||||||
|
assets.srcDirs = ['tests/assets']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ public class K9 extends Application {
|
|||||||
* The application instance. Never <code>null</code>.
|
* The application instance. Never <code>null</code>.
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
void initializeComponent(K9 application);
|
void initializeComponent(Application application);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Application app = null;
|
public static Application app = null;
|
||||||
@ -91,6 +91,15 @@ public class K9 extends Application {
|
|||||||
*/
|
*/
|
||||||
private static List<ApplicationAware> observers = new ArrayList<ApplicationAware>();
|
private static List<ApplicationAware> observers = new ArrayList<ApplicationAware>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will be {@code true} once the initialization is complete and {@link #notifyObservers()}
|
||||||
|
* was called.
|
||||||
|
* Afterwards calls to {@link #registerApplicationAware(com.fsck.k9.K9.ApplicationAware)} will
|
||||||
|
* immediately call {@link com.fsck.k9.K9.ApplicationAware#initializeComponent(K9)} for the
|
||||||
|
* supplied argument.
|
||||||
|
*/
|
||||||
|
private static boolean sInitialized = false;
|
||||||
|
|
||||||
public enum BACKGROUND_OPS {
|
public enum BACKGROUND_OPS {
|
||||||
WHEN_CHECKED, ALWAYS, NEVER, WHEN_CHECKED_AUTO_SYNC
|
WHEN_CHECKED, ALWAYS, NEVER, WHEN_CHECKED_AUTO_SYNC
|
||||||
}
|
}
|
||||||
@ -829,15 +838,20 @@ public class K9 extends Application {
|
|||||||
* component that the application is available and ready
|
* component that the application is available and ready
|
||||||
*/
|
*/
|
||||||
protected void notifyObservers() {
|
protected void notifyObservers() {
|
||||||
for (final ApplicationAware aware : observers) {
|
synchronized (observers) {
|
||||||
if (K9.DEBUG) {
|
for (final ApplicationAware aware : observers) {
|
||||||
Log.v(K9.LOG_TAG, "Initializing observer: " + aware);
|
if (K9.DEBUG) {
|
||||||
}
|
Log.v(K9.LOG_TAG, "Initializing observer: " + aware);
|
||||||
try {
|
}
|
||||||
aware.initializeComponent(this);
|
try {
|
||||||
} catch (Exception e) {
|
aware.initializeComponent(this);
|
||||||
Log.w(K9.LOG_TAG, "Failure when notifying " + aware, e);
|
} catch (Exception e) {
|
||||||
|
Log.w(K9.LOG_TAG, "Failure when notifying " + aware, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sInitialized = true;
|
||||||
|
observers.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -848,8 +862,12 @@ public class K9 extends Application {
|
|||||||
* Never <code>null</code>.
|
* Never <code>null</code>.
|
||||||
*/
|
*/
|
||||||
public static void registerApplicationAware(final ApplicationAware component) {
|
public static void registerApplicationAware(final ApplicationAware component) {
|
||||||
if (!observers.contains(component)) {
|
synchronized (observers) {
|
||||||
observers.add(component);
|
if (sInitialized) {
|
||||||
|
component.initializeComponent(K9.app);
|
||||||
|
} else if (!observers.contains(component)) {
|
||||||
|
observers.add(component);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
package com.fsck.k9.mail.store;
|
package com.fsck.k9.mail.store;
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
@ -13,6 +12,7 @@ import org.apache.commons.io.IOUtils;
|
|||||||
import javax.net.ssl.TrustManager;
|
import javax.net.ssl.TrustManager;
|
||||||
import javax.net.ssl.X509TrustManager;
|
import javax.net.ssl.X509TrustManager;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
@ -114,26 +114,9 @@ public final class TrustManagerFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
java.io.InputStream fis = null;
|
|
||||||
try {
|
try {
|
||||||
javax.net.ssl.TrustManagerFactory tmf = javax.net.ssl.TrustManagerFactory.getInstance("X509");
|
javax.net.ssl.TrustManagerFactory tmf = javax.net.ssl.TrustManagerFactory.getInstance("X509");
|
||||||
Application app = K9.app;
|
loadKeyStore();
|
||||||
keyStoreFile = new File(app.getDir("KeyStore", Context.MODE_PRIVATE) + File.separator + "KeyStore.bks");
|
|
||||||
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
|
||||||
try {
|
|
||||||
fis = new java.io.FileInputStream(keyStoreFile);
|
|
||||||
} catch (FileNotFoundException e1) {
|
|
||||||
fis = null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
keyStore.load(fis, "".toCharArray());
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(LOG_TAG, "KeyStore IOException while initializing TrustManagerFactory ", e);
|
|
||||||
keyStore = null;
|
|
||||||
} catch (CertificateException e) {
|
|
||||||
Log.e(LOG_TAG, "KeyStore CertificateException while initializing TrustManagerFactory ", e);
|
|
||||||
keyStore = null;
|
|
||||||
}
|
|
||||||
tmf.init(keyStore);
|
tmf.init(keyStore);
|
||||||
TrustManager[] tms = tmf.getTrustManagers();
|
TrustManager[] tms = tmf.getTrustManagers();
|
||||||
if (tms != null) {
|
if (tms != null) {
|
||||||
@ -160,10 +143,36 @@ public final class TrustManagerFactory {
|
|||||||
Log.e(LOG_TAG, "Unable to get X509 Trust Manager ", e);
|
Log.e(LOG_TAG, "Unable to get X509 Trust Manager ", e);
|
||||||
} catch (KeyStoreException e) {
|
} catch (KeyStoreException e) {
|
||||||
Log.e(LOG_TAG, "Key Store exception while initializing TrustManagerFactory ", e);
|
Log.e(LOG_TAG, "Key Store exception while initializing TrustManagerFactory ", e);
|
||||||
|
}
|
||||||
|
unsecureTrustManager = new SimpleX509TrustManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void loadKeyStore() throws KeyStoreException, NoSuchAlgorithmException {
|
||||||
|
Context context = K9.app;
|
||||||
|
|
||||||
|
keyStoreFile = new File(context.getDir("KeyStore", Context.MODE_PRIVATE) +
|
||||||
|
File.separator + "KeyStore.bks");
|
||||||
|
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
|
|
||||||
|
FileInputStream fis;
|
||||||
|
try {
|
||||||
|
fis = new FileInputStream(keyStoreFile);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
// If the file doesn't exist, that's fine, too
|
||||||
|
fis = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
keyStore.load(fis, "".toCharArray());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(LOG_TAG, "KeyStore IOException while initializing TrustManagerFactory ", e);
|
||||||
|
keyStore = null;
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
Log.e(LOG_TAG, "KeyStore CertificateException while initializing TrustManagerFactory ", e);
|
||||||
|
keyStore = null;
|
||||||
} finally {
|
} finally {
|
||||||
IOUtils.closeQuietly(fis);
|
IOUtils.closeQuietly(fis);
|
||||||
}
|
}
|
||||||
unsecureTrustManager = new SimpleX509TrustManager();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TrustManagerFactory() {
|
private TrustManagerFactory() {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.fsck.k9.provider;
|
package com.fsck.k9.provider;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
import android.content.ContentProvider;
|
import android.content.ContentProvider;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
@ -981,7 +982,7 @@ public class MessageProvider extends ContentProvider {
|
|||||||
|
|
||||||
K9.registerApplicationAware(new K9.ApplicationAware() {
|
K9.registerApplicationAware(new K9.ApplicationAware() {
|
||||||
@Override
|
@Override
|
||||||
public void initializeComponent(final K9 application) {
|
public void initializeComponent(final Application application) {
|
||||||
Log.v(K9.LOG_TAG, "Registering content resolver notifier");
|
Log.v(K9.LOG_TAG, "Registering content resolver notifier");
|
||||||
|
|
||||||
MessagingController.getInstance(application).addListener(new MessagingListener() {
|
MessagingController.getInstance(application).addListener(new MessagingListener() {
|
||||||
|
BIN
tests/assets/cert1.der
Normal file
BIN
tests/assets/cert1.der
Normal file
Binary file not shown.
BIN
tests/assets/cert2.der
Normal file
BIN
tests/assets/cert2.der
Normal file
Binary file not shown.
129
tests/src/com/fsck/k9/mail/store/TrustManagerFactoryTest.java
Normal file
129
tests/src/com/fsck/k9/mail/store/TrustManagerFactoryTest.java
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package com.fsck.k9.mail.store;
|
||||||
|
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
import com.fsck.k9.K9;
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the functionality of {@link TrustManagerFactory}.
|
||||||
|
*/
|
||||||
|
public class TrustManagerFactoryTest extends AndroidTestCase {
|
||||||
|
public static final String MATCHING_HOST = "k9.example.com";
|
||||||
|
public static final String NOT_MATCHING_HOST = "bla.example.com";
|
||||||
|
public static final int PORT1 = 993;
|
||||||
|
public static final int PORT2 = 465;
|
||||||
|
|
||||||
|
|
||||||
|
private Context mTestContext;
|
||||||
|
private X509Certificate mCert1;
|
||||||
|
private X509Certificate mCert2;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
waitForAppInitialization();
|
||||||
|
|
||||||
|
// Hack to make sure TrustManagerFactory.loadKeyStore() can create the key store file
|
||||||
|
K9.app = new DummyApplication(getContext());
|
||||||
|
|
||||||
|
// Source: https://kmansoft.wordpress.com/2011/04/18/accessing-resources-in-an-androidtestcase/
|
||||||
|
Method m = AndroidTestCase.class.getMethod("getTestContext", new Class[] {});
|
||||||
|
mTestContext = (Context) m.invoke(this, (Object[]) null);
|
||||||
|
|
||||||
|
// Delete the key store file to make sure we start without any stored certificates
|
||||||
|
File keyStoreDir = getContext().getDir("KeyStore", Context.MODE_PRIVATE);
|
||||||
|
new File(keyStoreDir + File.separator + "KeyStore.bks").delete();
|
||||||
|
|
||||||
|
// Load the empty key store file
|
||||||
|
TrustManagerFactory.loadKeyStore();
|
||||||
|
|
||||||
|
// Load certificates
|
||||||
|
AssetManager assets = mTestContext.getAssets();
|
||||||
|
|
||||||
|
CertificateFactory certFactory = CertificateFactory.getInstance("X509");
|
||||||
|
mCert1 = (X509Certificate) certFactory.generateCertificate(assets.open("cert1.der"));
|
||||||
|
mCert2 = (X509Certificate) certFactory.generateCertificate(assets.open("cert2.der"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitForAppInitialization() throws InterruptedException {
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
|
||||||
|
K9.registerApplicationAware(new K9.ApplicationAware() {
|
||||||
|
@Override
|
||||||
|
public void initializeComponent(Application application) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
latch.await();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if TrustManagerFactory supports a host with different certificates for different
|
||||||
|
* services (e.g. SMTP and IMAP).
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This test is to make sure entries in the keystore file aren't overwritten.
|
||||||
|
* See <a href="https://code.google.com/p/k9mail/issues/detail?id=1326">Issue 1326</a>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
* if anything goes wrong
|
||||||
|
*/
|
||||||
|
public void testDifferentCertificatesOnSameServer() throws Exception {
|
||||||
|
TrustManagerFactory.addCertificate(NOT_MATCHING_HOST, PORT1, mCert1);
|
||||||
|
TrustManagerFactory.addCertificate(NOT_MATCHING_HOST, PORT2, mCert2);
|
||||||
|
|
||||||
|
X509TrustManager trustManager1 = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1, true);
|
||||||
|
X509TrustManager trustManager2 = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT2, true);
|
||||||
|
trustManager2.checkServerTrusted(new X509Certificate[] { mCert2 }, "authType");
|
||||||
|
trustManager1.checkServerTrusted(new X509Certificate[] { mCert1 }, "authType");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSelfSignedCertificateMatchingHost() throws Exception {
|
||||||
|
TrustManagerFactory.addCertificate(MATCHING_HOST, PORT1, mCert1);
|
||||||
|
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1, true);
|
||||||
|
trustManager.checkServerTrusted(new X509Certificate[] { mCert1 }, "authType");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSelfSignedCertificateNotMatchingHost() throws Exception {
|
||||||
|
TrustManagerFactory.addCertificate(NOT_MATCHING_HOST, PORT1, mCert1);
|
||||||
|
X509TrustManager trustManager = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1, true);
|
||||||
|
trustManager.checkServerTrusted(new X509Certificate[] { mCert1 }, "authType");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWrongCertificate() throws Exception {
|
||||||
|
TrustManagerFactory.addCertificate(MATCHING_HOST, PORT1, mCert1);
|
||||||
|
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1, true);
|
||||||
|
boolean certificateValid;
|
||||||
|
try {
|
||||||
|
trustManager.checkServerTrusted(new X509Certificate[] { mCert2 }, "authType");
|
||||||
|
certificateValid = true;
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
certificateValid = false;
|
||||||
|
}
|
||||||
|
assertFalse("The certificate should have been rejected but wasn't", certificateValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DummyApplication extends Application {
|
||||||
|
private final Context mContext;
|
||||||
|
|
||||||
|
DummyApplication(Context context) {
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getDir(String name, int mode) {
|
||||||
|
return mContext.getDir(name, mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user