1

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.

s-hunter
  • 24,172
  • 16
  • 88
  • 130
  • First it would be nice to have MVCE so we could try it out. From what I have checked the issue is similar to OkHttp in that you need to patch Exoplayer silmilar way the OkHttp was in the github post you have linked - https://github.com/square/okhttp/issues/2372#issuecomment-244807676 – tukan Oct 30 '19 at 09:31

2 Answers2

4

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));
Zohaib Amir
  • 3,482
  • 2
  • 11
  • 29
  • Verified this worked by setting `HttpsURLConnection.setDefaultSSLSocketFactory(new MyCustomSSLSocketFactory());` first thing in the onCreate() in the Application class. However, I used ProviderInstaller instead as it is less code, at a higher level and no need to mess with the socket factory. – s-hunter Nov 06 '19 at 16:58
  • uh.. where does bandwidthMeter come from? – chitgoks Dec 18 '20 at 11:13
  • I guess it has something to do with media playback. You can see it's in ExoPlayer by calling `new ExoPlayer.Builder(this).setBandwidthMeter()` – Adrian Bartyczak Nov 17 '22 at 03:57
3

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:

Javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: Failure in SSL library, usually a protocol error

https://guides.codepath.com/android/Using-OkHttp#enabling-tls-v1-2-on-older-devices

s-hunter
  • 24,172
  • 16
  • 88
  • 130