Add one more error state to certificate process

When the KeyStore fails to give us back a certificate for any reason (it
was removed from the keystore perhaps), propagate the error back up.

Change-Id: I4f0ef783c1665589cc8ccb43d95da43a297a3e9a
This commit is contained in:
Ben Komalo 2011-06-16 13:37:10 -07:00
parent 8401dba404
commit cb24e515b7
8 changed files with 37 additions and 18 deletions

View File

@ -63,7 +63,9 @@ public class MessagingException extends Exception {
/** The server refused access */
public static final int ATTACHMENT_NOT_FOUND = 15;
/** A client SSL certificate is required for connections to the server */
public static final int CLIENT_CERTIFICATE_ERROR = 16;
public static final int CLIENT_CERTIFICATE_REQUIRED = 16;
/** The client SSL certificate specified is invalid */
public static final int CLIENT_CERTIFICATE_ERROR = 17;
protected int mExceptionType;
// Exception type-specific data

View File

@ -37,4 +37,7 @@ public interface EmailServiceStatus {
// Maybe we should automatically retry these?
public static final int CONNECTION_ERROR = 0x20;
// Client certificates used to authenticate cannot be retrieved from the system.
public static final int CLIENT_CERTIFICATE_ERROR = 0x21;
}

View File

@ -21,8 +21,6 @@ import com.android.emailcommon.Logging;
import com.android.emailcommon.utility.SSLUtils.KeyChainKeyManager;
import com.android.emailcommon.utility.SSLUtils.TrackingKeyManager;
import org.apache.http.conn.ClientConnectionRequest;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
@ -33,6 +31,8 @@ import android.content.Context;
import android.net.SSLCertificateSocketFactory;
import android.util.Log;
import java.security.cert.CertificateException;
import javax.net.ssl.KeyManager;
/**
@ -75,7 +75,8 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager {
* {@link #makeSchemeForClientCert(String, boolean)}.
*/
public synchronized void registerClientCert(
Context context, String clientCertAlias, boolean trustAllServerCerts) {
Context context, String clientCertAlias, boolean trustAllServerCerts)
throws CertificateException {
SchemeRegistry registry = getSchemeRegistry();
String schemeName = makeSchemeForClientCert(clientCertAlias, trustAllServerCerts);
Scheme existing = registry.get(schemeName);
@ -85,12 +86,6 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager {
+ clientCertAlias + "]");
}
KeyManager keyManager = KeyChainKeyManager.fromAlias(context, clientCertAlias);
if (keyManager == null) {
// TODO: handle failing to retrieve credentials from the keystore.
Log.e(Logging.LOG_TAG, "Unable to retrieve credentials for alias ["
+ clientCertAlias + "]");
return;
}
SSLCertificateSocketFactory underlying = SSLUtils.getSSLSocketFactory(
trustAllServerCerts);
underlying.setKeyManagers(new KeyManager[] { keyManager });

View File

@ -25,6 +25,7 @@ import android.util.Log;
import java.net.Socket;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
@ -178,16 +179,17 @@ public class SSLUtils {
* If for any reason retrieval of the credentials from the system {@link KeyChain} fails,
* a {@code null} value will be returned.
*/
public static KeyChainKeyManager fromAlias(Context context, String alias) {
public static KeyChainKeyManager fromAlias(Context context, String alias)
throws CertificateException {
X509Certificate[] certificateChain;
try {
certificateChain = KeyChain.getCertificateChain(context, alias);
} catch (KeyChainException e) {
logError(alias, "certificate chain", e);
return null;
throw new CertificateException(e);
} catch (InterruptedException e) {
logError(alias, "certificate chain", e);
return null;
throw new CertificateException(e);
}
PrivateKey privateKey;
@ -195,10 +197,15 @@ public class SSLUtils {
privateKey = KeyChain.getPrivateKey(context, alias);
} catch (KeyChainException e) {
logError(alias, "private key", e);
return null;
throw new CertificateException(e);
} catch (InterruptedException e) {
logError(alias, "private key", e);
return null;
throw new CertificateException(e);
}
if (certificateChain == null || privateKey == null) {
throw new CertificateException(
"Can't access certificate from keystore for alias [" + alias + "]");
}
return new KeyChainKeyManager(alias, certificateChain, privateKey);

View File

@ -770,8 +770,12 @@ save attachment.</string>
>Cannot safely connect to server.\n(<xliff:g id="error">%s</xliff:g>)</string>
<!-- An error message presented to the user when the server requires a
client certificate to connect [CHAR LIMIT=NONE] -->
<string name="account_setup_failed_certificate_required">User certificate
required to connect to server.</string>
<string name="account_setup_failed_certificate_required"
>User certificate required to connect to server.</string>
<!-- An error message presented to the user when the certificate they
specified for connecting to a server is inaccessible [CHAR LIMIT=NONE] -->
<string name="account_setup_failed_certificate_inaccessible"
>Certificate invalid or inaccessible.</string>
<!-- Dialog text for ambiguous setup failure; server error/bad credentials [CHAR LIMIT=none] -->
<string name="account_setup_failed_check_credentials_message">

View File

@ -1609,6 +1609,9 @@ public class Controller {
case EmailServiceStatus.ATTACHMENT_NOT_FOUND:
return new MessagingException(MessagingException.ATTACHMENT_NOT_FOUND);
case EmailServiceStatus.CLIENT_CERTIFICATE_ERROR:
return new MessagingException(MessagingException.CLIENT_CERTIFICATE_ERROR);
case EmailServiceStatus.MESSAGE_NOT_FOUND:
case EmailServiceStatus.FOLDER_NOT_DELETED:
case EmailServiceStatus.FOLDER_NOT_RENAMED:

View File

@ -49,6 +49,8 @@ public class MessagingExceptionStrings {
return R.string.account_setup_failed_security;
case MessagingException.ACCESS_DENIED:
return R.string.account_setup_failed_access_denied;
case MessagingException.CLIENT_CERTIFICATE_ERROR:
return R.string.account_setup_failed_certificate_inaccessible;
}
return R.string.status_network_error; // default
}

View File

@ -645,9 +645,12 @@ public class AccountCheckSettingsFragment extends Fragment {
case MessagingException.GENERAL_SECURITY:
id = R.string.account_setup_failed_security;
break;
case MessagingException.CLIENT_CERTIFICATE_ERROR:
case MessagingException.CLIENT_CERTIFICATE_REQUIRED:
id = R.string.account_setup_failed_certificate_required;
break;
case MessagingException.CLIENT_CERTIFICATE_ERROR:
id = R.string.account_setup_failed_certificate_inaccessible;
break;
default:
id = TextUtils.isEmpty(message)
? R.string.account_setup_failed_dlg_server_message