android - 如何为 Exoplayer 启用/支持 TLS 1.1、1.2?

标签 android tls1.2 exoplayer

错误: SSL 握手中止:ssl=0x676a5680:SSL 库失败,通常是协议(protocol)错误

根据这个Android docTLS 1.11.2 在 API 16+ 上受支持,但在 API 20+ 之前默认不启用。我找到了一些解决方案( herehereherehere )来为 OkHttp 启用 TLS 1.1 和 1.2 支持。如何为 Exoplayer 启用 TLS 1.1/1.2 支持?我找到的关于 Exoplayer TLS 1.1/1.2 支持的唯一帖子来自这个 github issue建议改为在这里提问。

"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 使用 HttpsURLConnection,它有一个用于默认 SSLSocketFactory 的静态字段。 HttpsURLConnection 的所有新实例都将分配此默认 SSLSocketFactory,除非在实例上调用了 setSSLSocketFactory()。所以从技术上讲,如果您在 实例化 DefaultHttpsDataSource 之前调用设置默认 SSLSocketFactory,它应该可以工作:

HttpsURLConnection.setDefaultSSLSocketFactory(new MyCustomSSLSocketFactory());

MyCustomSSLSocketFactoy 可能看起来像这样:

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;
    }
}

但是请记住,这可能会在意想不到的地方改变您的应用程序的行为(极不可能,但谨慎一点也无妨),为了避免这种情况,您可以在使用 DefaultHttpDataSource

然而,还有另一种更可靠的解决方案。 您可以使用 OkHttpDataSource,您可以在构造函数中传递一个 OkHttpClient 实例。这个 OkHttpClient 实例可以配置为使用我们自定义的 SSLSocketFactory。它看起来像这样:

okhttpclient.sslSocketFactory(new MyCustomSSLSocketFactory());
DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter,
    new OkHttpDataSource(okHttpClient, userAgent, null, bandwidthMeter));

关于android - 如何为 Exoplayer 启用/支持 TLS 1.1、1.2?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58456778/

相关文章:

java - onTouchListener 一个ImageView需要点击两次才能起作用,另一个只需点击一次

android - 模块与包 Android Studio

Android:ExoPlayer - 流 mp3

android - Exoplayer 和 ProgressBar

java - Exo Player是否可以动态播放mp3文件的范围而不进行剪辑?

Android - 从另一台设备访问 Google Drive appdata 文件夹,但使用相同的应用程序

android - 带有 jackson 转换器的 Retrofit 2.0

ssl - SSL证书中的sct列表是什么?

java - Java Apache httpclient调用https服务是否需要Server证书?

paypal - WinHTTP.WinHTTPRequest.5.1 在 TLS 1.2 之后不适用于 PayPal 沙箱