Merge into 'trunk'

r124@hotel-dan (orig r123):  jessev | 2008-11-07 03:35:09 -0500
Branch for Bradley Young
r126@hotel-dan (orig r125):  young.bradley | 2008-11-08 17:27:30 -0500
Initial checkin of self signed certificates capability.

Missing ability to save updated KeyStore.
r127@hotel-dan (orig r126):  young.bradley | 2008-11-10 13:04:49 -0500
Update to allow saving updated keys to keystore
r17200@hotel-dan (orig r131):  young.bradley | 2008-11-17 14:09:24 -0500
Updates to handle chains properly, and handle default behavior.
r17206@hotel-dan (orig r137):  young.bradley | 2008-11-29 14:14:25 -0500
Checkin for beta 2: this should be the release candidate.
This commit is contained in:
Jesse Vincent 2008-12-03 00:04:24 +00:00
parent 9dbcae5eed
commit 194d673f91
9 changed files with 274 additions and 43 deletions

View File

@ -192,4 +192,8 @@
<string name="status_network_error">Verbindungsfehler</string>
<string name="status_sending">Senden\u2026</string>
<string name="view_hide_details_action">Details anzeigen/ausblenden</string>
<string name="account_setup_failed_dlg_invalid_certificate_title">Unrecognized Certificate</string>
<string name="account_setup_failed_dlg_invalid_certificate_accept">Accept Key</string>
<string name="account_setup_failed_dlg_invalid_certificate_reject">Reject Key</string>
</resources>

View File

@ -191,5 +191,9 @@
<string name="status_loading_more_failed">Reintentar la carga de más mensajes</string>
<string name="status_network_error">Error de conexión</string>
<string name="status_sending">Enviando\u2026</string>
<string name="view_hide_details_action">Ver/ocultar detalles</string>
<string name="view_hide_details_action">Ver/ocultar detalles</string>
<string name="account_setup_failed_dlg_invalid_certificate_title">Unrecognized Certificate</string>
<string name="account_setup_failed_dlg_invalid_certificate_accept">Accept Key</string>
<string name="account_setup_failed_dlg_invalid_certificate_reject">Reject Key</string>
</resources>

View File

@ -191,5 +191,8 @@
<string name="status_loading_more_failed">Ressayer le chargement de plus de messages</string>
<string name="status_network_error">Erreur de connexion</string>
<string name="status_sending">Envoi\u2026</string>
<string name="view_hide_details_action">Afficher/masquer les détails</string>
<string name="view_hide_details_action">Afficher/masquer les détails</string>
<string name="account_setup_failed_dlg_invalid_certificate_title">Unrecognized Certificate</string>
<string name="account_setup_failed_dlg_invalid_certificate_accept">Accept Key</string>
<string name="account_setup_failed_dlg_invalid_certificate_reject">Reject Key</string>
</resources>

View File

@ -192,4 +192,7 @@
<string name="status_network_error">Errore di connessione</string>
<string name="status_sending">Invio in corso\u2026</string>
<string name="view_hide_details_action">Visualizza/Nascondi dettagli</string>
<string name="account_setup_failed_dlg_invalid_certificate_title">Unrecognized Certificate</string>
<string name="account_setup_failed_dlg_invalid_certificate_accept">Accept Key</string>
<string name="account_setup_failed_dlg_invalid_certificate_reject">Reject Key</string>
</resources>

View File

@ -192,4 +192,7 @@
<string name="status_network_error">連線錯誤</string>
<string name="status_sending">正在傳送\u2026</string>
<string name="view_hide_details_action">檢視/隱藏詳細資料</string>
<string name="account_setup_failed_dlg_invalid_certificate_title">Unrecognized Certificate</string>
<string name="account_setup_failed_dlg_invalid_certificate_accept">Accept Key</string>
<string name="account_setup_failed_dlg_invalid_certificate_reject">Reject Key</string>
</resources>

