Skip to content

Instantly share code, notes, and snippets.

@longbai
Last active January 30, 2016 06:17
Show Gist options
  • Save longbai/fa72b6c5f2648131f14d to your computer and use it in GitHub Desktop.
Save longbai/fa72b6c5f2648131f14d to your computer and use it in GitHub Desktop.
public class TlsSniSocketFactory extends MySSLSocketFactory {
private static final String TAG = "davdroid.SNISocketFactory";
final static HostnameVerifier hostnameVerifier = new StrictHostnameVerifier();
// Plain TCP/IP (layer below TLS)
@Override
public Socket connectSocket(Socket s, String host, int port, InetAddress localAddress, int localPort, HttpParams params) throws IOException {
// enable TLSv1.1/1.2 if available
// (see https://github.com/rfc2822/davdroid/issues/229)
ssl.setEnabledProtocols(ssl.getSupportedProtocols());
// set up SNI before the handshake
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Log.i(TAG, "Setting SNI hostname");
this.setHostname(ssl, host);
} 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);
}
}
// copy super implements to here
}
@Override
public Socket createSocket() throws IOException {
return super.createSocket();
}
@Override
public boolean isSecure(Socket s) throws IllegalArgumentException {
if (s instanceof SSLSocket)
return ((SSLSocket)s).isConnected();
return false;
}
// TLS layer
@Override
public Socket createSocket(Socket plainSocket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
if (autoClose) {
// we don't need the plainSocket
plainSocket.close();
}
// create and connect SSL socket, but don't do hostname/certificate verification yet
SSLCertificateSocketFactory sslSocketFactory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(0);
SSLSocket ssl = (SSLSocket)sslSocketFactory.createSocket(InetAddress.getByName(host), port);
// enable TLSv1.1/1.2 if available
// (see https://github.com/rfc2822/davdroid/issues/229)
ssl.setEnabledProtocols(ssl.getSupportedProtocols());
// set up SNI before the handshake
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Log.i(TAG, "Setting SNI hostname");
sslSocketFactory.setHostname(ssl, host);
} 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, session))
throw new SSLPeerUnverifiedException("Cannot verify hostname: " + host);
Log.i(TAG, "Established " + session.getProtocol() + " connection with " + session.getPeerHost() +
" using " + session.getCipherSuite());
return ssl;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment