Skip to content
This repository has been archived by the owner on Nov 8, 2023. It is now read-only.

Commit

Permalink
email: add support for Server Name Indication (SNI)
Browse files Browse the repository at this point in the history
Change the underlaying socket factory reference to SSLCertificateSocketFactory which
has support for SNI since 4.2, and remove the access to the obsolete methods from
the old factory reference.
This change will setup the socket with a proper hostname prior to the ssl handshake.

Change-Id: Ie537b1d8c3da33de3665e481320be134939155ca
Signed-off-by: Jorge Ruesga <j.ruesga.criado@gmail.com>
  • Loading branch information
Jorge Ruesga committed Mar 29, 2014
1 parent 4c28b38 commit a7a4831
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 34 deletions.
2 changes: 1 addition & 1 deletion emailcommon/Android.mk
Expand Up @@ -42,7 +42,7 @@ LOCAL_SRC_FILES += $(call all-java-files-under, $(apache_src_dir))
LOCAL_SRC_FILES += $(imported_unified_email_files)
LOCAL_SRC_FILES += $(call all-java-files-under, $(unified_email_src_dir)/com/android/emailcommon)

LOCAL_SDK_VERSION := 14
LOCAL_SDK_VERSION := 17

LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res

Expand Down
Expand Up @@ -33,6 +33,10 @@

package com.android.emailcommon.utility;

import android.annotation.TargetApi;
import android.net.SSLCertificateSocketFactory;
import android.os.Build;

import org.apache.http.conn.scheme.HostNameResolver;
import org.apache.http.conn.scheme.LayeredSocketFactory;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
Expand All @@ -42,7 +46,6 @@
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
Expand Down Expand Up @@ -156,21 +159,9 @@ public class SSLSocketFactory implements LayeredSocketFactory {

public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER
= new StrictHostnameVerifier();
/**
* The factory using the default JVM settings for secure connections.
*/
private static final SSLSocketFactory DEFAULT_FACTORY = new SSLSocketFactory();

/**
* Gets an singleton instance of the SSLProtocolSocketFactory.
* @return a SSLProtocolSocketFactory
*/
public static SSLSocketFactory getSocketFactory() {
return DEFAULT_FACTORY;
}

private final SSLContext sslcontext;
private final javax.net.ssl.SSLSocketFactory socketfactory;
private final SSLCertificateSocketFactory socketfactory;
private final HostNameResolver nameResolver;
private X509HostnameVerifier hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;

Expand All @@ -197,7 +188,7 @@ public SSLSocketFactory(
}
sslcontext = SSLContext.getInstance(algorithm);
sslcontext.init(keymanagers, trustmanagers, random);
socketfactory = sslcontext.getSocketFactory();
socketfactory = (SSLCertificateSocketFactory) sslcontext.getSocketFactory();
this.nameResolver = nameResolver;
}

Expand Down Expand Up @@ -226,25 +217,13 @@ public SSLSocketFactory(final KeyStore truststore)
* Constructs an HttpClient SSLSocketFactory backed by the given JSSE
* SSLSocketFactory.
*/
public SSLSocketFactory(javax.net.ssl.SSLSocketFactory socketfactory) {
public SSLSocketFactory(SSLCertificateSocketFactory socketfactory) {
super();
sslcontext = null;
this.socketfactory = socketfactory;
nameResolver = null;
}

/**
* Creates the default SSL socket factory.
* This constructor is used exclusively to instantiate the factory for
* {@link #getSocketFactory getSocketFactory}.
*/
private SSLSocketFactory() {
super();
sslcontext = null;
socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory();
nameResolver = null;
}

private static KeyManager[] createKeyManagers(final KeyStore keystore, final String password)
throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
if (keystore == null) {
Expand Down Expand Up @@ -280,6 +259,7 @@ public Socket createSocket()

// non-javadoc, see interface org.apache.http.conn.SocketFactory
@Override
@TargetApi(17)
public Socket connectSocket(
final Socket sock,
final String host,
Expand Down Expand Up @@ -323,6 +303,12 @@ public Socket connectSocket(
sslsock.connect(remoteAddress, connTimeout);

sslsock.setSoTimeout(soTimeout);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
// Turn on Server Name Indication (SNI)
socketfactory.setHostname(sslsock, host);
}

try {
hostnameVerifier.verify(host, sslsock);
// verifyHostName() didn't blowup - good!
Expand Down Expand Up @@ -374,19 +360,43 @@ public boolean isSecure(Socket sock)

// non-javadoc, see interface LayeredSocketFactory
@Override
@TargetApi(17)
public Socket createSocket(
final Socket socket,
final String host,
final int port,
final boolean autoClose
) throws IOException, UnknownHostException {
// Close the plain socket if requested. The underlaying socket factory will
// create a new socket.
if (autoClose) {
socket.close();
}

// We don't want to verify the hostname from the previous socket here (we must call
// setHostname in order to proper get SNI working), so just create a new ssl socket
// based in the previous socket
SSLSocket sslSocket = (SSLSocket) socketfactory.createSocket(
socket,
host,
port,
autoClose
socket.getInetAddress(),
port
);
hostnameVerifier.verify(host, sslSocket);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
// Turn on Server Name Indication (SNI)
socketfactory.setHostname(sslSocket, host);
}

try {
hostnameVerifier.verify(host, sslSocket);
// verifyHostName() didn't blowup - good!
} catch (IOException iox) {
// close the socket before re-throwing the exception
if (autoClose) {
try { sslSocket.close(); } catch (Exception x) { /*ignore*/ }
}
throw iox;
}

// verifyHostName() didn't blowup - good!
return sslSocket;
}
Expand Down

0 comments on commit a7a4831

Please sign in to comment.