From: David A. Velasco Date: Mon, 25 Nov 2013 13:43:03 +0000 (+0100) Subject: Enable Server Name Indication in HTTPS connections if available in the network implem... X-Git-Tag: oc-android-1.5.5~113^2~2 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/7ed9f299733cf6de6bd91a2861f1f4be657ca4e2?ds=inline;hp=--cc Enable Server Name Indication in HTTPS connections if available in the network implementation under the API --- 7ed9f299733cf6de6bd91a2861f1f4be657ca4e2 diff --git a/oc_framework/src/com/owncloud/android/oc_framework/network/AdvancedSslSocketFactory.java b/oc_framework/src/com/owncloud/android/oc_framework/network/AdvancedSslSocketFactory.java index e24fa364..775473a2 100644 --- a/oc_framework/src/com/owncloud/android/oc_framework/network/AdvancedSslSocketFactory.java +++ b/oc_framework/src/com/owncloud/android/oc_framework/network/AdvancedSslSocketFactory.java @@ -24,12 +24,15 @@ import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.net.UnknownHostException; +//import java.security.Provider; import java.security.cert.X509Certificate; +//import java.util.Enumeration; import javax.net.SocketFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; +//import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; @@ -39,6 +42,7 @@ import org.apache.commons.httpclient.params.HttpConnectionParams; import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; import org.apache.http.conn.ssl.X509HostnameVerifier; +//import android.os.Build; import android.util.Log; @@ -84,8 +88,47 @@ public class AdvancedSslSocketFactory implements ProtocolSocketFactory { return socket; } + /* + private void logSslInfo() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.FROYO) { + Log.v(TAG, "SUPPORTED SSL PARAMETERS"); + logSslParameters(mSslContext.getSupportedSSLParameters()); + Log.v(TAG, "DEFAULT SSL PARAMETERS"); + logSslParameters(mSslContext.getDefaultSSLParameters()); + Log.i(TAG, "CURRENT PARAMETERS"); + Log.i(TAG, "Protocol: " + mSslContext.getProtocol()); + } + Log.i(TAG, "PROVIDER"); + logSecurityProvider(mSslContext.getProvider()); + } - /** + private void logSecurityProvider(Provider provider) { + Log.i(TAG, "name: " + provider.getName()); + Log.i(TAG, "version: " + provider.getVersion()); + Log.i(TAG, "info: " + provider.getInfo()); + Enumeration keys = provider.propertyNames(); + String key; + while (keys.hasMoreElements()) { + key = (String) keys.nextElement(); + Log.i(TAG, " property " + key + " : " + provider.getProperty(key)); + } + } + + private void logSslParameters(SSLParameters params) { + Log.v(TAG, "Cipher suites: "); + String [] elements = params.getCipherSuites(); + for (int i=0; i. + * + */ + +package com.owncloud.android.oc_framework.network; + +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicReference; + +import javax.net.ssl.SSLSocket; + +import android.util.Log; + + +/** + * Enables the support of Server Name Indication if existing + * in the underlying network implementation. + * + * Build as a singleton. + * + * @author David A. Velasco + */ +public class ServerNameIndicator { + + private static final String TAG = ServerNameIndicator.class.getSimpleName(); + + private static final AtomicReference mSingleInstance = new AtomicReference(); + + private static final String METHOD_NAME = "setHostname"; + + private final WeakReference> mSSLSocketClassRef; + private final WeakReference mSetHostnameMethodRef; + + + /** + * Private constructor, class is a singleton. + * + * @param sslSocketClass Underlying implementation class of {@link SSLSocket} used to connect with the server. + * @param setHostnameMethod Name of the method to call to enable the SNI support. + */ + private ServerNameIndicator(Class sslSocketClass, Method setHostnameMethod) { + mSSLSocketClassRef = new WeakReference>(sslSocketClass); + mSetHostnameMethodRef = (setHostnameMethod == null) ? null : new WeakReference(setHostnameMethod); + } + + + /** + * Calls the {@code #setHostname(String)} method of the underlying implementation + * of {@link SSLSocket} if exists. + * + * Creates and initializes the single instance of the class when needed + * + * @param hostname The name of the server host of interest. + * @param sslSocket Client socket to connect with the server. + */ + public static void setServerNameIndication(String hostname, SSLSocket sslSocket) { + final Method setHostnameMethod = getMethod(sslSocket); + if (setHostnameMethod != null) { + try { + setHostnameMethod.invoke(sslSocket, hostname); + Log.i(TAG, "SNI done, hostname: " + hostname); + + } catch (IllegalArgumentException e) { + Log.e(TAG, "Call to SSLSocket#setHost(String) failed ", e); + + } catch (IllegalAccessException e) { + Log.e(TAG, "Call to SSLSocket#setHost(String) failed ", e); + + } catch (InvocationTargetException e) { + Log.e(TAG, "Call to SSLSocket#setHost(String) failed ", e); + } + } else { + Log.i(TAG, "SNI not supported"); + } + } + + + /** + * Gets the method to invoke trying to minimize the effective + * application of reflection. + * + * @param sslSocket Instance of the SSL socket to use in connection with server. + * @return Method to call to indicate the server name of interest to the server. + */ + private static Method getMethod(SSLSocket sslSocket) { + final Class sslSocketClass = sslSocket.getClass(); + final ServerNameIndicator instance = mSingleInstance.get(); + if (instance == null) { + return initFrom(sslSocketClass); + + } else if (instance.mSSLSocketClassRef.get() != sslSocketClass) { + // the underlying class changed + return initFrom(sslSocketClass); + + } else if (instance.mSetHostnameMethodRef == null) { + // SNI not supported + return null; + + } else { + final Method cachedSetHostnameMethod = instance.mSetHostnameMethodRef.get(); + return (cachedSetHostnameMethod == null) ? initFrom(sslSocketClass) : cachedSetHostnameMethod; + } + } + + + /** + * Singleton initializer. + * + * Uses reflection to extract and 'cache' the method to invoke to indicate the desited host name to the server side. + * + * @param sslSocketClass Underlying class providing the implementation of {@link SSLSocket}. + * @return Method to call to indicate the server name of interest to the server. + */ + private static Method initFrom(Class sslSocketClass) { + Log.i(TAG, "SSLSocket implementation: " + sslSocketClass.getCanonicalName()); + Method setHostnameMethod = null; + try { + setHostnameMethod = sslSocketClass.getMethod(METHOD_NAME, String.class); + } catch (SecurityException e) { + Log.e(TAG, "Could not access to SSLSocket#setHostname(String) method ", e); + + } catch (NoSuchMethodException e) { + Log.i(TAG, "Could not find SSLSocket#setHostname(String) method - SNI not supported"); + } + mSingleInstance.set(new ServerNameIndicator(sslSocketClass, setHostnameMethod)); + return setHostnameMethod; + } + +}