Get SSLSocketFactory from GmsCore

b/15721931
This follows an example pattern from GoogleHttpClient.
It tries to get the SSLCertificateSocketFactory from
GmsCore using reflection. If that fails, (because GmsCore
is not installed on the device) then it will fall back
to the platform implementation.
MailApplication sets a static object in SSLUtils that
allows it to get an externally created SSLCertifcateSocketFactory.
If this method is set, then it will use it, otherwise it
will fall back to the platform factory. This way there
is no reference to GmsCore in the AOSP email.

Change-Id: I0890fe4c3d79283fb98a4dc5a62a32efd320e52a
This commit is contained in:
Martin Hibdon 2014-09-25 20:24:55 -07:00
parent 083a013826
commit 601700a61e
2 changed files with 49 additions and 14 deletions

View File

@ -21,6 +21,7 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.SSLCertificateSocketFactory;
import android.net.SSLSessionCache;
import android.security.KeyChain;
import android.security.KeyChainException;
@ -49,7 +50,7 @@ import javax.net.ssl.X509TrustManager;
public class SSLUtils {
// All secure factories are the same; all insecure factories are associated with HostAuth's
private static SSLCertificateSocketFactory sSecureFactory;
private static javax.net.ssl.SSLSocketFactory sSecureFactory;
private static final boolean LOG_ENABLED = false;
private static final String TAG = "Email.Ssl";
@ -137,40 +138,73 @@ public class SSLUtils {
}
}
public static abstract class ExternalSecureSocketFactoryBuilder {
abstract public javax.net.ssl.SSLSocketFactory createSecureSocketFactory(
final Context context, final int handshakeTimeoutMillis);
}
private static ExternalSecureSocketFactoryBuilder sExternalSocketFactoryBuilder;
public static void setExternalSecureSocketFactoryBuilder(
ExternalSecureSocketFactoryBuilder builder) {
sExternalSocketFactoryBuilder = builder;
}
/**
* Returns a {@link javax.net.ssl.SSLSocketFactory}.
* Optionally bypass all SSL certificate checks.
*
* @param insecure if true, bypass all SSL certificate checks
*/
public synchronized static SSLCertificateSocketFactory getSSLSocketFactory(Context context,
HostAuth hostAuth, boolean insecure) {
public synchronized static javax.net.ssl.SSLSocketFactory getSSLSocketFactory(
final Context context, final HostAuth hostAuth, final KeyManager keyManager,
final boolean insecure) {
if (insecure) {
SSLCertificateSocketFactory insecureFactory = (SSLCertificateSocketFactory)
final SSLCertificateSocketFactory insecureFactory = (SSLCertificateSocketFactory)
SSLCertificateSocketFactory.getInsecure(SSL_HANDSHAKE_TIMEOUT, null);
insecureFactory.setTrustManagers(
new TrustManager[] {
new SameCertificateCheckingTrustManager(context, hostAuth)});
if (keyManager != null) {
insecureFactory.setKeyManagers(new KeyManager[] { keyManager });
}
return insecureFactory;
} else {
if (sSecureFactory == null) {
sSecureFactory = (SSLCertificateSocketFactory)
SSLCertificateSocketFactory.getDefault(SSL_HANDSHAKE_TIMEOUT, null);
// First try to get use an externally supplied, more secure SSLSocketBuilder.
// If so we should use that.
javax.net.ssl.SSLSocketFactory socketFactory = null;
if (sExternalSocketFactoryBuilder != null) {
socketFactory = sExternalSocketFactoryBuilder.createSecureSocketFactory(
context, SSL_HANDSHAKE_TIMEOUT);
}
if (socketFactory != null) {
sSecureFactory = socketFactory;
LogUtils.d(TAG, "Using externally created CertificateSocketFactory");
return sSecureFactory;
}
// Only fall back to the platform one if that fails.
LogUtils.d(TAG, "Falling back to platform CertificateSocketFactory");
final SSLCertificateSocketFactory certificateSocketFactory =
(SSLCertificateSocketFactory)
SSLCertificateSocketFactory.getDefault(SSL_HANDSHAKE_TIMEOUT,
new SSLSessionCache(context));
if (keyManager != null) {
certificateSocketFactory.setKeyManagers(new KeyManager[] { keyManager });
}
sSecureFactory = certificateSocketFactory;
}
return sSecureFactory;
}
}
/**
* Returns a {@link org.apache.http.conn.ssl.SSLSocketFactory SSLSocketFactory} for use with the
* Apache HTTP stack.
* Returns a com.android.emailcommon.utility.SSLSocketFactory
*/
public static SSLSocketFactory getHttpSocketFactory(Context context, HostAuth hostAuth,
KeyManager keyManager, boolean insecure) {
SSLCertificateSocketFactory underlying = getSSLSocketFactory(context, hostAuth, insecure);
if (keyManager != null) {
underlying.setKeyManagers(new KeyManager[] { keyManager });
}
javax.net.ssl.SSLSocketFactory underlying = getSSLSocketFactory(context, hostAuth,
keyManager, insecure);
SSLSocketFactory wrapped = new SSLSocketFactory(underlying);
if (insecure) {
wrapped.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

View File

@ -112,7 +112,7 @@ public class MailTransport {
SocketAddress socketAddress = new InetSocketAddress(getHost(), getPort());
if (canTrySslSecurity()) {
mSocket = SSLUtils.getSSLSocketFactory(
mContext, mHostAuth, canTrustAllCertificates()).createSocket();
mContext, mHostAuth, null, canTrustAllCertificates()).createSocket();
} else {
mSocket = new Socket();
}
@ -152,7 +152,8 @@ public class MailTransport {
*/
public void reopenTls() throws MessagingException {
try {
mSocket = SSLUtils.getSSLSocketFactory(mContext, mHostAuth, canTrustAllCertificates())
mSocket = SSLUtils.getSSLSocketFactory(mContext, mHostAuth, null,
canTrustAllCertificates())
.createSocket(mSocket, getHost(), getPort(), true);
mSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
mIn = new BufferedInputStream(mSocket.getInputStream(), 1024);