mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-24 02:12:15 -05:00
Merge branch 'cert_validation'
This commit is contained in:
commit
448a80e96a
@ -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']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
|
||||||
package com.fsck.k9;
|
package com.fsck.k9;
|
||||||
|
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
@ -22,6 +24,7 @@ import android.net.ConnectivityManager;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection;
|
||||||
import com.fsck.k9.crypto.Apg;
|
import com.fsck.k9.crypto.Apg;
|
||||||
import com.fsck.k9.crypto.CryptoProvider;
|
import com.fsck.k9.crypto.CryptoProvider;
|
||||||
import com.fsck.k9.helper.Utility;
|
import com.fsck.k9.helper.Utility;
|
||||||
@ -40,6 +43,7 @@ import com.fsck.k9.search.SqlQueryBuilder;
|
|||||||
import com.fsck.k9.search.SearchSpecification.Attribute;
|
import com.fsck.k9.search.SearchSpecification.Attribute;
|
||||||
import com.fsck.k9.search.SearchSpecification.SearchCondition;
|
import com.fsck.k9.search.SearchSpecification.SearchCondition;
|
||||||
import com.fsck.k9.search.SearchSpecification.Searchfield;
|
import com.fsck.k9.search.SearchSpecification.Searchfield;
|
||||||
|
import com.fsck.k9.security.LocalKeyStore;
|
||||||
import com.fsck.k9.view.ColorChip;
|
import com.fsck.k9.view.ColorChip;
|
||||||
import com.larswerkman.colorpicker.ColorPicker;
|
import com.larswerkman.colorpicker.ColorPicker;
|
||||||
|
|
||||||
@ -1865,4 +1869,57 @@ public class Account implements BaseAccount {
|
|||||||
search.and(Searchfield.FOLDER, folderName, Attribute.NOT_EQUALS);
|
search.and(Searchfield.FOLDER, folderName, Attribute.NOT_EQUALS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new certificate for the incoming or outgoing server to the local key store.
|
||||||
|
*/
|
||||||
|
public void addCertificate(CheckDirection direction,
|
||||||
|
X509Certificate certificate) throws CertificateException {
|
||||||
|
Uri uri;
|
||||||
|
if (direction.equals(CheckDirection.INCOMING)) {
|
||||||
|
uri = Uri.parse(getStoreUri());
|
||||||
|
} else {
|
||||||
|
uri = Uri.parse(getTransportUri());
|
||||||
|
}
|
||||||
|
LocalKeyStore localKeyStore = LocalKeyStore.getInstance();
|
||||||
|
localKeyStore.addCertificate(uri.getHost(), uri.getPort(), certificate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Examine the existing settings for an account. If the old host/port is different from the
|
||||||
|
* new host/port, then try and delete any (possibly non-existent) certificate stored for the
|
||||||
|
* old host/port.
|
||||||
|
*/
|
||||||
|
public void deleteCertificate(String newHost, int newPort,
|
||||||
|
CheckDirection direction) {
|
||||||
|
Uri uri;
|
||||||
|
if (direction.equals(CheckDirection.INCOMING)) {
|
||||||
|
uri = Uri.parse(getStoreUri());
|
||||||
|
} else {
|
||||||
|
uri = Uri.parse(getTransportUri());
|
||||||
|
}
|
||||||
|
String oldHost = uri.getHost();
|
||||||
|
int oldPort = uri.getPort();
|
||||||
|
if (oldPort == -1) {
|
||||||
|
// This occurs when a new account is created
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!newHost.equals(oldHost) || newPort != oldPort) {
|
||||||
|
LocalKeyStore localKeyStore = LocalKeyStore.getInstance();
|
||||||
|
localKeyStore.deleteCertificate(oldHost, oldPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Examine the settings for the account and attempt to delete (possibly non-existent)
|
||||||
|
* certificates for the incoming and outgoing servers.
|
||||||
|
*/
|
||||||
|
public void deleteCertificates() {
|
||||||
|
LocalKeyStore localKeyStore = LocalKeyStore.getInstance();
|
||||||
|
|
||||||
|
Uri uri = Uri.parse(getStoreUri());
|
||||||
|
localKeyStore.deleteCertificate(uri.getHost(), uri.getPort());
|
||||||
|
uri = Uri.parse(getTransportUri());
|
||||||
|
localKeyStore.deleteCertificate(uri.getHost(), uri.getPort());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ import com.fsck.k9.mail.MessagingException;
|
|||||||
import com.fsck.k9.mail.internet.BinaryTempFileBody;
|
import com.fsck.k9.mail.internet.BinaryTempFileBody;
|
||||||
import com.fsck.k9.mail.store.LocalStore;
|
import com.fsck.k9.mail.store.LocalStore;
|
||||||
import com.fsck.k9.provider.UnreadWidgetProvider;
|
import com.fsck.k9.provider.UnreadWidgetProvider;
|
||||||
|
import com.fsck.k9.security.LocalKeyStore;
|
||||||
import com.fsck.k9.service.BootReceiver;
|
import com.fsck.k9.service.BootReceiver;
|
||||||
import com.fsck.k9.service.MailService;
|
import com.fsck.k9.service.MailService;
|
||||||
import com.fsck.k9.service.ShutdownReceiver;
|
import com.fsck.k9.service.ShutdownReceiver;
|
||||||
@ -59,7 +60,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 +92,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
|
||||||
}
|
}
|
||||||
@ -581,6 +591,8 @@ public class K9 extends Application {
|
|||||||
*/
|
*/
|
||||||
BinaryTempFileBody.setTempDirectory(getCacheDir());
|
BinaryTempFileBody.setTempDirectory(getCacheDir());
|
||||||
|
|
||||||
|
LocalKeyStore.setKeyStoreLocation(getDir("KeyStore", MODE_PRIVATE).toString());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enable background sync of messages
|
* Enable background sync of messages
|
||||||
*/
|
*/
|
||||||
@ -829,6 +841,7 @@ 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() {
|
||||||
|
synchronized (observers) {
|
||||||
for (final ApplicationAware aware : observers) {
|
for (final ApplicationAware aware : observers) {
|
||||||
if (K9.DEBUG) {
|
if (K9.DEBUG) {
|
||||||
Log.v(K9.LOG_TAG, "Initializing observer: " + aware);
|
Log.v(K9.LOG_TAG, "Initializing observer: " + aware);
|
||||||
@ -839,6 +852,10 @@ public class K9 extends Application {
|
|||||||
Log.w(K9.LOG_TAG, "Failure when notifying " + aware, e);
|
Log.w(K9.LOG_TAG, "Failure when notifying " + aware, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sInitialized = true;
|
||||||
|
observers.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -848,10 +865,14 @@ 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) {
|
||||||
|
if (sInitialized) {
|
||||||
|
component.initializeComponent(K9.app);
|
||||||
|
} else if (!observers.contains(component)) {
|
||||||
observers.add(component);
|
observers.add(component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static String getK9Language() {
|
public static String getK9Language() {
|
||||||
return language;
|
return language;
|
||||||
|
@ -127,6 +127,7 @@ public class Preferences {
|
|||||||
|
|
||||||
Store.removeAccount(account);
|
Store.removeAccount(account);
|
||||||
|
|
||||||
|
account.deleteCertificates();
|
||||||
account.delete(this);
|
account.delete(this);
|
||||||
|
|
||||||
if (newAccount == account) {
|
if (newAccount == account) {
|
||||||
|
@ -17,6 +17,7 @@ import android.widget.Button;
|
|||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import com.fsck.k9.*;
|
import com.fsck.k9.*;
|
||||||
import com.fsck.k9.activity.K9Activity;
|
import com.fsck.k9.activity.K9Activity;
|
||||||
|
import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection;
|
||||||
import com.fsck.k9.helper.Utility;
|
import com.fsck.k9.helper.Utility;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
@ -37,6 +38,8 @@ public class AccountSetupBasics extends K9Activity
|
|||||||
private final static int DIALOG_NOTE = 1;
|
private final static int DIALOG_NOTE = 1;
|
||||||
private final static String STATE_KEY_PROVIDER =
|
private final static String STATE_KEY_PROVIDER =
|
||||||
"com.fsck.k9.AccountSetupBasics.provider";
|
"com.fsck.k9.AccountSetupBasics.provider";
|
||||||
|
private final static String STATE_KEY_CHECKED_INCOMING =
|
||||||
|
"com.fsck.k9.AccountSetupBasics.checkedIncoming";
|
||||||
|
|
||||||
private EditText mEmailView;
|
private EditText mEmailView;
|
||||||
private EditText mPasswordView;
|
private EditText mPasswordView;
|
||||||
@ -46,6 +49,7 @@ public class AccountSetupBasics extends K9Activity
|
|||||||
private Provider mProvider;
|
private Provider mProvider;
|
||||||
|
|
||||||
private EmailAddressValidator mEmailValidator = new EmailAddressValidator();
|
private EmailAddressValidator mEmailValidator = new EmailAddressValidator();
|
||||||
|
private boolean mCheckedIncoming = false;
|
||||||
|
|
||||||
public static void actionNewAccount(Context context) {
|
public static void actionNewAccount(Context context) {
|
||||||
Intent i = new Intent(context, AccountSetupBasics.class);
|
Intent i = new Intent(context, AccountSetupBasics.class);
|
||||||
@ -66,15 +70,6 @@ public class AccountSetupBasics extends K9Activity
|
|||||||
|
|
||||||
mEmailView.addTextChangedListener(this);
|
mEmailView.addTextChangedListener(this);
|
||||||
mPasswordView.addTextChangedListener(this);
|
mPasswordView.addTextChangedListener(this);
|
||||||
|
|
||||||
if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_ACCOUNT)) {
|
|
||||||
String accountUuid = savedInstanceState.getString(EXTRA_ACCOUNT);
|
|
||||||
mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedInstanceState != null && savedInstanceState.containsKey(STATE_KEY_PROVIDER)) {
|
|
||||||
mProvider = (Provider)savedInstanceState.getSerializable(STATE_KEY_PROVIDER);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -92,6 +87,23 @@ public class AccountSetupBasics extends K9Activity
|
|||||||
if (mProvider != null) {
|
if (mProvider != null) {
|
||||||
outState.putSerializable(STATE_KEY_PROVIDER, mProvider);
|
outState.putSerializable(STATE_KEY_PROVIDER, mProvider);
|
||||||
}
|
}
|
||||||
|
outState.putBoolean(STATE_KEY_CHECKED_INCOMING, mCheckedIncoming);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||||
|
super.onRestoreInstanceState(savedInstanceState);
|
||||||
|
|
||||||
|
if (savedInstanceState.containsKey(EXTRA_ACCOUNT)) {
|
||||||
|
String accountUuid = savedInstanceState.getString(EXTRA_ACCOUNT);
|
||||||
|
mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (savedInstanceState.containsKey(STATE_KEY_PROVIDER)) {
|
||||||
|
mProvider = (Provider) savedInstanceState.getSerializable(STATE_KEY_PROVIDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
mCheckedIncoming = savedInstanceState.getBoolean(STATE_KEY_CHECKED_INCOMING);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void afterTextChanged(Editable s) {
|
public void afterTextChanged(Editable s) {
|
||||||
@ -229,7 +241,8 @@ public class AccountSetupBasics extends K9Activity
|
|||||||
} else if (incomingUri.toString().startsWith("pop3")) {
|
} else if (incomingUri.toString().startsWith("pop3")) {
|
||||||
mAccount.setDeletePolicy(Account.DELETE_POLICY_NEVER);
|
mAccount.setDeletePolicy(Account.DELETE_POLICY_NEVER);
|
||||||
}
|
}
|
||||||
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, true, true);
|
// Check incoming here. Then check outgoing in onActivityResult()
|
||||||
|
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.INCOMING);
|
||||||
} catch (UnsupportedEncodingException enc) {
|
} catch (UnsupportedEncodingException enc) {
|
||||||
// This really shouldn't happen since the encoding is hardcoded to UTF-8
|
// This really shouldn't happen since the encoding is hardcoded to UTF-8
|
||||||
Log.e(K9.LOG_TAG, "Couldn't urlencode username or password.", enc);
|
Log.e(K9.LOG_TAG, "Couldn't urlencode username or password.", enc);
|
||||||
@ -266,6 +279,12 @@ public class AccountSetupBasics extends K9Activity
|
|||||||
@Override
|
@Override
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
if (resultCode == RESULT_OK) {
|
if (resultCode == RESULT_OK) {
|
||||||
|
if (!mCheckedIncoming) {
|
||||||
|
//We've successfully checked incoming. Now check outgoing.
|
||||||
|
mCheckedIncoming = true;
|
||||||
|
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.OUTGOING);
|
||||||
|
} else {
|
||||||
|
//We've successfully checked outgoing as well.
|
||||||
mAccount.setDescription(mAccount.getEmail());
|
mAccount.setDescription(mAccount.getEmail());
|
||||||
mAccount.save(Preferences.getPreferences(this));
|
mAccount.save(Preferences.getPreferences(this));
|
||||||
K9.setServicesEnabled(this);
|
K9.setServicesEnabled(this);
|
||||||
@ -273,6 +292,7 @@ public class AccountSetupBasics extends K9Activity
|
|||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void onManualSetup() {
|
private void onManualSetup() {
|
||||||
String email = mEmailView.getText().toString();
|
String email = mEmailView.getText().toString();
|
||||||
|
@ -22,7 +22,6 @@ import com.fsck.k9.mail.AuthenticationFailedException;
|
|||||||
import com.fsck.k9.mail.CertificateValidationException;
|
import com.fsck.k9.mail.CertificateValidationException;
|
||||||
import com.fsck.k9.mail.Store;
|
import com.fsck.k9.mail.Store;
|
||||||
import com.fsck.k9.mail.Transport;
|
import com.fsck.k9.mail.Transport;
|
||||||
import com.fsck.k9.mail.store.TrustManagerFactory;
|
|
||||||
import com.fsck.k9.mail.store.WebDavStore;
|
import com.fsck.k9.mail.store.WebDavStore;
|
||||||
import com.fsck.k9.mail.filter.Hex;
|
import com.fsck.k9.mail.filter.Hex;
|
||||||
|
|
||||||
@ -47,9 +46,12 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
|
|||||||
|
|
||||||
private static final String EXTRA_ACCOUNT = "account";
|
private static final String EXTRA_ACCOUNT = "account";
|
||||||
|
|
||||||
private static final String EXTRA_CHECK_INCOMING = "checkIncoming";
|
private static final String EXTRA_CHECK_DIRECTION ="checkDirection";
|
||||||
|
|
||||||
private static final String EXTRA_CHECK_OUTGOING = "checkOutgoing";
|
public enum CheckDirection {
|
||||||
|
INCOMING,
|
||||||
|
OUTGOING
|
||||||
|
}
|
||||||
|
|
||||||
private Handler mHandler = new Handler();
|
private Handler mHandler = new Handler();
|
||||||
|
|
||||||
@ -59,20 +61,16 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
|
|||||||
|
|
||||||
private Account mAccount;
|
private Account mAccount;
|
||||||
|
|
||||||
private boolean mCheckIncoming;
|
private CheckDirection mDirection;
|
||||||
|
|
||||||
private boolean mCheckOutgoing;
|
|
||||||
|
|
||||||
private boolean mCanceled;
|
private boolean mCanceled;
|
||||||
|
|
||||||
private boolean mDestroyed;
|
private boolean mDestroyed;
|
||||||
|
|
||||||
public static void actionCheckSettings(Activity context, Account account,
|
public static void actionCheckSettings(Activity context, Account account, CheckDirection direction) {
|
||||||
boolean checkIncoming, boolean checkOutgoing) {
|
|
||||||
Intent i = new Intent(context, AccountSetupCheckSettings.class);
|
Intent i = new Intent(context, AccountSetupCheckSettings.class);
|
||||||
i.putExtra(EXTRA_ACCOUNT, account.getUuid());
|
i.putExtra(EXTRA_ACCOUNT, account.getUuid());
|
||||||
i.putExtra(EXTRA_CHECK_INCOMING, checkIncoming);
|
i.putExtra(EXTRA_CHECK_DIRECTION, direction);
|
||||||
i.putExtra(EXTRA_CHECK_OUTGOING, checkOutgoing);
|
|
||||||
context.startActivityForResult(i, ACTIVITY_REQUEST_CODE);
|
context.startActivityForResult(i, ACTIVITY_REQUEST_CODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,8 +87,7 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
|
|||||||
|
|
||||||
String accountUuid = getIntent().getStringExtra(EXTRA_ACCOUNT);
|
String accountUuid = getIntent().getStringExtra(EXTRA_ACCOUNT);
|
||||||
mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
|
mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
|
||||||
mCheckIncoming = getIntent().getBooleanExtra(EXTRA_CHECK_INCOMING, false);
|
mDirection = (CheckDirection) getIntent().getSerializableExtra(EXTRA_CHECK_DIRECTION);
|
||||||
mCheckOutgoing = getIntent().getBooleanExtra(EXTRA_CHECK_OUTGOING, false);
|
|
||||||
|
|
||||||
new Thread() {
|
new Thread() {
|
||||||
@Override
|
@Override
|
||||||
@ -108,9 +105,9 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
|
|||||||
|
|
||||||
final MessagingController ctrl = MessagingController.getInstance(getApplication());
|
final MessagingController ctrl = MessagingController.getInstance(getApplication());
|
||||||
ctrl.clearCertificateErrorNotifications(AccountSetupCheckSettings.this,
|
ctrl.clearCertificateErrorNotifications(AccountSetupCheckSettings.this,
|
||||||
mAccount, mCheckIncoming, mCheckOutgoing);
|
mAccount, mDirection);
|
||||||
|
|
||||||
if (mCheckIncoming) {
|
if (mDirection.equals(CheckDirection.INCOMING)) {
|
||||||
store = mAccount.getRemoteStore();
|
store = mAccount.getRemoteStore();
|
||||||
|
|
||||||
if (store instanceof WebDavStore) {
|
if (store instanceof WebDavStore) {
|
||||||
@ -133,7 +130,7 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
|
|||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mCheckOutgoing) {
|
if (mDirection.equals(CheckDirection.OUTGOING)) {
|
||||||
if (!(mAccount.getRemoteStore() instanceof WebDavStore)) {
|
if (!(mAccount.getRemoteStore() instanceof WebDavStore)) {
|
||||||
setMessage(R.string.account_setup_check_settings_check_outgoing_msg);
|
setMessage(R.string.account_setup_check_settings_check_outgoing_msg);
|
||||||
}
|
}
|
||||||
@ -366,21 +363,14 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
|
|||||||
new DialogInterface.OnClickListener() {
|
new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
try {
|
try {
|
||||||
String alias = mAccount.getUuid();
|
mAccount.addCertificate(mDirection, chain[0]);
|
||||||
if (mCheckIncoming) {
|
|
||||||
alias = alias + ".incoming";
|
|
||||||
}
|
|
||||||
if (mCheckOutgoing) {
|
|
||||||
alias = alias + ".outgoing";
|
|
||||||
}
|
|
||||||
TrustManagerFactory.addCertificateChain(alias, chain);
|
|
||||||
} catch (CertificateException e) {
|
} catch (CertificateException e) {
|
||||||
showErrorDialog(
|
showErrorDialog(
|
||||||
R.string.account_setup_failed_dlg_certificate_message_fmt,
|
R.string.account_setup_failed_dlg_certificate_message_fmt,
|
||||||
e.getMessage() == null ? "" : e.getMessage());
|
e.getMessage() == null ? "" : e.getMessage());
|
||||||
}
|
}
|
||||||
AccountSetupCheckSettings.actionCheckSettings(AccountSetupCheckSettings.this, mAccount,
|
AccountSetupCheckSettings.actionCheckSettings(AccountSetupCheckSettings.this, mAccount,
|
||||||
mCheckIncoming, mCheckOutgoing);
|
mDirection);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.setNegativeButton(
|
.setNegativeButton(
|
||||||
|
@ -16,6 +16,7 @@ import android.widget.CompoundButton.OnCheckedChangeListener;
|
|||||||
|
|
||||||
import com.fsck.k9.*;
|
import com.fsck.k9.*;
|
||||||
import com.fsck.k9.activity.K9Activity;
|
import com.fsck.k9.activity.K9Activity;
|
||||||
|
import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection;
|
||||||
import com.fsck.k9.helper.Utility;
|
import com.fsck.k9.helper.Utility;
|
||||||
import com.fsck.k9.mail.ConnectionSecurity;
|
import com.fsck.k9.mail.ConnectionSecurity;
|
||||||
import com.fsck.k9.mail.ServerSettings;
|
import com.fsck.k9.mail.ServerSettings;
|
||||||
@ -427,6 +428,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
|||||||
mWebdavMailboxPathView.getText().toString());
|
mWebdavMailboxPathView.getText().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mAccount.deleteCertificate(host, port, CheckDirection.INCOMING);
|
||||||
ServerSettings settings = new ServerSettings(mStoreType, host, port,
|
ServerSettings settings = new ServerSettings(mStoreType, host, port,
|
||||||
connectionSecurity, authType, username, password, extra);
|
connectionSecurity, authType, username, password, extra);
|
||||||
|
|
||||||
@ -437,7 +439,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
|||||||
mAccount.setCompression(Account.TYPE_OTHER, mCompressionOther.isChecked());
|
mAccount.setCompression(Account.TYPE_OTHER, mCompressionOther.isChecked());
|
||||||
mAccount.setSubscribedFoldersOnly(mSubscribedFoldersOnly.isChecked());
|
mAccount.setSubscribedFoldersOnly(mSubscribedFoldersOnly.isChecked());
|
||||||
|
|
||||||
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, true, false);
|
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.INCOMING);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
failure(e);
|
failure(e);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import android.widget.*;
|
|||||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||||
import com.fsck.k9.*;
|
import com.fsck.k9.*;
|
||||||
import com.fsck.k9.activity.K9Activity;
|
import com.fsck.k9.activity.K9Activity;
|
||||||
|
import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection;
|
||||||
import com.fsck.k9.helper.Utility;
|
import com.fsck.k9.helper.Utility;
|
||||||
import com.fsck.k9.mail.transport.SmtpTransport;
|
import com.fsck.k9.mail.transport.SmtpTransport;
|
||||||
|
|
||||||
@ -95,7 +96,7 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
|
|||||||
try {
|
try {
|
||||||
if (new URI(mAccount.getStoreUri()).getScheme().startsWith("webdav")) {
|
if (new URI(mAccount.getStoreUri()).getScheme().startsWith("webdav")) {
|
||||||
mAccount.setTransportUri(mAccount.getStoreUri());
|
mAccount.setTransportUri(mAccount.getStoreUri());
|
||||||
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, false, true);
|
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.OUTGOING);
|
||||||
}
|
}
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
@ -308,10 +309,12 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
|
|||||||
if (mRequireLoginView.isChecked()) {
|
if (mRequireLoginView.isChecked()) {
|
||||||
userInfo = usernameEnc + ":" + passwordEnc + ":" + authType;
|
userInfo = usernameEnc + ":" + passwordEnc + ":" + authType;
|
||||||
}
|
}
|
||||||
uri = new URI(smtpSchemes[securityType], userInfo, mServerView.getText().toString(),
|
String newHost = mServerView.getText().toString();
|
||||||
Integer.parseInt(mPortView.getText().toString()), null, null, null);
|
int newPort = Integer.parseInt(mPortView.getText().toString());
|
||||||
|
uri = new URI(smtpSchemes[securityType], userInfo, newHost, newPort, null, null, null);
|
||||||
|
mAccount.deleteCertificate(newHost, newPort, CheckDirection.OUTGOING);
|
||||||
mAccount.setTransportUri(uri.toString());
|
mAccount.setTransportUri(uri.toString());
|
||||||
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, false, true);
|
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.OUTGOING);
|
||||||
} catch (UnsupportedEncodingException enc) {
|
} catch (UnsupportedEncodingException enc) {
|
||||||
// This really shouldn't happen since the encoding is hardcoded to UTF-8
|
// This really shouldn't happen since the encoding is hardcoded to UTF-8
|
||||||
Log.e(K9.LOG_TAG, "Couldn't urlencode username or password.", enc);
|
Log.e(K9.LOG_TAG, "Couldn't urlencode username or password.", enc);
|
||||||
|
@ -53,6 +53,7 @@ import com.fsck.k9.activity.FolderList;
|
|||||||
import com.fsck.k9.activity.MessageList;
|
import com.fsck.k9.activity.MessageList;
|
||||||
import com.fsck.k9.activity.MessageReference;
|
import com.fsck.k9.activity.MessageReference;
|
||||||
import com.fsck.k9.activity.NotificationDeleteConfirmation;
|
import com.fsck.k9.activity.NotificationDeleteConfirmation;
|
||||||
|
import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection;
|
||||||
import com.fsck.k9.activity.setup.AccountSetupIncoming;
|
import com.fsck.k9.activity.setup.AccountSetupIncoming;
|
||||||
import com.fsck.k9.activity.setup.AccountSetupOutgoing;
|
import com.fsck.k9.activity.setup.AccountSetupOutgoing;
|
||||||
import com.fsck.k9.cache.EmailProviderCache;
|
import com.fsck.k9.cache.EmailProviderCache;
|
||||||
@ -2671,14 +2672,13 @@ public class MessagingController implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void clearCertificateErrorNotifications(Context context,
|
public void clearCertificateErrorNotifications(Context context,
|
||||||
final Account account, boolean incoming, boolean outgoing) {
|
final Account account, CheckDirection direction) {
|
||||||
final NotificationManager nm = (NotificationManager)
|
final NotificationManager nm = (NotificationManager)
|
||||||
context.getSystemService(Context.NOTIFICATION_SERVICE);
|
context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
|
||||||
if (incoming) {
|
if (direction.equals(CheckDirection.INCOMING)) {
|
||||||
nm.cancel(null, K9.CERTIFICATE_EXCEPTION_NOTIFICATION_INCOMING + account.getAccountNumber());
|
nm.cancel(null, K9.CERTIFICATE_EXCEPTION_NOTIFICATION_INCOMING + account.getAccountNumber());
|
||||||
}
|
} else {
|
||||||
if (outgoing) {
|
|
||||||
nm.cancel(null, K9.CERTIFICATE_EXCEPTION_NOTIFICATION_OUTGOING + account.getAccountNumber());
|
nm.cancel(null, K9.CERTIFICATE_EXCEPTION_NOTIFICATION_OUTGOING + account.getAccountNumber());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,6 +96,8 @@ import com.fsck.k9.mail.store.ImapResponseParser.ImapList;
|
|||||||
import com.fsck.k9.mail.store.ImapResponseParser.ImapResponse;
|
import com.fsck.k9.mail.store.ImapResponseParser.ImapResponse;
|
||||||
import com.fsck.k9.mail.store.imap.ImapUtility;
|
import com.fsck.k9.mail.store.imap.ImapUtility;
|
||||||
import com.fsck.k9.mail.transport.imap.ImapSettings;
|
import com.fsck.k9.mail.transport.imap.ImapSettings;
|
||||||
|
import com.fsck.k9.net.ssl.TrustManagerFactory;
|
||||||
|
import com.fsck.k9.net.ssl.TrustedSocketFactory;
|
||||||
import com.jcraft.jzlib.JZlib;
|
import com.jcraft.jzlib.JZlib;
|
||||||
import com.jcraft.jzlib.ZOutputStream;
|
import com.jcraft.jzlib.ZOutputStream;
|
||||||
|
|
||||||
@ -2446,9 +2448,12 @@ public class ImapStore extends Store {
|
|||||||
connectionSecurity == CONNECTION_SECURITY_SSL_OPTIONAL) {
|
connectionSecurity == CONNECTION_SECURITY_SSL_OPTIONAL) {
|
||||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
boolean secure = connectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED;
|
boolean secure = connectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED;
|
||||||
sslContext.init(null, new TrustManager[] {
|
sslContext
|
||||||
TrustManagerFactory.get(mSettings.getHost(), secure)
|
.init(null,
|
||||||
}, new SecureRandom());
|
new TrustManager[] { TrustManagerFactory.get(
|
||||||
|
mSettings.getHost(),
|
||||||
|
mSettings.getPort(), secure) },
|
||||||
|
new SecureRandom());
|
||||||
mSocket = TrustedSocketFactory.createSocket(sslContext);
|
mSocket = TrustedSocketFactory.createSocket(sslContext);
|
||||||
} else {
|
} else {
|
||||||
mSocket = new Socket();
|
mSocket = new Socket();
|
||||||
@ -2501,9 +2506,11 @@ public class ImapStore extends Store {
|
|||||||
|
|
||||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
boolean secure = mSettings.getConnectionSecurity() == CONNECTION_SECURITY_TLS_REQUIRED;
|
boolean secure = mSettings.getConnectionSecurity() == CONNECTION_SECURITY_TLS_REQUIRED;
|
||||||
sslContext.init(null, new TrustManager[] {
|
sslContext.init(null,
|
||||||
TrustManagerFactory.get(mSettings.getHost(), secure)
|
new TrustManager[] { TrustManagerFactory.get(
|
||||||
}, new SecureRandom());
|
mSettings.getHost(),
|
||||||
|
mSettings.getPort(), secure) },
|
||||||
|
new SecureRandom());
|
||||||
mSocket = TrustedSocketFactory.createSocket(sslContext, mSocket,
|
mSocket = TrustedSocketFactory.createSocket(sslContext, mSocket,
|
||||||
mSettings.getHost(), mSettings.getPort(), true);
|
mSettings.getHost(), mSettings.getPort(), true);
|
||||||
mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT);
|
mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT);
|
||||||
|
@ -10,6 +10,8 @@ import com.fsck.k9.helper.Utility;
|
|||||||
import com.fsck.k9.mail.*;
|
import com.fsck.k9.mail.*;
|
||||||
|
|
||||||
import com.fsck.k9.mail.internet.MimeMessage;
|
import com.fsck.k9.mail.internet.MimeMessage;
|
||||||
|
import com.fsck.k9.net.ssl.TrustManagerFactory;
|
||||||
|
import com.fsck.k9.net.ssl.TrustedSocketFactory;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
@ -327,9 +329,9 @@ public class Pop3Store extends Store {
|
|||||||
mConnectionSecurity == CONNECTION_SECURITY_SSL_OPTIONAL) {
|
mConnectionSecurity == CONNECTION_SECURITY_SSL_OPTIONAL) {
|
||||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
final boolean secure = mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED;
|
final boolean secure = mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED;
|
||||||
sslContext.init(null, new TrustManager[] {
|
sslContext.init(null,
|
||||||
TrustManagerFactory.get(mHost, secure)
|
new TrustManager[] { TrustManagerFactory.get(mHost,
|
||||||
}, new SecureRandom());
|
mPort, secure) }, new SecureRandom());
|
||||||
mSocket = TrustedSocketFactory.createSocket(sslContext);
|
mSocket = TrustedSocketFactory.createSocket(sslContext);
|
||||||
} else {
|
} else {
|
||||||
mSocket = new Socket();
|
mSocket = new Socket();
|
||||||
@ -356,9 +358,10 @@ public class Pop3Store extends Store {
|
|||||||
|
|
||||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
boolean secure = mConnectionSecurity == CONNECTION_SECURITY_TLS_REQUIRED;
|
boolean secure = mConnectionSecurity == CONNECTION_SECURITY_TLS_REQUIRED;
|
||||||
sslContext.init(null, new TrustManager[] {
|
sslContext.init(null,
|
||||||
TrustManagerFactory.get(mHost, secure)
|
new TrustManager[] { TrustManagerFactory.get(
|
||||||
}, new SecureRandom());
|
mHost, mPort, secure) },
|
||||||
|
new SecureRandom());
|
||||||
mSocket = TrustedSocketFactory.createSocket(sslContext, mSocket, mHost,
|
mSocket = TrustedSocketFactory.createSocket(sslContext, mSocket, mHost,
|
||||||
mPort, true);
|
mPort, true);
|
||||||
mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT);
|
mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT);
|
||||||
|
@ -1,214 +0,0 @@
|
|||||||
|
|
||||||
package com.fsck.k9.mail.store;
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.Log;
|
|
||||||
import com.fsck.k9.K9;
|
|
||||||
import com.fsck.k9.helper.DomainNameChecker;
|
|
||||||
import com.fsck.k9.mail.CertificateChainException;
|
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
|
|
||||||
import javax.net.ssl.TrustManager;
|
|
||||||
import javax.net.ssl.X509TrustManager;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.KeyStore;
|
|
||||||
import java.security.KeyStoreException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public final class TrustManagerFactory {
|
|
||||||
private static final String LOG_TAG = "TrustManagerFactory";
|
|
||||||
|
|
||||||
private static X509TrustManager defaultTrustManager;
|
|
||||||
private static X509TrustManager unsecureTrustManager;
|
|
||||||
private static X509TrustManager localTrustManager;
|
|
||||||
|
|
||||||
private static File keyStoreFile;
|
|
||||||
private static KeyStore keyStore;
|
|
||||||
|
|
||||||
|
|
||||||
private static class SimpleX509TrustManager implements X509TrustManager {
|
|
||||||
public void checkClientTrusted(X509Certificate[] chain, String authType)
|
|
||||||
throws CertificateException {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void checkServerTrusted(X509Certificate[] chain, String authType)
|
|
||||||
throws CertificateException {
|
|
||||||
}
|
|
||||||
|
|
||||||
public X509Certificate[] getAcceptedIssuers() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SecureX509TrustManager implements X509TrustManager {
|
|
||||||
private static final Map<String, SecureX509TrustManager> mTrustManager =
|
|
||||||
new HashMap<String, SecureX509TrustManager>();
|
|
||||||
|
|
||||||
private final String mHost;
|
|
||||||
|
|
||||||
private SecureX509TrustManager(String host) {
|
|
||||||
mHost = host;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized static X509TrustManager getInstance(String host) {
|
|
||||||
SecureX509TrustManager trustManager;
|
|
||||||
if (mTrustManager.containsKey(host)) {
|
|
||||||
trustManager = mTrustManager.get(host);
|
|
||||||
} else {
|
|
||||||
trustManager = new SecureX509TrustManager(host);
|
|
||||||
mTrustManager.put(host, trustManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
return trustManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void checkClientTrusted(X509Certificate[] chain, String authType)
|
|
||||||
throws CertificateException {
|
|
||||||
defaultTrustManager.checkClientTrusted(chain, authType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void checkServerTrusted(X509Certificate[] chain, String authType)
|
|
||||||
throws CertificateException {
|
|
||||||
try {
|
|
||||||
defaultTrustManager.checkServerTrusted(chain, authType);
|
|
||||||
} catch (CertificateException e) {
|
|
||||||
try {
|
|
||||||
localTrustManager.checkServerTrusted(
|
|
||||||
new X509Certificate[] { chain[0] }, authType);
|
|
||||||
} catch (CertificateException ce) {
|
|
||||||
throw new CertificateChainException(ce, chain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!DomainNameChecker.match(chain[0], mHost)) {
|
|
||||||
try {
|
|
||||||
String dn = chain[0].getSubjectDN().toString();
|
|
||||||
if ((dn != null) && (dn.equalsIgnoreCase(keyStore.getCertificateAlias(chain[0])))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (KeyStoreException e) {
|
|
||||||
throw new CertificateException("Certificate cannot be verified; KeyStore Exception: " + e);
|
|
||||||
}
|
|
||||||
throw new CertificateChainException(
|
|
||||||
"Certificate domain name does not match " + mHost,
|
|
||||||
chain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public X509Certificate[] getAcceptedIssuers() {
|
|
||||||
return defaultTrustManager.getAcceptedIssuers();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
|
||||||
java.io.InputStream fis = null;
|
|
||||||
try {
|
|
||||||
javax.net.ssl.TrustManagerFactory tmf = javax.net.ssl.TrustManagerFactory.getInstance("X509");
|
|
||||||
Application app = K9.app;
|
|
||||||
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);
|
|
||||||
TrustManager[] tms = tmf.getTrustManagers();
|
|
||||||
if (tms != null) {
|
|
||||||
for (TrustManager tm : tms) {
|
|
||||||
if (tm instanceof X509TrustManager) {
|
|
||||||
localTrustManager = (X509TrustManager)tm;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmf = javax.net.ssl.TrustManagerFactory.getInstance("X509");
|
|
||||||
tmf.init((KeyStore)null);
|
|
||||||
tms = tmf.getTrustManagers();
|
|
||||||
if (tms != null) {
|
|
||||||
for (TrustManager tm : tms) {
|
|
||||||
if (tm instanceof X509TrustManager) {
|
|
||||||
defaultTrustManager = (X509TrustManager) tm;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
Log.e(LOG_TAG, "Unable to get X509 Trust Manager ", e);
|
|
||||||
} catch (KeyStoreException e) {
|
|
||||||
Log.e(LOG_TAG, "Key Store exception while initializing TrustManagerFactory ", e);
|
|
||||||
} finally {
|
|
||||||
IOUtils.closeQuietly(fis);
|
|
||||||
}
|
|
||||||
unsecureTrustManager = new SimpleX509TrustManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
private TrustManagerFactory() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static X509TrustManager get(String host, boolean secure) {
|
|
||||||
return secure ? SecureX509TrustManager.getInstance(host) :
|
|
||||||
unsecureTrustManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static KeyStore getKeyStore() {
|
|
||||||
return keyStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addCertificateChain(String alias, X509Certificate[] chain) throws CertificateException {
|
|
||||||
try {
|
|
||||||
javax.net.ssl.TrustManagerFactory tmf = javax.net.ssl.TrustManagerFactory.getInstance("X509");
|
|
||||||
for (X509Certificate element : chain) {
|
|
||||||
keyStore.setCertificateEntry
|
|
||||||
(element.getSubjectDN().toString(), element);
|
|
||||||
}
|
|
||||||
|
|
||||||
tmf.init(keyStore);
|
|
||||||
TrustManager[] tms = tmf.getTrustManagers();
|
|
||||||
if (tms != null) {
|
|
||||||
for (TrustManager tm : tms) {
|
|
||||||
if (tm instanceof X509TrustManager) {
|
|
||||||
localTrustManager = (X509TrustManager) tm;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
java.io.OutputStream keyStoreStream = null;
|
|
||||||
try {
|
|
||||||
keyStoreStream = new java.io.FileOutputStream(keyStoreFile);
|
|
||||||
keyStore.store(keyStoreStream, "".toCharArray());
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
throw new CertificateException("Unable to write KeyStore: " + e.getMessage());
|
|
||||||
} catch (CertificateException e) {
|
|
||||||
throw new CertificateException("Unable to write KeyStore: " + e.getMessage());
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new CertificateException("Unable to write KeyStore: " + e.getMessage());
|
|
||||||
} finally {
|
|
||||||
IOUtils.closeQuietly(keyStoreStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
Log.e(LOG_TAG, "Unable to get X509 Trust Manager ", e);
|
|
||||||
} catch (KeyStoreException e) {
|
|
||||||
Log.e(LOG_TAG, "Key Store exception while initializing TrustManagerFactory ", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,6 +4,8 @@ import org.apache.http.conn.ConnectTimeoutException;
|
|||||||
import org.apache.http.conn.scheme.LayeredSocketFactory;
|
import org.apache.http.conn.scheme.LayeredSocketFactory;
|
||||||
import org.apache.http.params.HttpParams;
|
import org.apache.http.params.HttpParams;
|
||||||
|
|
||||||
|
import com.fsck.k9.net.ssl.TrustManagerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
@ -26,10 +28,10 @@ public class WebDavSocketFactory implements LayeredSocketFactory {
|
|||||||
private SSLSocketFactory mSocketFactory;
|
private SSLSocketFactory mSocketFactory;
|
||||||
private org.apache.http.conn.ssl.SSLSocketFactory mSchemeSocketFactory;
|
private org.apache.http.conn.ssl.SSLSocketFactory mSchemeSocketFactory;
|
||||||
|
|
||||||
public WebDavSocketFactory(String host, boolean secure) throws NoSuchAlgorithmException, KeyManagementException {
|
public WebDavSocketFactory(String host, int port, boolean secure) throws NoSuchAlgorithmException, KeyManagementException {
|
||||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
sslContext.init(null, new TrustManager[] {
|
sslContext.init(null, new TrustManager[] {
|
||||||
TrustManagerFactory.get(host, secure)
|
TrustManagerFactory.get(host, port, secure)
|
||||||
}, new SecureRandom());
|
}, new SecureRandom());
|
||||||
mSocketFactory = sslContext.getSocketFactory();
|
mSocketFactory = sslContext.getSocketFactory();
|
||||||
mSchemeSocketFactory = org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory();
|
mSchemeSocketFactory = org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory();
|
||||||
|
@ -1079,7 +1079,7 @@ public class WebDavStore extends Store {
|
|||||||
|
|
||||||
SchemeRegistry reg = mHttpClient.getConnectionManager().getSchemeRegistry();
|
SchemeRegistry reg = mHttpClient.getConnectionManager().getSchemeRegistry();
|
||||||
try {
|
try {
|
||||||
Scheme s = new Scheme("https", new WebDavSocketFactory(mHost, mSecure), 443);
|
Scheme s = new Scheme("https", new WebDavSocketFactory(mHost, 443, mSecure), 443);
|
||||||
reg.register(s);
|
reg.register(s);
|
||||||
} catch (NoSuchAlgorithmException nsa) {
|
} catch (NoSuchAlgorithmException nsa) {
|
||||||
Log.e(K9.LOG_TAG, "NoSuchAlgorithmException in getHttpClient: " + nsa);
|
Log.e(K9.LOG_TAG, "NoSuchAlgorithmException in getHttpClient: " + nsa);
|
||||||
|
@ -12,9 +12,9 @@ import com.fsck.k9.mail.filter.LineWrapOutputStream;
|
|||||||
import com.fsck.k9.mail.filter.PeekableInputStream;
|
import com.fsck.k9.mail.filter.PeekableInputStream;
|
||||||
import com.fsck.k9.mail.filter.SmtpDataStuffing;
|
import com.fsck.k9.mail.filter.SmtpDataStuffing;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.store.TrustManagerFactory;
|
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
||||||
import com.fsck.k9.mail.store.TrustedSocketFactory;
|
import com.fsck.k9.net.ssl.TrustManagerFactory;
|
||||||
|
import com.fsck.k9.net.ssl.TrustedSocketFactory;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
@ -242,9 +242,10 @@ public class SmtpTransport extends Transport {
|
|||||||
mConnectionSecurity == CONNECTION_SECURITY_SSL_OPTIONAL) {
|
mConnectionSecurity == CONNECTION_SECURITY_SSL_OPTIONAL) {
|
||||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
boolean secure = mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED;
|
boolean secure = mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED;
|
||||||
sslContext.init(null, new TrustManager[] {
|
sslContext.init(null,
|
||||||
TrustManagerFactory.get(mHost, secure)
|
new TrustManager[] { TrustManagerFactory.get(
|
||||||
}, new SecureRandom());
|
mHost, mPort, secure) },
|
||||||
|
new SecureRandom());
|
||||||
mSocket = TrustedSocketFactory.createSocket(sslContext);
|
mSocket = TrustedSocketFactory.createSocket(sslContext);
|
||||||
mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
|
mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
|
||||||
} else {
|
} else {
|
||||||
@ -301,9 +302,9 @@ public class SmtpTransport extends Transport {
|
|||||||
|
|
||||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
boolean secure = mConnectionSecurity == CONNECTION_SECURITY_TLS_REQUIRED;
|
boolean secure = mConnectionSecurity == CONNECTION_SECURITY_TLS_REQUIRED;
|
||||||
sslContext.init(null, new TrustManager[] {
|
sslContext.init(null,
|
||||||
TrustManagerFactory.get(mHost, secure)
|
new TrustManager[] { TrustManagerFactory.get(mHost,
|
||||||
}, new SecureRandom());
|
mPort, secure) }, new SecureRandom());
|
||||||
mSocket = TrustedSocketFactory.createSocket(sslContext, mSocket, mHost,
|
mSocket = TrustedSocketFactory.createSocket(sslContext, mSocket, mHost,
|
||||||
mPort, true);
|
mPort, true);
|
||||||
mIn = new PeekableInputStream(new BufferedInputStream(mSocket.getInputStream(),
|
mIn = new PeekableInputStream(new BufferedInputStream(mSocket.getInputStream(),
|
||||||
|
133
src/com/fsck/k9/net/ssl/TrustManagerFactory.java
Normal file
133
src/com/fsck/k9/net/ssl/TrustManagerFactory.java
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
|
||||||
|
package com.fsck.k9.net.ssl;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.fsck.k9.helper.DomainNameChecker;
|
||||||
|
import com.fsck.k9.mail.CertificateChainException;
|
||||||
|
import com.fsck.k9.security.LocalKeyStore;
|
||||||
|
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class TrustManagerFactory {
|
||||||
|
private static final String LOG_TAG = "TrustManagerFactory";
|
||||||
|
|
||||||
|
private static X509TrustManager defaultTrustManager;
|
||||||
|
private static X509TrustManager unsecureTrustManager;
|
||||||
|
|
||||||
|
private static LocalKeyStore keyStore;
|
||||||
|
|
||||||
|
private static class SimpleX509TrustManager implements X509TrustManager {
|
||||||
|
public void checkClientTrusted(X509Certificate[] chain, String authType)
|
||||||
|
throws CertificateException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkServerTrusted(X509Certificate[] chain, String authType)
|
||||||
|
throws CertificateException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SecureX509TrustManager implements X509TrustManager {
|
||||||
|
private static final Map<String, SecureX509TrustManager> mTrustManager =
|
||||||
|
new HashMap<String, SecureX509TrustManager>();
|
||||||
|
|
||||||
|
private final String mHost;
|
||||||
|
private final int mPort;
|
||||||
|
|
||||||
|
private SecureX509TrustManager(String host, int port) {
|
||||||
|
mHost = host;
|
||||||
|
mPort = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static X509TrustManager getInstance(String host, int port) {
|
||||||
|
String key = host + ":" + port;
|
||||||
|
SecureX509TrustManager trustManager;
|
||||||
|
if (mTrustManager.containsKey(key)) {
|
||||||
|
trustManager = mTrustManager.get(key);
|
||||||
|
} else {
|
||||||
|
trustManager = new SecureX509TrustManager(host, port);
|
||||||
|
mTrustManager.put(key, trustManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
return trustManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkClientTrusted(X509Certificate[] chain, String authType)
|
||||||
|
throws CertificateException {
|
||||||
|
defaultTrustManager.checkClientTrusted(chain, authType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkServerTrusted(X509Certificate[] chain, String authType)
|
||||||
|
throws CertificateException {
|
||||||
|
boolean foundInGlobalKeyStore = false;
|
||||||
|
try {
|
||||||
|
defaultTrustManager.checkServerTrusted(chain, authType);
|
||||||
|
foundInGlobalKeyStore = true;
|
||||||
|
} catch (CertificateException e) { /* ignore */ }
|
||||||
|
|
||||||
|
X509Certificate certificate = chain[0];
|
||||||
|
|
||||||
|
// Check the local key store if we couldn't verify the certificate using the global
|
||||||
|
// key store or if the host name doesn't match the certificate name
|
||||||
|
if (foundInGlobalKeyStore
|
||||||
|
&& DomainNameChecker.match(certificate, mHost)
|
||||||
|
|| keyStore.isValidCertificate(certificate, mHost, mPort)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String message = (foundInGlobalKeyStore) ?
|
||||||
|
"Certificate domain name does not match " + mHost :
|
||||||
|
"Couldn't find certificate in local key store";
|
||||||
|
|
||||||
|
throw new CertificateChainException(message, chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return defaultTrustManager.getAcceptedIssuers();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
keyStore = LocalKeyStore.getInstance();
|
||||||
|
|
||||||
|
javax.net.ssl.TrustManagerFactory tmf = javax.net.ssl.TrustManagerFactory.getInstance("X509");
|
||||||
|
tmf.init((KeyStore) null);
|
||||||
|
|
||||||
|
TrustManager[] tms = tmf.getTrustManagers();
|
||||||
|
if (tms != null) {
|
||||||
|
for (TrustManager tm : tms) {
|
||||||
|
if (tm instanceof X509TrustManager) {
|
||||||
|
defaultTrustManager = (X509TrustManager) tm;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Log.e(LOG_TAG, "Unable to get X509 Trust Manager ", e);
|
||||||
|
} catch (KeyStoreException e) {
|
||||||
|
Log.e(LOG_TAG, "Key Store exception while initializing TrustManagerFactory ", e);
|
||||||
|
}
|
||||||
|
unsecureTrustManager = new SimpleX509TrustManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TrustManagerFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static X509TrustManager get(String host, int port, boolean secure) {
|
||||||
|
return secure ? SecureX509TrustManager.getInstance(host, port) :
|
||||||
|
unsecureTrustManager;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.fsck.k9.mail.store;
|
package com.fsck.k9.net.ssl;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLSocket;
|
import javax.net.ssl.SSLSocket;
|
@ -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() {
|
||||||
|
181
src/com/fsck/k9/security/LocalKeyStore.java
Normal file
181
src/com/fsck/k9/security/LocalKeyStore.java
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
package com.fsck.k9.security;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.fsck.k9.K9;
|
||||||
|
|
||||||
|
public class LocalKeyStore {
|
||||||
|
private static final int KEY_STORE_FILE_VERSION = 1;
|
||||||
|
|
||||||
|
private static String sKeyStoreLocation;
|
||||||
|
|
||||||
|
public static void setKeyStoreLocation(String directory) {
|
||||||
|
sKeyStoreLocation = directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LocalKeyStoreHolder {
|
||||||
|
static final LocalKeyStore INSTANCE = new LocalKeyStore();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LocalKeyStore getInstance() {
|
||||||
|
return LocalKeyStoreHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private File mKeyStoreFile;
|
||||||
|
private KeyStore mKeyStore;
|
||||||
|
|
||||||
|
|
||||||
|
private LocalKeyStore() {
|
||||||
|
if (sKeyStoreLocation == null) {
|
||||||
|
Log.e(K9.LOG_TAG, "Local key store location has not been initialized");
|
||||||
|
} else {
|
||||||
|
upgradeKeyStoreFile();
|
||||||
|
setKeyStoreFile(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reinitialize the local key store with certificates contained in
|
||||||
|
* {@code file}
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* {@link File} containing locally saved certificates. May be 0
|
||||||
|
* length, in which case it is deleted and recreated. May be
|
||||||
|
* {@code null}, in which case a default file location is used.
|
||||||
|
*/
|
||||||
|
public synchronized void setKeyStoreFile(File file) {
|
||||||
|
if (file == null) {
|
||||||
|
file = new File(getKeyStoreFilePath(KEY_STORE_FILE_VERSION));
|
||||||
|
}
|
||||||
|
if (file.length() == 0) {
|
||||||
|
/*
|
||||||
|
* The file may be empty (e.g., if it was created with
|
||||||
|
* File.createTempFile). We can't pass an empty file to
|
||||||
|
* Keystore.load. Instead, we let it be created anew.
|
||||||
|
*/
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInputStream fis = null;
|
||||||
|
try {
|
||||||
|
fis = new FileInputStream(file);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
// If the file doesn't exist, that's fine, too
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
|
store.load(fis, "".toCharArray());
|
||||||
|
mKeyStore = store;
|
||||||
|
mKeyStoreFile = file;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Failed to initialize local key store", e);
|
||||||
|
// Use of the local key store is effectively disabled.
|
||||||
|
mKeyStore = null;
|
||||||
|
mKeyStoreFile = null;
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(fis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void addCertificate(String host, int port,
|
||||||
|
X509Certificate certificate) throws CertificateException {
|
||||||
|
if (mKeyStore == null) {
|
||||||
|
throw new CertificateException(
|
||||||
|
"Certificate not added because key store not initialized");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mKeyStore.setCertificateEntry(getCertKey(host, port), certificate);
|
||||||
|
} catch (KeyStoreException e) {
|
||||||
|
throw new CertificateException(
|
||||||
|
"Failed to add certificate to local key store", e);
|
||||||
|
}
|
||||||
|
writeCertificateFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeCertificateFile() throws CertificateException {
|
||||||
|
java.io.OutputStream keyStoreStream = null;
|
||||||
|
try {
|
||||||
|
keyStoreStream = new java.io.FileOutputStream(mKeyStoreFile);
|
||||||
|
mKeyStore.store(keyStoreStream, "".toCharArray());
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
throw new CertificateException("Unable to write KeyStore: "
|
||||||
|
+ e.getMessage());
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
throw new CertificateException("Unable to write KeyStore: "
|
||||||
|
+ e.getMessage());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new CertificateException("Unable to write KeyStore: "
|
||||||
|
+ e.getMessage());
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new CertificateException("Unable to write KeyStore: "
|
||||||
|
+ e.getMessage());
|
||||||
|
} catch (KeyStoreException e) {
|
||||||
|
throw new CertificateException("Unable to write KeyStore: "
|
||||||
|
+ e.getMessage());
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(keyStoreStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean isValidCertificate(Certificate certificate,
|
||||||
|
String host, int port) {
|
||||||
|
if (mKeyStore == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Certificate storedCert = null;
|
||||||
|
try {
|
||||||
|
storedCert = mKeyStore.getCertificate(getCertKey(host, port));
|
||||||
|
return (storedCert != null && storedCert.equals(certificate));
|
||||||
|
} catch (KeyStoreException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getCertKey(String host, int port) {
|
||||||
|
return host + ":" + port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void deleteCertificate(String oldHost, int oldPort) {
|
||||||
|
if (mKeyStore == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mKeyStore.deleteEntry(getCertKey(oldHost, oldPort));
|
||||||
|
writeCertificateFile();
|
||||||
|
} catch (KeyStoreException e) {
|
||||||
|
// Ignore: most likely there was no cert. found
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Error updating the local key store file", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void upgradeKeyStoreFile() {
|
||||||
|
if (KEY_STORE_FILE_VERSION > 0) {
|
||||||
|
// Blow away version "0" because certificate aliases have changed.
|
||||||
|
new File(getKeyStoreFilePath(0)).delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getKeyStoreFilePath(int version) {
|
||||||
|
if (version < 1) {
|
||||||
|
return sKeyStoreLocation + File.separator + "KeyStore.bks";
|
||||||
|
} else {
|
||||||
|
return sKeyStoreLocation + File.separator + "KeyStore_v" + version + ".bks";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
335
tests/src/com/fsck/k9/net/ssl/TrustManagerFactoryTest.java
Normal file
335
tests/src/com/fsck/k9/net/ssl/TrustManagerFactoryTest.java
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
package com.fsck.k9.net.ssl;
|
||||||
|
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
|
||||||
|
import com.fsck.k9.security.LocalKeyStore;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
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 static final String K9_EXAMPLE_COM_CERT1 =
|
||||||
|
"-----BEGIN CERTIFICATE-----\n"
|
||||||
|
+ "MIICCTCCAXICCQD/R0TV7d0C5TANBgkqhkiG9w0BAQUFADBJMQswCQYDVQQGEwJD\n"
|
||||||
|
+ "SDETMBEGA1UECBMKU29tZS1TdGF0ZTEMMAoGA1UEChMDSy05MRcwFQYDVQQDEw5r\n"
|
||||||
|
+ "OS5leGFtcGxlLmNvbTAeFw0xMTA5MDYxOTU3MzVaFw0yMTA5MDMxOTU3MzVaMEkx\n"
|
||||||
|
+ "CzAJBgNVBAYTAkNIMRMwEQYDVQQIEwpTb21lLVN0YXRlMQwwCgYDVQQKEwNLLTkx\n"
|
||||||
|
+ "FzAVBgNVBAMTDms5LmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB\n"
|
||||||
|
+ "iQKBgQCp7FvHRaQaOIu3iyB5GB0PtPCxy/bLlBxBb8p9QsMimX2Yz3SNjWVUzU5N\n"
|
||||||
|
+ "ggpXmmeGopLAnvZlhWYSx0yIGWwPB44kGK5eaYDRWav+K+XXgdNCJij1UWPSmFwZ\n"
|
||||||
|
+ "hUoNbrahco5AFw0jC1qi+3Dht6Y64nfNzTOYTcm1Pz4tqXiADQIDAQABMA0GCSqG\n"
|
||||||
|
+ "SIb3DQEBBQUAA4GBAIPsgd6fuFRojSOAcUyhaoKaY5hXJf8d7R3AYWxcAPYmn6g7\n"
|
||||||
|
+ "3Zms+f7/CH0y/tM81oBTlq9ZLbrJyLzC7vG1pqWHMNaK7miAho22IRuk+HwvL6OA\n"
|
||||||
|
+ "uH3x3W1/mH4ci268cIFVmofID0nYLTqOxBTczfYhI7q0VBUXqv/bZ+3bVMSh\n"
|
||||||
|
+ "-----END CERTIFICATE-----\n";
|
||||||
|
|
||||||
|
private static final String K9_EXAMPLE_COM_CERT2 =
|
||||||
|
"-----BEGIN CERTIFICATE-----\n"
|
||||||
|
+ "MIICCTCCAXICCQDMryqq0gZ80jANBgkqhkiG9w0BAQUFADBJMQswCQYDVQQGEwJD\n"
|
||||||
|
+ "SDETMBEGA1UECBMKU29tZS1TdGF0ZTEMMAoGA1UEChMDSy05MRcwFQYDVQQDEw5r\n"
|
||||||
|
+ "OS5leGFtcGxlLmNvbTAeFw0xMTA5MDYyMDAwNTVaFw0yMTA5MDMyMDAwNTVaMEkx\n"
|
||||||
|
+ "CzAJBgNVBAYTAkNIMRMwEQYDVQQIEwpTb21lLVN0YXRlMQwwCgYDVQQKEwNLLTkx\n"
|
||||||
|
+ "FzAVBgNVBAMTDms5LmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB\n"
|
||||||
|
+ "iQKBgQDOLzRucC3tuXL/NthnGkgTnVn03balrvYPkABvvrG83Dpp5ipIC/iPsQvw\n"
|
||||||
|
+ "pvqypSNHqrloEB7o3obQ8tiRDtbOsNQ7gKJ+YoD1drDNClV0pBvr7mvRgA2AcDpw\n"
|
||||||
|
+ "CTLKwVIyKmE+rm3vl8CWFd9CqHcYQ3Mc1KXXasN4DEAzZ/sHRwIDAQABMA0GCSqG\n"
|
||||||
|
+ "SIb3DQEBBQUAA4GBAFDcHFpmZ9SUrc0WayrKNUpSaHLRG94uzIx0VUMLROcXEEWU\n"
|
||||||
|
+ "soRw1RfoSBkcy2SEjB4CAvex6qAiOT3ubXuL+BYFav/uU8JPWZ9ovSAYqBZ9aUJo\n"
|
||||||
|
+ "G6A2hvA1lpvP97qQ/NFaGQ38XqSykZamZwSx3PlZUM/i9S9n/3MfuuXWqtLC\n"
|
||||||
|
+ "-----END CERTIFICATE-----\n";
|
||||||
|
|
||||||
|
private static final String CA_CERT =
|
||||||
|
"-----BEGIN CERTIFICATE-----\n"
|
||||||
|
+ "MIIDbTCCAlWgAwIBAgIJANCdQ+Cwnyg+MA0GCSqGSIb3DQEBBQUAME0xCzAJBgNV\n"
|
||||||
|
+ "BAYTAkNIMRMwEQYDVQQIDApTb21lLVN0YXRlMQwwCgYDVQQKDANLLTkxGzAZBgNV\n"
|
||||||
|
+ "BAMMEnRlc3QtY2EuazltYWlsLm9yZzAeFw0xMzEyMDIxMjUwNThaFw0yMzExMzAx\n"
|
||||||
|
+ "MjUwNThaME0xCzAJBgNVBAYTAkNIMRMwEQYDVQQIDApTb21lLVN0YXRlMQwwCgYD\n"
|
||||||
|
+ "VQQKDANLLTkxGzAZBgNVBAMMEnRlc3QtY2EuazltYWlsLm9yZzCCASIwDQYJKoZI\n"
|
||||||
|
+ "hvcNAQEBBQADggEPADCCAQoCggEBAJ+YLg9enfFk5eba6B3LtQzUE7GiR2tIpQSi\n"
|
||||||
|
+ "zHMtHzn8KUnRDiGwC8VnSuWCOX7hXyQ0P6i2+DVRVBYOAeDCNMZHOq1hRqI66B33\n"
|
||||||
|
+ "QqLfkBnJAIDeLqfqlgigHs1+//7eagVA6Z38ZFre3PFuKnK9NCwS+gz7PKw/poIG\n"
|
||||||
|
+ "/FZP+ltMlkwvPww4S8SMlY6RXXH09+S/uM8aG6DUBT298eoAXTbSEIeaNhwBHZPe\n"
|
||||||
|
+ "rXqqzd8QDAIE9BFXSkh/BQiVEFDPSBMSdmUzUAsT2aM8osntnKWY5/G7B60wutvA\n"
|
||||||
|
+ "jYCULgtR6lR6jIDbG3ECHVDsTWR+Pgl+h1zeyERhN5iG1ffOtLUCAwEAAaNQME4w\n"
|
||||||
|
+ "HQYDVR0OBBYEFBlUYiTGlOu9zIPx8Q13xcnDL5QpMB8GA1UdIwQYMBaAFBlUYiTG\n"
|
||||||
|
+ "lOu9zIPx8Q13xcnDL5QpMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB\n"
|
||||||
|
+ "AJ6oC6O6I6p0vgA4+7dfyxKX745zl/fK6IVHV/GO75mLjVdyw00USbHGHAmZM5C6\n"
|
||||||
|
+ "eCKVV83m/Re5lHf8ZBjc+3rWdGCEjwyUwvDeUvzpcKF3wPxYDUOOqSI+np1cxj6q\n"
|
||||||
|
+ "6+XI5QXwyUObWtWyw1GOpLuFPbxny/TlRWvk8AfOaLANg3UhvITNZMdMHoQ2sJ3u\n"
|
||||||
|
+ "MrQ+CHe/Tal2MkwiCrYT91f3YWVaswiEAxpqxnwuSXnYyaJpqMCcA1txBDgX84FP\n"
|
||||||
|
+ "dSIM4ut+QltV2Tlx0lpH43dvttAwkPB+iL7ZF6zUki/Nq5aKyNoHOL88TACe18Lq\n"
|
||||||
|
+ "zOztD2HZfxhIz3uH2gXmqUo=\n"
|
||||||
|
+ "-----END CERTIFICATE-----\n";
|
||||||
|
|
||||||
|
private static final String CERT3 =
|
||||||
|
"-----BEGIN CERTIFICATE-----\n"
|
||||||
|
+ "MIIDjDCCAnSgAwIBAgIBATANBgkqhkiG9w0BAQUFADBNMQswCQYDVQQGEwJDSDET\n"
|
||||||
|
+ "MBEGA1UECAwKU29tZS1TdGF0ZTEMMAoGA1UECgwDSy05MRswGQYDVQQDDBJ0ZXN0\n"
|
||||||
|
+ "LWNhLms5bWFpbC5vcmcwHhcNMTMxMjAyMTMxNzEyWhcNMjMxMTMwMTMxNzEyWjBJ\n"
|
||||||
|
+ "MQswCQYDVQQGEwJDSDETMBEGA1UECAwKU29tZS1TdGF0ZTEMMAoGA1UECgwDSy05\n"
|
||||||
|
+ "MRcwFQYDVQQDDA5rOS5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP\n"
|
||||||
|
+ "ADCCAQoCggEBAL9OvWtLcp6bd40Hai6A6cCmJRwn3mwcTB8E41iEQgQexqx/f9RR\n"
|
||||||
|
+ "BuQi2s80k/vXq8QU2GbwGiPkBBXMUHuiT27Lsoj8kMOnH5BXeKLaWDiMpvNqfent\n"
|
||||||
|
+ "UzBXSIOK6Yu9UtlU0MzAuYxXaunrXoS5Dejrbz743P9yW8hx7pANNU0Qfck+ekR7\n"
|
||||||
|
+ "Q4PWNgfbFHrnvcobzuFzJeWg8x9iTTsVGIaX9AVMjMUlIKvhhOWTlcTJHKzU67sp\n"
|
||||||
|
+ "OLzwH9IJ3hqwdmsgZu5D/2AZlYlpFk6AlnoxNhfy9m+T41P8+iWDYCJoxvf3d6gl\n"
|
||||||
|
+ "TlZ1FL0PzPReXeAgugyJ1qx5gJ9Vhf/rBaUCAwEAAaN7MHkwCQYDVR0TBAIwADAs\n"
|
||||||
|
+ "BglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYD\n"
|
||||||
|
+ "VR0OBBYEFPm9hbTbfmcnjjfOzrec/TrvsS5ZMB8GA1UdIwQYMBaAFBlUYiTGlOu9\n"
|
||||||
|
+ "zIPx8Q13xcnDL5QpMA0GCSqGSIb3DQEBBQUAA4IBAQAgvYQoCEklJNXBwLuWpSMx\n"
|
||||||
|
+ "CQrVxLI1XsYRzqMs0kUgM59OhwAPwdSR+UEuyXQ8QGKwSt1d//DkdhzQDATXSBYc\n"
|
||||||
|
+ "VHr16ocYPGNd/VNo7BoUCvykp3cCH3WxYYpAugXbLU8RBJzQwCM75SLQtFe20qfI\n"
|
||||||
|
+ "LErbrmKONtMk3Rfg6XtLLcaOVh1A3q13CKqDvwtZT4oo56EJOvkBkzlCvTuxJb6s\n"
|
||||||
|
+ "FD9pwROFpIN8O54C333tZzj4TDP4g9zb3sofAJ4U0osfQAXekZJdZETFGJsU6TIM\n"
|
||||||
|
+ "Dcf5/G8bZe2DnavBQfML1wI5d7NUWE8CWb95SsIvFXI0qZE0oIR+axBVl9u97uaO\n"
|
||||||
|
+ "-----END CERTIFICATE-----\n";
|
||||||
|
|
||||||
|
private static final String DIGI_CERT =
|
||||||
|
"-----BEGIN CERTIFICATE-----\n"
|
||||||
|
+ "MIIG5jCCBc6gAwIBAgIQAze5KDR8YKauxa2xIX84YDANBgkqhkiG9w0BAQUFADBs\n"
|
||||||
|
+ "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
|
||||||
|
+ "d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j\n"
|
||||||
|
+ "ZSBFViBSb290IENBMB4XDTA3MTEwOTEyMDAwMFoXDTIxMTExMDAwMDAwMFowaTEL\n"
|
||||||
|
+ "MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3\n"
|
||||||
|
+ "LmRpZ2ljZXJ0LmNvbTEoMCYGA1UEAxMfRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug\n"
|
||||||
|
+ "RVYgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPOWYth1bhn/\n"
|
||||||
|
+ "PzR8SU8xfg0ETpmB4rOFVZEwscCvcLssqOcYqj9495BoUoYBiJfiOwZlkKq9ZXbC\n"
|
||||||
|
+ "7L4QWzd4g2B1Rca9dKq2n6Q6AVAXxDlpufFP74LByvNK28yeUE9NQKM6kOeGZrzw\n"
|
||||||
|
+ "PnYoTNF1gJ5qNRQ1A57bDIzCKK1Qss72kaPDpQpYSfZ1RGy6+c7pqzoC4E3zrOJ6\n"
|
||||||
|
+ "4GAiBTyC01Li85xH+DvYskuTVkq/cKs+6WjIHY9YHSpNXic9rQpZL1oRIEDZaARo\n"
|
||||||
|
+ "LfTAhAsKG3jf7RpY3PtBWm1r8u0c7lwytlzs16YDMqbo3rcoJ1mIgP97rYlY1R4U\n"
|
||||||
|
+ "pPKwcNSgPqcCAwEAAaOCA4UwggOBMA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAy\n"
|
||||||
|
+ "BggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUH\n"
|
||||||
|
+ "AwgwggHEBgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUH\n"
|
||||||
|
+ "AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o\n"
|
||||||
|
+ "dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0\n"
|
||||||
|
+ "AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1\n"
|
||||||
|
+ "AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp\n"
|
||||||
|
+ "AGcAaQBDAGUAcgB0ACAARQBWACAAQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl\n"
|
||||||
|
+ "AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo\n"
|
||||||
|
+ "AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg\n"
|
||||||
|
+ "AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg\n"
|
||||||
|
+ "AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wEgYDVR0TAQH/BAgwBgEB/wIBADCB\n"
|
||||||
|
+ "gwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy\n"
|
||||||
|
+ "dC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NBQ2Vy\n"
|
||||||
|
+ "dHMvRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0MIGPBgNVHR8EgYcw\n"
|
||||||
|
+ "gYQwQKA+oDyGOmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hB\n"
|
||||||
|
+ "c3N1cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0\n"
|
||||||
|
+ "LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHQYDVR0OBBYE\n"
|
||||||
|
+ "FExYyyXwQU9S9CjIgUObpqig5pLlMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoI\n"
|
||||||
|
+ "Au9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQBMeheHKF0XvLIyc7/NLvVYMR3wsXFU\n"
|
||||||
|
+ "nNabZ5PbLwM+Fm8eA8lThKNWYB54lBuiqG+jpItSkdfdXJW777UWSemlQk808kf/\n"
|
||||||
|
+ "roF/E1S3IMRwFcuBCoHLdFfcnN8kpCkMGPAc5K4HM+zxST5Vz25PDVR708noFUjU\n"
|
||||||
|
+ "xbvcNRx3RQdIRYW9135TuMAW2ZXNi419yWBP0aKb49Aw1rRzNubS+QOy46T15bg+\n"
|
||||||
|
+ "BEkAui6mSnKDcp33C4ypieez12Qf1uNgywPE3IjpnSUBAHHLA7QpYCWP+UbRe3Gu\n"
|
||||||
|
+ "zVMSW4SOwg/H7ZMZ2cn6j1g0djIvruFQFGHUqFijyDATI+/GJYw2jxyA\n"
|
||||||
|
+ "-----END CERTIFICATE-----\n";
|
||||||
|
|
||||||
|
private static final String GITHUB_CERT =
|
||||||
|
"-----BEGIN CERTIFICATE-----\n"
|
||||||
|
+ "MIIHOjCCBiKgAwIBAgIQBH++LkveAITSyvjj7P5wWDANBgkqhkiG9w0BAQUFADBp\n"
|
||||||
|
+ "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
|
||||||
|
+ "d3cuZGlnaWNlcnQuY29tMSgwJgYDVQQDEx9EaWdpQ2VydCBIaWdoIEFzc3VyYW5j\n"
|
||||||
|
+ "ZSBFViBDQS0xMB4XDTEzMDYxMDAwMDAwMFoXDTE1MDkwMjEyMDAwMFowgfAxHTAb\n"
|
||||||
|
+ "BgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRMwEQYLKwYBBAGCNzwCAQMTAlVT\n"
|
||||||
|
+ "MRkwFwYLKwYBBAGCNzwCAQITCERlbGF3YXJlMRAwDgYDVQQFEwc1MTU3NTUwMRcw\n"
|
||||||
|
+ "FQYDVQQJEw41NDggNHRoIFN0cmVldDEOMAwGA1UEERMFOTQxMDcxCzAJBgNVBAYT\n"
|
||||||
|
+ "AlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2Nv\n"
|
||||||
|
+ "MRUwEwYDVQQKEwxHaXRIdWIsIEluYy4xEzARBgNVBAMTCmdpdGh1Yi5jb20wggEi\n"
|
||||||
|
+ "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDt04nDXXByCfMzTxpydNm2WpVQ\n"
|
||||||
|
+ "u2hhn/f7Hxnh2gQxrxV8Gn/5c68d5UMrVgkARWlK6MRb38J3UlEZW9Er2TllNqAy\n"
|
||||||
|
+ "GRxBc/sysj2fmOyCWws3ZDkstxCDcs3w6iRL+tmULsOFFTmpOvaI2vQniaaVT4Si\n"
|
||||||
|
+ "N058JXg6yYNtAheVeH1HqFWD7hPIGRqzPPFf/jsC4YX7EWarCV2fTEPwxyReKXIo\n"
|
||||||
|
+ "ztR1aE8kcimuOSj8341PTYNzdAxvEZun3WLe/+LrF+b/DL/ALTE71lmi8t2HSkh7\n"
|
||||||
|
+ "bTMRFE00nzI49sgZnfG2PcVG71ELisYz7UhhxB0XG718tmfpOc+lUoAK9OrNAgMB\n"
|
||||||
|
+ "AAGjggNUMIIDUDAfBgNVHSMEGDAWgBRMWMsl8EFPUvQoyIFDm6aooOaS5TAdBgNV\n"
|
||||||
|
+ "HQ4EFgQUh9GPGW7kh29TjHeRB1Dfo79VRyAwJQYDVR0RBB4wHIIKZ2l0aHViLmNv\n"
|
||||||
|
+ "bYIOd3d3LmdpdGh1Yi5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsG\n"
|
||||||
|
+ "AQUFBwMBBggrBgEFBQcDAjBjBgNVHR8EXDBaMCugKaAnhiVodHRwOi8vY3JsMy5k\n"
|
||||||
|
+ "aWdpY2VydC5jb20vZXZjYTEtZzIuY3JsMCugKaAnhiVodHRwOi8vY3JsNC5kaWdp\n"
|
||||||
|
+ "Y2VydC5jb20vZXZjYTEtZzIuY3JsMIIBxAYDVR0gBIIBuzCCAbcwggGzBglghkgB\n"
|
||||||
|
+ "hv1sAgEwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9z\n"
|
||||||
|
+ "c2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4A\n"
|
||||||
|
+ "eQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQA\n"
|
||||||
|
+ "ZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUA\n"
|
||||||
|
+ "IABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAA\n"
|
||||||
|
+ "YQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcA\n"
|
||||||
|
+ "cgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIA\n"
|
||||||
|
+ "aQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQA\n"
|
||||||
|
+ "ZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMH0G\n"
|
||||||
|
+ "CCsGAQUFBwEBBHEwbzAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu\n"
|
||||||
|
+ "Y29tMEcGCCsGAQUFBzAChjtodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln\n"
|
||||||
|
+ "aUNlcnRIaWdoQXNzdXJhbmNlRVZDQS0xLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqG\n"
|
||||||
|
+ "SIb3DQEBBQUAA4IBAQBfFW1nwzrVo94WnEUzJtU9yRZ0NMqHSBsUkG31q0eGufW4\n"
|
||||||
|
+ "4wFFZWjuqRJ1n3Ym7xF8fTjP3fdKGQnxIHKSsE0nuuh/XbQX5DpBJknHdGFoLwY8\n"
|
||||||
|
+ "xZ9JPI57vgvzLo8+fwHyZp3Vm/o5IYLEQViSo+nlOSUQ8YAVqu6KcsP/e612UiqS\n"
|
||||||
|
+ "+UMBmgdx9KPDDzZy4MJZC2hbfUoXj9A54mJN8cuEOPyw3c3yKOcq/h48KzVguQXi\n"
|
||||||
|
+ "SdJbwfqNIbQ9oJM+YzDjzS62+TCtNSNWzWbwABZCmuQxK0oEOSbTmbhxUF7rND3/\n"
|
||||||
|
+ "+mx9u8cY//7uAxLWYS5gIZlCbxcf0lkiKSHJB319\n"
|
||||||
|
+ "-----END CERTIFICATE-----\n";
|
||||||
|
|
||||||
|
private File mKeyStoreFile;
|
||||||
|
private LocalKeyStore mKeyStore;
|
||||||
|
private X509Certificate mCert1;
|
||||||
|
private X509Certificate mCert2;
|
||||||
|
private X509Certificate mCaCert;
|
||||||
|
private X509Certificate mCert3;
|
||||||
|
private X509Certificate mDigiCert;
|
||||||
|
private X509Certificate mGithubCert;
|
||||||
|
|
||||||
|
|
||||||
|
public TrustManagerFactoryTest() throws CertificateException {
|
||||||
|
mCert1 = loadCert(K9_EXAMPLE_COM_CERT1);
|
||||||
|
mCert2 = loadCert(K9_EXAMPLE_COM_CERT2);
|
||||||
|
mCaCert = loadCert(CA_CERT);
|
||||||
|
mCert3 = loadCert(CERT3);
|
||||||
|
mDigiCert = loadCert(DIGI_CERT);
|
||||||
|
mGithubCert = loadCert(GITHUB_CERT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private X509Certificate loadCert(String encodedCert) throws CertificateException {
|
||||||
|
CertificateFactory certFactory = CertificateFactory.getInstance("X509");
|
||||||
|
return (X509Certificate) certFactory.generateCertificate(
|
||||||
|
new ByteArrayInputStream(encodedCert.getBytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
mKeyStoreFile = File.createTempFile("localKeyStore", null, getContext().getCacheDir());
|
||||||
|
mKeyStore = LocalKeyStore.getInstance();
|
||||||
|
mKeyStore.setKeyStoreFile(mKeyStoreFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() {
|
||||||
|
mKeyStoreFile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT1, mCert1);
|
||||||
|
mKeyStore.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 {
|
||||||
|
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1);
|
||||||
|
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1, true);
|
||||||
|
trustManager.checkServerTrusted(new X509Certificate[] { mCert1 }, "authType");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSelfSignedCertificateNotMatchingHost() throws Exception {
|
||||||
|
mKeyStore.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 {
|
||||||
|
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1);
|
||||||
|
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1, true);
|
||||||
|
assertCertificateRejection(trustManager, new X509Certificate[] { mCert2 });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCertificateOfOtherHost() throws Exception {
|
||||||
|
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1);
|
||||||
|
mKeyStore.addCertificate(MATCHING_HOST, PORT2, mCert2);
|
||||||
|
|
||||||
|
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1, true);
|
||||||
|
assertCertificateRejection(trustManager, new X509Certificate[] { mCert2 });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testUntrustedCertificateChain() throws Exception {
|
||||||
|
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1, true);
|
||||||
|
assertCertificateRejection(trustManager, new X509Certificate[] { mCert3, mCaCert });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLocallyTrustedCertificateChain() throws Exception {
|
||||||
|
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert3);
|
||||||
|
|
||||||
|
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1, true);
|
||||||
|
trustManager.checkServerTrusted(new X509Certificate[] { mCert3, mCaCert }, "authType");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLocallyTrustedCertificateChainNotMatchingHost() throws Exception {
|
||||||
|
mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT1, mCert3);
|
||||||
|
|
||||||
|
X509TrustManager trustManager = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1, true);
|
||||||
|
trustManager.checkServerTrusted(new X509Certificate[] { mCert3, mCaCert }, "authType");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGloballyTrustedCertificateChain() throws Exception {
|
||||||
|
X509TrustManager trustManager = TrustManagerFactory.get("github.com", PORT1, true);
|
||||||
|
X509Certificate[] certificates = new X509Certificate[] { mGithubCert, mDigiCert };
|
||||||
|
trustManager.checkServerTrusted(certificates, "authType");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGloballyTrustedCertificateNotMatchingHost() throws Exception {
|
||||||
|
X509TrustManager trustManager = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1, true);
|
||||||
|
assertCertificateRejection(trustManager, new X509Certificate[] { mGithubCert, mDigiCert});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGloballyTrustedCertificateNotMatchingHostOverride() throws Exception {
|
||||||
|
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mGithubCert);
|
||||||
|
|
||||||
|
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1, true);
|
||||||
|
X509Certificate[] certificates = new X509Certificate[] { mGithubCert, mDigiCert };
|
||||||
|
trustManager.checkServerTrusted(certificates, "authType");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertCertificateRejection(X509TrustManager trustManager,
|
||||||
|
X509Certificate[] certificates) {
|
||||||
|
boolean certificateValid;
|
||||||
|
try {
|
||||||
|
trustManager.checkServerTrusted(certificates, "authType");
|
||||||
|
certificateValid = true;
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
certificateValid = false;
|
||||||
|
}
|
||||||
|
assertFalse("The certificate should have been rejected but wasn't", certificateValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testKeyStoreLoading() throws Exception {
|
||||||
|
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mCert1);
|
||||||
|
mKeyStore.addCertificate(NOT_MATCHING_HOST, PORT2, mCert2);
|
||||||
|
assertTrue(mKeyStore.isValidCertificate(mCert1, MATCHING_HOST, PORT1));
|
||||||
|
assertTrue(mKeyStore.isValidCertificate(mCert2, NOT_MATCHING_HOST, PORT2));
|
||||||
|
|
||||||
|
// reload store from same file
|
||||||
|
mKeyStore.setKeyStoreFile(mKeyStoreFile);
|
||||||
|
assertTrue(mKeyStore.isValidCertificate(mCert1, MATCHING_HOST, PORT1));
|
||||||
|
assertTrue(mKeyStore.isValidCertificate(mCert2, NOT_MATCHING_HOST, PORT2));
|
||||||
|
|
||||||
|
// reload store from empty file
|
||||||
|
mKeyStoreFile.delete();
|
||||||
|
mKeyStore.setKeyStoreFile(mKeyStoreFile);
|
||||||
|
assertFalse(mKeyStore.isValidCertificate(mCert1, MATCHING_HOST, PORT1));
|
||||||
|
assertFalse(mKeyStore.isValidCertificate(mCert2, NOT_MATCHING_HOST, PORT2));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user