You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
etesync-android/src/at/bitfire/davdroid/webdav/TlsSniSocketFactory.java

90 lines
3.5 KiB

/*******************************************************************************
* Copyright (c) 2014 Richard Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* Richard Hirner (bitfire web engineering) - initial API and implementation
******************************************************************************/
package at.bitfire.davdroid.webdav;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import android.annotation.TargetApi;
import android.net.SSLCertificateSocketFactory;
import android.os.Build;
import android.util.Log;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.conn.socket.LayeredConnectionSocketFactory;
import ch.boye.httpclientandroidlib.conn.ssl.BrowserCompatHostnameVerifier;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public class TlsSniSocketFactory implements LayeredConnectionSocketFactory {
private static final String TAG = "davdroid.SNISocketFactory";
final static TlsSniSocketFactory INSTANCE = new TlsSniSocketFactory();
private final static SSLCertificateSocketFactory sslSocketFactory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(0);
private final static HostnameVerifier hostnameVerifier = new BrowserCompatHostnameVerifier();
@Override
public Socket createSocket(HttpContext context) throws IOException {
return sslSocketFactory.createSocket();
}
@Override
public Socket connectSocket(int timeout, Socket socket, HttpHost host, InetSocketAddress remoteAddr, InetSocketAddress localAddr, HttpContext context) throws IOException {
// we'll rather create a new socket
socket.close();
// create and connect SSL socket, but don't do hostname/certificate verification yet
SSLSocket ssl = (SSLSocket)sslSocketFactory.createSocket(remoteAddr.getAddress(), host.getPort());
// set up SNI before the handshake
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
// Android 4.2+, use documented way to set SNI host name
Log.d(TAG, "Setting SNI hostname");
sslSocketFactory.setHostname(ssl, host.getHostName());
} else {
Log.d(TAG, "No documented SNI support on Android <4.2, trying with reflection");
try {
java.lang.reflect.Method setHostnameMethod = ssl.getClass().getMethod("setHostname", String.class);
setHostnameMethod.invoke(ssl, host.getHostName());
} catch (Exception e) {
Log.w(TAG, "SNI not useable", e);
}
}
// verify hostname and certificate
SSLSession session = ssl.getSession();
if (!hostnameVerifier.verify(host.getHostName(), session))
throw new SSLPeerUnverifiedException("Cannot verify hostname: " + host);
Log.i(TAG, "Established " + session.getProtocol() + " connection with " + session.getPeerHost() +
" using " + session.getCipherSuite());
return ssl;
}
// TLS layer
@Override
public Socket createLayeredSocket(Socket plainSocket, String host, int port, HttpContext context) throws IOException, UnknownHostException {
Log.wtf(TAG, "createLayeredSocket should never be called");
return plainSocket;
}
}