mirror of https://github.com/etesync/android
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.
90 lines
3.5 KiB
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;
|
|
}
|
|
}
|