mirror of
https://github.com/nextcloud/android.git
synced 2024-11-23 13:45:35 +03:00
Enable Server Name Indication in HTTPS connections if available in the network implementation under the API
This commit is contained in:
parent
59f806f692
commit
7ed9f29973
2 changed files with 194 additions and 3 deletions
|
@ -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<elements.length ; i++) {
|
||||
Log.v(TAG, " " + elements[i]);
|
||||
}
|
||||
Log.v(TAG, "Protocols: ");
|
||||
elements = params.getProtocols();
|
||||
for (int i=0; i<elements.length ; i++) {
|
||||
Log.v(TAG, " " + elements[i]);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Attempts to get a new socket connection to the given host within the
|
||||
* given time limit.
|
||||
*
|
||||
|
@ -110,6 +153,9 @@ public class AdvancedSslSocketFactory implements ProtocolSocketFactory {
|
|||
throw new IllegalArgumentException("Parameters may not be null");
|
||||
}
|
||||
int timeout = params.getConnectionTimeout();
|
||||
|
||||
//logSslInfo();
|
||||
|
||||
SocketFactory socketfactory = mSslContext.getSocketFactory();
|
||||
Log.d(TAG, " ... with connection timeout " + timeout + " and socket timeout " + params.getSoTimeout());
|
||||
Socket socket = socketfactory.createSocket();
|
||||
|
@ -117,12 +163,13 @@ public class AdvancedSslSocketFactory implements ProtocolSocketFactory {
|
|||
SocketAddress remoteaddr = new InetSocketAddress(host, port);
|
||||
socket.setSoTimeout(params.getSoTimeout());
|
||||
socket.bind(localaddr);
|
||||
ServerNameIndicator.setServerNameIndication(host, (SSLSocket)socket);
|
||||
socket.connect(remoteaddr, timeout);
|
||||
verifyPeerIdentity(host, port, socket);
|
||||
return socket;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @see ProtocolSocketFactory#createSocket(java.lang.String,int)
|
||||
*/
|
||||
public Socket createSocket(String host, int port) throws IOException,
|
||||
|
@ -238,5 +285,5 @@ public class AdvancedSslSocketFactory implements ProtocolSocketFactory {
|
|||
throw io;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/* ownCloud Android client application
|
||||
* Copyright (C) 2012-2013 ownCloud Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
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<ServerNameIndicator> mSingleInstance = new AtomicReference<ServerNameIndicator>();
|
||||
|
||||
private static final String METHOD_NAME = "setHostname";
|
||||
|
||||
private final WeakReference<Class<?>> mSSLSocketClassRef;
|
||||
private final WeakReference<Method> 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<Class<?>>(sslSocketClass);
|
||||
mSetHostnameMethodRef = (setHostnameMethod == null) ? null : new WeakReference<Method>(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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue