Make client certificate requests optional.
This prevents things from always failing if the server requires a client SSL certificate. Note that the solution used to determine if a certificate request was made for a given request is approximate; it is timestamp based and can theoretically give a false positive. In practice, this is very unlikely, since another cert request had to have happened around the same time, AND the response must be a 401/403. Change-Id: Ieb77cf91db3bd52ba4adf1fb07357fef7e204ba5
This commit is contained in:
parent
490708b079
commit
f4f10a3fdf
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue