Indicate to the user when a cert error happens.
This introduces an exception which needs to be thrown from a KeyManager when it tries to establish a connection with a server requesting a certificate. Change-Id: I06dfad7789ed5d320b630e7e4380e15da42a48df
This commit is contained in:
parent
f8d8dfac7a
commit
f4dbbf1099
|
@ -50,7 +50,7 @@ public class MessagingException extends Exception {
|
|||
public static final int SECURITY_POLICIES_UNSUPPORTED = 8;
|
||||
/** The protocol (or protocol version) isn't supported */
|
||||
public static final int PROTOCOL_VERSION_UNSUPPORTED = 9;
|
||||
/** An SSL certificate couldn't be validated */
|
||||
/** The server's SSL certificate couldn't be validated */
|
||||
public static final int CERTIFICATE_VALIDATION_ERROR = 10;
|
||||
/** Authentication failed during autodiscover */
|
||||
public static final int AUTODISCOVER_AUTHENTICATION_FAILED = 11;
|
||||
|
@ -62,6 +62,8 @@ public class MessagingException extends Exception {
|
|||
public static final int ACCESS_DENIED = 14;
|
||||
/** 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;
|
||||
|
||||
protected int mExceptionType;
|
||||
// Exception type-specific data
|
||||
|
|
|
@ -19,7 +19,10 @@ package com.android.emailcommon.utility;
|
|||
|
||||
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;
|
||||
|
@ -48,15 +51,18 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager {
|
|||
}
|
||||
|
||||
public static EmailClientConnectionManager newInstance(HttpParams params) {
|
||||
TrackingKeyManager keyManager = new TrackingKeyManager();
|
||||
|
||||
// Create a registry for our three schemes; http and https will use built-in factories
|
||||
SchemeRegistry registry = new SchemeRegistry();
|
||||
registry.register(new Scheme("http",
|
||||
PlainSocketFactory.getSocketFactory(), 80));
|
||||
registry.register(new Scheme("https", SSLUtils.getHttpSocketFactory(false), 443));
|
||||
registry.register(new Scheme("https",
|
||||
SSLUtils.getHttpSocketFactory(false, keyManager), 443));
|
||||
|
||||
// Register the httpts scheme with our insecure factory
|
||||
registry.register(new Scheme("httpts",
|
||||
SSLUtils.getHttpSocketFactory(true /*insecure*/), 443));
|
||||
SSLUtils.getHttpSocketFactory(true /*insecure*/, keyManager), 443));
|
||||
|
||||
return new EmailClientConnectionManager(params, registry);
|
||||
}
|
||||
|
|
|
@ -65,10 +65,11 @@ public class SSLUtils {
|
|||
* Returns a {@link org.apache.http.conn.ssl.SSLSocketFactory SSLSocketFactory} for use with the
|
||||
* Apache HTTP stack.
|
||||
*/
|
||||
public static SSLSocketFactory getHttpSocketFactory(boolean insecure) {
|
||||
public static SSLSocketFactory getHttpSocketFactory(boolean insecure, KeyManager keyManager) {
|
||||
SSLCertificateSocketFactory underlying = getSSLSocketFactory(insecure);
|
||||
// TODO: register a keymanager that will simply listen for requests for a client
|
||||
// certificate so that higher levels know to ask the user for such credentials.
|
||||
if (keyManager != null) {
|
||||
underlying.setKeyManagers(new KeyManager[] { keyManager });
|
||||
}
|
||||
return new SSLSocketFactory(underlying);
|
||||
}
|
||||
|
||||
|
@ -132,6 +133,38 @@ public class SSLUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A dummy {@link KeyManager} which throws a {@link CertificateRequestedException} if the
|
||||
* server requests a certificate.
|
||||
*/
|
||||
public static class TrackingKeyManager extends StubKeyManager {
|
||||
@Override
|
||||
public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
|
||||
if (LOG_ENABLED) {
|
||||
Log.i(TAG, "TrackingKeyManager: requesting a client cert alias for "
|
||||
+ Arrays.toString(keyTypes));
|
||||
}
|
||||
throw new CertificateRequestedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getCertificateChain(String alias) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateKey getPrivateKey(String alias) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An exception indicating that a server requested a client certificate but none was
|
||||
* available to be presented.
|
||||
*/
|
||||
public static class CertificateRequestedException extends RuntimeException {
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link KeyManager} that reads uses credentials stored in the system {@link KeyChain}.
|
||||
*/
|
||||
|
@ -150,12 +183,10 @@ public class SSLUtils {
|
|||
try {
|
||||
certificateChain = KeyChain.getCertificateChain(context, alias);
|
||||
} catch (KeyChainException e) {
|
||||
Log.e(TAG, "Unable to retrieve certificate chain for [" + alias + "] due to "
|
||||
+ e);
|
||||
logError(alias, "certificate chain", e);
|
||||
return null;
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(TAG, "Unable to retrieve certificate chain for [" + alias + "] due to "
|
||||
+ e);
|
||||
logError(alias, "certificate chain", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -163,16 +194,20 @@ public class SSLUtils {
|
|||
try {
|
||||
privateKey = KeyChain.getPrivateKey(context, alias);
|
||||
} catch (KeyChainException e) {
|
||||
Log.e(TAG, "Unable to retrieve private key for [" + alias + "] due to " + e);
|
||||
logError(alias, "private key", e);
|
||||
return null;
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(TAG, "Unable to retrieve private key for [" + alias + "] due to " + e);
|
||||
logError(alias, "private key", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new KeyChainKeyManager(alias, certificateChain, privateKey);
|
||||
}
|
||||
|
||||
private static void logError(String alias, String type, Exception ex) {
|
||||
Log.e(TAG, "Unable to retrieve " + type + " for [" + alias + "] due to " + ex);
|
||||
}
|
||||
|
||||
private KeyChainKeyManager(
|
||||
String clientAlias, X509Certificate[] certificateChain, PrivateKey privateKey) {
|
||||
mClientAlias = clientAlias;
|
||||
|
|
|
@ -760,10 +760,18 @@ save attachment.</string>
|
|||
<!-- "Setup could not finish" dialog text; e.g., Username or password incorrect\n(ERR01 Account does not exist) -->
|
||||
<string name="account_setup_failed_dlg_auth_message_fmt">Username or password incorrect.\n(<xliff:g id="error">%s</xliff:g>)</string>
|
||||
|
||||
<!-- "Setup could not finish" dialog text; e.g., Cannot safely connect to server -->
|
||||
<string name="account_setup_failed_dlg_certificate_message">Cannot safely connect to server.</string>
|
||||
<!-- "Setup could not finish" dialog text; e.g., Cannot safely connect to server\n(TLS Not Supported) -->
|
||||
<string name="account_setup_failed_dlg_certificate_message_fmt">Cannot safely connect to server.\n(<xliff:g id="error">%s</xliff:g>)</string>
|
||||
<!-- An error message presented to the user when the server's identity
|
||||
cannot be established or trusted [CHAR LIMIT=NONE] -->
|
||||
<string name="account_setup_failed_dlg_certificate_message"
|
||||
>Cannot safely connect to server.</string>
|
||||
<!-- An error message presented to the user when the server's identity
|
||||
cannot be established or trusted [CHAR LIMIT=NONE] -->
|
||||
<string name="account_setup_failed_dlg_certificate_message_fmt"
|
||||
>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>
|
||||
|
||||
<!-- Dialog text for ambiguous setup failure; server error/bad credentials [CHAR LIMIT=none] -->
|
||||
<string name="account_setup_failed_check_credentials_message">
|
||||
|
|
|
@ -645,6 +645,9 @@ public class AccountCheckSettingsFragment extends Fragment {
|
|||
case MessagingException.GENERAL_SECURITY:
|
||||
id = R.string.account_setup_failed_security;
|
||||
break;
|
||||
case MessagingException.CLIENT_CERTIFICATE_ERROR:
|
||||
id = R.string.account_setup_failed_certificate_required;
|
||||
break;
|
||||
default:
|
||||
id = TextUtils.isEmpty(message)
|
||||
? R.string.account_setup_failed_dlg_server_message
|
||||
|
|
Loading…
Reference in New Issue