diff --git a/emailcommon/src/com/android/emailcommon/utility/EmailClientConnectionManager.java b/emailcommon/src/com/android/emailcommon/utility/EmailClientConnectionManager.java index 5b50e2b38..36ece4261 100644 --- a/emailcommon/src/com/android/emailcommon/utility/EmailClientConnectionManager.java +++ b/emailcommon/src/com/android/emailcommon/utility/EmailClientConnectionManager.java @@ -17,6 +17,10 @@ package com.android.emailcommon.utility; +import android.content.Context; +import android.net.SSLCertificateSocketFactory; +import android.util.Log; + import com.android.emailcommon.Logging; import com.android.emailcommon.utility.SSLUtils.KeyChainKeyManager; import com.android.emailcommon.utility.SSLUtils.TrackingKeyManager; @@ -27,10 +31,6 @@ import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.params.HttpParams; -import android.content.Context; -import android.net.SSLCertificateSocketFactory; -import android.util.Log; - import java.security.cert.CertificateException; import javax.net.ssl.KeyManager; @@ -43,11 +43,18 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager { private static final boolean LOG_ENABLED = false; + /** + * A {@link KeyManager} to track client certificate requests from servers. + */ + private final TrackingKeyManager mTrackingKeyManager; + /** * Not publicly instantiable except via {@link #newInstance(HttpParams)} */ - private EmailClientConnectionManager(HttpParams params, SchemeRegistry registry) { + private EmailClientConnectionManager( + HttpParams params, SchemeRegistry registry, TrackingKeyManager keyManager) { super(params, registry); + mTrackingKeyManager = keyManager; } public static EmailClientConnectionManager newInstance(HttpParams params) { @@ -64,7 +71,7 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager { registry.register(new Scheme("httpts", SSLUtils.getHttpSocketFactory(true /*insecure*/, keyManager), 443)); - return new EmailClientConnectionManager(params, registry); + return new EmailClientConnectionManager(params, registry, keyManager); } /** @@ -129,4 +136,13 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager { String safeAlias = SSLUtils.escapeForSchemeName(clientCertAlias); return (trustAllServerCerts ? "httpts" : "https") + "+clientCert+" + safeAlias; } + + /** + * @param since A timestamp in millis from epoch from which to check + * @return whether or not this connection manager has detected any unsatisfied requests for + * a client SSL certificate by any servers + */ + public synchronized boolean hasDetectedUnsatisfiedCertReq(long since) { + return mTrackingKeyManager.getLastCertReqTime() >= since; + } } diff --git a/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java b/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java index 620a8d050..aa5229aec 100644 --- a/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java +++ b/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java @@ -16,14 +16,15 @@ package com.android.emailcommon.utility; -import com.google.common.annotations.VisibleForTesting; - import android.content.Context; import android.net.SSLCertificateSocketFactory; import android.security.KeyChain; import android.security.KeyChainException; import android.util.Log; +import com.google.common.annotations.VisibleForTesting; + +import java.net.InetAddress; import java.net.Socket; import java.security.Principal; import java.security.PrivateKey; @@ -137,35 +138,46 @@ public class SSLUtils { } /** - * A dummy {@link KeyManager} which throws a {@link CertificateRequestedException} if the - * server requests a certificate. + * A dummy {@link KeyManager} which keeps track of the last time a server has requested + * a client certificate. */ public static class TrackingKeyManager extends StubKeyManager { + private volatile long mLastTimeCertRequested = 0L; + @Override public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) { if (LOG_ENABLED) { + InetAddress address = socket.getInetAddress(); Log.i(TAG, "TrackingKeyManager: requesting a client cert alias for " - + Arrays.toString(keyTypes)); + + address.getCanonicalHostName()); } - throw new CertificateRequestedException(); + mLastTimeCertRequested = System.currentTimeMillis(); + return null; } @Override public X509Certificate[] getCertificateChain(String alias) { + if (LOG_ENABLED) { + Log.i(TAG, "TrackingKeyManager: returning a null cert chain"); + } return null; } @Override public PrivateKey getPrivateKey(String alias) { + if (LOG_ENABLED) { + Log.i(TAG, "TrackingKeyManager: returning a null private key"); + } 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 { + /** + * @return the last time that this {@link KeyManager} detected a request by a server + * for a client certificate (in millis since epoch). + */ + public long getLastCertReqTime() { + return mLastTimeCertRequested; + } } /**