Created
July 21, 2020 10:39
-
-
Save rayworks/91e954ce90ac7d07d07064736abc06a8 to your computer and use it in GitHub Desktop.
The compatible version of SSLSocketFactory for resolving SNI issue when using library android-async-http with version 1.4.x
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.util.Log; | |
import java.io.IOException; | |
import java.lang.reflect.Method; | |
import java.net.Socket; | |
import java.util.ArrayList; | |
import java.util.List; | |
import javax.net.ssl.SSLContext; | |
import javax.net.ssl.SSLSocket; | |
import cz.msebera.android.httpclient.conn.ssl.SSLSocketFactory; | |
import cz.msebera.android.httpclient.conn.ssl.X509HostnameVerifier; | |
import cz.msebera.android.httpclient.protocol.HttpContext; | |
/*** | |
* The compatible version of {@link SSLSocketFactory} for resolving SNI issue. | |
* {@link cz.msebera.android.httpclient.conn.ssl.SSLConnectionSocketFactory} is the recommended one, | |
* however {@link com.loopj.android.http.AsyncHttpClient} doesn't integrate it properly. | |
* | |
* <p> | |
* TODO: refactor it with OKHttp | |
* @see <a href="https://github.com/smarek/httpclient-android/issues/7"> the original issue on httpclient-android</a> | |
* @see <a href="https://www.cloudflare.com/learning/ssl/what-is-sni/">What is SNI</a> | |
* </p> | |
*/ | |
class CompatibleSSLSockFactory extends SSLSocketFactory { | |
public static final String TAG = "CompatSSLSockFactory"; | |
private SSLContext sslContext; | |
public CompatibleSSLSockFactory(SSLContext sslContext, X509HostnameVerifier hostnameVerifier) { | |
super(sslContext, hostnameVerifier); | |
this.sslContext = sslContext; | |
} | |
@Override | |
public Socket createLayeredSocket( | |
final Socket socket, | |
final String target, | |
final int port, | |
final HttpContext context) throws IOException { | |
final SSLSocket sslsock = (SSLSocket) sslContext.getSocketFactory().createSocket(socket, target, port, true); | |
// internalPrepareSocket(sslsock); | |
// If supported protocols are not explicitly set, remove all SSL protocol versions | |
final String[] allProtocols = sslsock.getEnabledProtocols(); | |
final List<String> enabledProtocols = new ArrayList<>(allProtocols.length); | |
for (String protocol : allProtocols) { | |
if (!protocol.startsWith("SSL")) { | |
enabledProtocols.add(protocol); | |
} | |
} | |
if (!enabledProtocols.isEmpty()) { | |
sslsock.setEnabledProtocols(enabledProtocols.toArray(new String[enabledProtocols.size()])); | |
} | |
// Android specific code to enable SNI : SDK 21+ | |
if (Log.isLoggable(TAG, Log.DEBUG)) { | |
Log.d(TAG, "Enabling SNI for " + target); | |
} | |
try { | |
Method method = sslsock.getClass().getMethod("setHostname", String.class); | |
method.invoke(sslsock, target); | |
} catch (Exception ex) { | |
if (Log.isLoggable(TAG, Log.DEBUG)) { | |
Log.d(TAG, "SNI configuration failed", ex); | |
} | |
} | |
// End of Android specific code | |
sslsock.startHandshake(); | |
verifyHostname(sslsock, target); | |
return sslsock; | |
} | |
private void verifyHostname(final SSLSocket sslsock, final String hostname) throws IOException { | |
try { | |
BROWSER_COMPATIBLE_HOSTNAME_VERIFIER.verify(hostname, sslsock); | |
// verifyHostName() didn't blowup - good! | |
} catch (final IOException iox) { | |
// close the socket before re-throwing the exception | |
try { | |
sslsock.close(); | |
} catch (final Exception x) { /*ignore*/ } | |
throw iox; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
See also 'enable SNI on TLS to get the proper certificate'