View File

@ -248,9 +248,12 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based
<string name="account_settings_always_bcc_summary">Send this address a copy of every outgoing message</string>
<string name="account_settings_signature_label">Signature</string>
<string name="account_settings_signature_summary">Append a signature to every message you send</string>
<string name="account_settings_signature_label">Signature</string>
<string name="account_settings_signature_summary">Append a signature to every message you send</string>
<string name="account_settings_sent_items_label">Sent Items Folder</string>
<string name="account_settings_sent_items_summary">Save all sent messages to this folder</string>
<string name="account_delete_dlg_title">Remove</string>
<string name="account_delete_dlg_instructions_fmt">The account \"<xliff:g id="account">%s</xliff:g>\" will be removed from Email.</string>
@ -260,4 +263,7 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based
your correct email address and password, you may not have a paid
\"Plus\" account. Please launch the Web browser to gain access to
these mail accounts.</string>
<string name="account_setup_failed_dlg_invalid_certificate_title">Unrecognized Certificate</string>
<string name="account_setup_failed_dlg_invalid_certificate_accept">Accept Key</string>
<string name="account_setup_failed_dlg_invalid_certificate_reject">Reject Key</string>
</resources>

View File

@ -1,6 +1,9 @@
package com.fsck.k9.activity.setup;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
@ -21,6 +24,7 @@ import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.Transport;
import com.fsck.k9.mail.CertificateValidationException;
import com.fsck.k9.mail.store.TrustManagerFactory;
/**
* Checks the given settings to make sure that they can be used to send and
@ -78,6 +82,7 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
new Thread() {
public void run() {
Store store = null;
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
try {
if (mDestroyed) {
@ -88,9 +93,9 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
return;
}
if (mCheckIncoming) {
setMessage(R.string.account_setup_check_settings_check_incoming_msg);
Store store = Store.getInstance(mAccount.getStoreUri(), getApplication());
store.checkSettings();
setMessage(R.string.account_setup_check_settings_check_incoming_msg);
store = Store.getInstance(mAccount.getStoreUri(), getApplication());
store.checkSettings();
}
if (mDestroyed) {
return;
@ -118,17 +123,19 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
} catch (final AuthenticationFailedException afe) {
showErrorDialog(
R.string.account_setup_failed_dlg_auth_message_fmt,
afe.getMessage() == null ? "" : afe.getMessage());
afe.getMessage() == null ? "" : afe.getMessage());
} catch (final CertificateValidationException cve) {
showErrorDialog(
acceptKeyDialog(
R.string.account_setup_failed_dlg_certificate_message_fmt,
cve.getMessage() == null ? "" : cve.getMessage());
cve);
//cve.getMessage() == null ? "" : cve.getMessage());
} catch (final MessagingException me) {
showErrorDialog(
R.string.account_setup_failed_dlg_server_message_fmt,
me.getMessage() == null ? "" : me.getMessage());
}
}
}.start();
}
@ -172,7 +179,86 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
}
});
}
private void acceptKeyDialog(final int msgResId, final Object... args) {
mHandler.post(new Runnable() {
public void run() {
if (mDestroyed) {
return;
}
final X509Certificate[] chain = TrustManagerFactory.getLastCertChain();
String exMessage = "Unknown Error";
Exception ex = ((Exception)args[0]);
if (ex != null) {
if (ex.getCause() != null) {
if (ex.getCause().getCause() != null) {
exMessage = ex.getCause().getCause().getMessage();
} else {
exMessage = ex.getCause().getMessage();
}
} else {
exMessage = ex.getMessage();
}
}
mProgressBar.setIndeterminate(false);
StringBuffer chainInfo = new StringBuffer(100);
for (int i = 0; i < chain.length; i++)
{
// display certificate chain information
chainInfo.append("Certificate chain[" + i + "]:\n");
chainInfo.append("Subject: " + chain[i].getSubjectDN().toString() + "\n");
chainInfo.append("Issuer: " + chain[i].getIssuerDN().toString() + "\n");
}
new AlertDialog.Builder(AccountSetupCheckSettings.this)
.setTitle(getString(R.string.account_setup_failed_dlg_invalid_certificate_title))
//.setMessage(getString(R.string.account_setup_failed_dlg_invalid_certificate)
.setMessage(getString(msgResId,exMessage)
+ " " + chainInfo.toString()
)
.setCancelable(true)
.setPositiveButton(
getString(R.string.account_setup_failed_dlg_invalid_certificate_accept),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
try {
String alias = mAccount.getUuid();
if (mCheckIncoming) {
alias = alias + ".incoming";
}
if (mCheckOutgoing) {
alias = alias + ".outgoing";
}
TrustManagerFactory.addCertificateChain(alias, chain);
} catch (CertificateException e) {
showErrorDialog(
R.string.account_setup_failed_dlg_certificate_message_fmt,
e.getMessage() == null ? "" : e.getMessage());
}
AccountSetupCheckSettings.actionCheckSettings(AccountSetupCheckSettings.this, mAccount,
mCheckIncoming, mCheckOutgoing);
}
})
.setNegativeButton(
getString(R.string.account_setup_failed_dlg_invalid_certificate_reject),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.show();
}
});
}
public void onActivityResult(int reqCode, int resCode, Intent data) {
setResult(resCode);
finish();
}
private void onCancel() {
mCanceled = true;
setMessage(R.string.account_setup_check_settings_canceling_msg);

View File

@ -17,6 +17,8 @@ import com.fsck.k9.service.BootReceiver;
import com.fsck.k9.service.MailService;
public class k9 extends Application {
public static Application app = null;
public static final String LOG_TAG = "k9";
public static File tempDirectory;
@ -64,13 +66,14 @@ public class k9 extends Application {
* The MIME type(s) of attachments we're willing to download to SD.
*/
public static final String[] ACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES = new String[] {
"*/*",
"image/*",
};
/**
* The MIME type(s) of attachments we're not willing to download to SD.
*/
public static final String[] UNACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES = new String[] {
"image/gif",
};
/**
@ -87,7 +90,7 @@ public class k9 extends Application {
public static final int DEFAULT_VISIBLE_LIMIT = 25;
/**
* Number of additional messages to load when a user selectes "Load more messages..."
* Number of additioanl messages to load when a user selectes "Load more messages..."
*/
public static final int VISIBLE_LIMIT_INCREMENT = 25;
@ -146,6 +149,7 @@ public class k9 extends Application {
@Override
public void onCreate() {
super.onCreate();
app = this;
Preferences prefs = Preferences.getPreferences(this);
DEBUG = prefs.geteEnableDebugLogging();
DEBUG_SENSITIVE = prefs.getEnableSensitiveLogging();
@ -156,13 +160,6 @@ public class k9 extends Application {
* doesn't work in Android and MimeMessage does not have access to a Context.
*/
BinaryTempFileBody.setTempDirectory(getCacheDir());
/*
* Enable background sync of messages
*/
setServicesEnabled(this);
}
}

View File

@ -2,8 +2,13 @@
package com.fsck.k9.mail.store;
import android.util.Log;
import android.app.Application;
import android.content.Context;
import android.net.http.DomainNameChecker;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.KeyStoreException;
@ -13,11 +18,22 @@ import java.security.cert.CertificateException;
import javax.net.ssl.X509TrustManager;
import javax.net.ssl.TrustManager;
import com.fsck.k9.k9;
public final class TrustManagerFactory {
private static final String LOG_TAG = "TrustManagerFactory";
private static X509TrustManager sSecureTrustManager;
private static X509TrustManager sUnsecureTrustManager;
private static X509TrustManager defaultTrustManager;
private static X509TrustManager unsecureTrustManager;
private static X509TrustManager localTrustManager;
private static SecureX509TrustManager secureTrustManager;
private static X509Certificate[] lastCertChain = null;
private static File keyStoreFile;
private static KeyStore keyStore;
private static class SimpleX509TrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] chain, String authType)
@ -34,62 +50,171 @@ public final class TrustManagerFactory {
}
private static class SecureX509TrustManager implements X509TrustManager {
private X509TrustManager mTrustManager;
private String mHost;
//private static X509TrustManager mTrustManager;
//private static X509TrustManager mLocalTrustManager;
private static String mHost;
private static SecureX509TrustManager me;
SecureX509TrustManager(X509TrustManager trustManager, String host) {
mTrustManager = trustManager;
mHost = host;
private SecureX509TrustManager() {
}
public static X509TrustManager getInstance(String host) {
mHost = host;
if (me == null) {
me = new SecureX509TrustManager();
}
return me;
}
public static void setHost(String host){
mHost = host;
}
//
// public static void updateTrustManager(X509TrustManager trustManager) {
// mTrustManager = trustManager;
// }
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
mTrustManager.checkClientTrusted(chain, authType);
defaultTrustManager.checkClientTrusted(chain, authType);
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
mTrustManager.checkServerTrusted(chain, authType);
if (!DomainNameChecker.match(chain[0], mHost)) {
throw new CertificateException("Certificate domain name does not match "
+ mHost);
}
throws CertificateException {
TrustManagerFactory.setLastCertChain(chain);
try {
defaultTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException e) {
localTrustManager.checkServerTrusted(chain, authType);
}
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 CertificateException("Certificate domain name does not match "
+ mHost);
}
}
public X509Certificate[] getAcceptedIssuers() {
return mTrustManager.getAcceptedIssuers();
return defaultTrustManager.getAcceptedIssuers();
}
}
static {
try {
javax.net.ssl.TrustManagerFactory tmf = javax.net.ssl.TrustManagerFactory.getInstance("X509");
tmf.init((KeyStore) null);
Application app = k9.app;
keyStoreFile = new File(app.getDir("KeyStore", Context.MODE_PRIVATE) + File.separator + "KeyStore.bks");
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
//TODO: read store from disk.
java.io.FileInputStream fis;
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) {
sSecureTrustManager = (X509TrustManager) tm;
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);
}
sUnsecureTrustManager = new SimpleX509TrustManager();
unsecureTrustManager = new SimpleX509TrustManager();
}
private TrustManagerFactory() {
}
public static X509TrustManager get(String host, boolean secure) {
return secure ? new SecureX509TrustManager(sSecureTrustManager, host) :
sUnsecureTrustManager;
return secure ? SecureX509TrustManager.getInstance(host) :
unsecureTrustManager;
}
public static KeyStore getKeyStore() {
return keyStore;
}
public static void setLastCertChain(X509Certificate[] chain) {
lastCertChain = chain;
}
public static X509Certificate[] getLastCertChain() {
return lastCertChain;
}
public static void addCertificateChain(String alias, X509Certificate[] chain) throws CertificateException {
try {
javax.net.ssl.TrustManagerFactory tmf = javax.net.ssl.TrustManagerFactory.getInstance("X509");
for (int i = 0; i < chain.length; i++)
{
keyStore.setCertificateEntry
(chain[i].getSubjectDN().toString(), chain[i]);
}
tmf.init(keyStore);
TrustManager[] tms = tmf.getTrustManagers();
if (tms != null) {
for (TrustManager tm : tms) {
if (tm instanceof X509TrustManager) {
localTrustManager = (X509TrustManager) tm;
break;
}
}
}
java.io.FileOutputStream keyStoreStream;
try {
keyStoreStream = new java.io.FileOutputStream(keyStoreFile);
keyStore.store(keyStoreStream, "".toCharArray());
keyStoreStream.close();
} 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) {
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);
}
}
}