Error: SSL handshake aborted: ssl=0x676a5680: Failure in SSL library, usually a protocol error
According to this Android doc, TLS 1.1 and 1.2 is supported on API 16+ but not enabled by default until API 20+. I found some solutions(here, here, here and here) for enabling TLS 1.1 and 1.2 support for OkHttp. How can I enable the TLS 1.1/1.2 support for Exoplayer? The only post I found for Exoplayer TLS 1.1/1.2 support is from this github issue which suggested to ask the question here instead.
"07-27 13:21:09.817 8925-9065/com.ftsgps.monarch E/ExoPlayerImplInternal: Source error. com.google.android.exoplayer2.upstream.HttpDataSource$HttpDataSourceException: Unable to connect to https://liveStream/LIVE-0089000D05/manifest.mpd at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:194) at com.google.android.exoplayer2.upstream.DefaultDataSource.open(DefaultDataSource.java:147) at com.google.android.exoplayer2.upstream.DataSourceInputStream.checkOpened(DataSourceInputStream.java:102) at com.google.android.exoplayer2.upstream.DataSourceInputStream.open(DataSourceInputStream.java:65) at com.google.android.exoplayer2.upstream.ParsingLoadable.load(ParsingLoadable.java:129) at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:308) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) at java.lang.Thread.run(Thread.java:841) Caused by: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x722c3af8: Failure in SSL library, usually a protocol error error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:744 0x689d8f10:0x00000000)"
This is happening only below API 21 version (lollipop). Server is using TLS1.2 protocol, which isn't support on Android below Lollipop version.
DefaultHttpDataSource
uses HttpsURLConnection
which has a static field for default SSLSocketFactory
. All new instances of HttpsURLConnection
will have this default SSLSocketFactory
assigned unless setSSLSocketFactory()
is called on an instance. So technically, if you call set the default SSLSocketFactory
before instantiating DefaultHttpsDataSource
, it should work:
HttpsURLConnection.setDefaultSSLSocketFactory(new MyCustomSSLSocketFactory());
where MyCustomSSLSocketFactoy
may look something like this:
class MyCustomSSLSocketFactory extends SSLSocketFactory {
private javax.net.ssl.SSLSocketFactory internalSSLSocketFactory;
public MyCustomSSLSocketFactory () throws KeyManagementException, NoSuchAlgorithmException {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
internalSSLSocketFactory = context.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return internalSSLSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return internalSSLSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket());
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {
if(socket != null && (socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
}
return socket;
}
}
Keep in mind however that this may change behavior of your app in unexpected places (highly unlikely but never hurts to be cautious), In order to avoid that, you can revert back the Default SSLSocketFactory to old one after using your DefaultHttpDataSource
.
However, there is another more reliable solution.
You can use OkHttpDataSource
where you can pass an OkHttpClient
instance in the constructor. This OkHttpClient
instance can be configured to use our custom SSLSocketFactory
. It would look something like this:
okhttpclient.sslSocketFactory(new MyCustomSSLSocketFactory());
DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter,
new OkHttpDataSource(okHttpClient, userAgent, null, bandwidthMeter));
After some digging around, all I have to do is to enable the TLS1.2 in the application class using the ProviderInstaller. I've verified it with server serving video content that don't accept TLS1.0, and it worked.
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
try {
// Google Play will install latest OpenSSL
ProviderInstaller.installIfNeeded(getApplicationContext());
SSLContext sslContext;
sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
sslContext.createSSLEngine();
} catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException
| NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
}
}
Reference:
https://guides.codepath.com/android/Using-OkHttp#enabling-tls-v1-2-on-older-devices
User contributions licensed under CC BY-SA 3.0