From 601700a61e453c612e0dabe4e93002766b3751b7 Mon Sep 17 00:00:00 2001 From: Martin Hibdon Date: Thu, 25 Sep 2014 20:24:55 -0700 Subject: [PATCH] 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 --- .../android/emailcommon/utility/SSLUtils.java | 58 +++++++++++++++---- .../email/mail/transport/MailTransport.java | 5 +- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java b/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java index 33ddde7dc..779bc5296 100644 --- a/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java +++ b/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java @@ -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); diff --git a/provider_src/com/android/email/mail/transport/MailTransport.java b/provider_src/com/android/email/mail/transport/MailTransport.java index 213fbfc99..74d0ab59e 100644 --- a/provider_src/com/android/email/mail/transport/MailTransport.java +++ b/provider_src/com/android/email/mail/transport/MailTransport.java @@ -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);