【问题标题】:How to enable/support TLS 1.1,1.2 for Exoplayer?如何为 Exoplayer 启用/支持 TLS 1.1、1.2?
【发布时间】:2020-02-15 19:12:56
【问题描述】:

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

根据Android doc,API 16+ 支持 TLS 1.1 和 1.2,但在 API 20+ 之前默认不启用。我找到了一些解决方案(here、here、here 和 here)来启用对 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:源错误。 com.google.android.exoplayer2.upstream.HttpDataSource$HttpDataSourceException: 无法连接到https://liveStream/LIVE-0089000D05/manifest.mpd 在 com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:194) 在 com.google.android.exoplayer2.upstream.DefaultDataSource.open(DefaultDataSource.java:147) 在 com.google.android.exoplayer2.upstream.DataSourceInputStream.checkOpened(DataSourceInputStream.java:102) 在 com.google.android.exoplayer2.upstream.DataSourceInputStream.open(DataSourceInputStream.java:65) 在 com.google.android.exoplayer2.upstream.ParsingLoadable.load(ParsingLoadable.java:129) 在 com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:308) 在 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) 在 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) 在 java.lang.Thread.run(Thread.java:841) 引起: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException:SSL 握手中止: ssl=0x722c3af8:SSL 库失败,通常是协议错误 错误:14077410:SSL 例程:SSL23_GET_SERVER_HELLO:sslv3 警报 握手失败(外部/openssl/ssl/s23_clnt.c:744 0x689d8f10:0x00000000)"

这仅在 API 21 版本(棒棒糖)以下发生。服务器是 使用 TLS1.2 协议,在 Lollipop 以下的 Android 上不支持 版本。

【问题讨论】:

标签: android tls1.2 exoplayer


【解决方案1】:

DefaultHttpDataSource 使用HttpsURLConnection,它有一个默认的静态字段SSLSocketFactory。除非在实例上调用 setSSLSocketFactory(),否则所有 HttpsURLConnection 的新实例都将分配此默认 SSLSocketFactory。所以从技术上讲,如果你调用 set default SSLSocketFactory before 实例化DefaultHttpsDataSource,它应该可以工作:

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

但是请记住,这可能会改变您的应用程序在意想不到的地方的行为(极不可能,但要小心谨慎),为了避免这种情况,您可以在使用 @987654332 后将默认 SSLSocketFactory 恢复为旧的@。

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

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

【讨论】:

  • 通过在 Application 类的 onCreate() 中设置 HttpsURLConnection.setDefaultSSLSocketFactory(new MyCustomSSLSocketFactory()); 第一件事来验证这是否有效。但是,我使用了 ProviderInstaller,因为它的代码更少,级别更高,并且不需要弄乱套接字工厂。
  • 呃..bandwidthMeter从何而来?
【解决方案2】:

经过一番挖掘,我所要做的就是使用ProviderInstaller 在应用程序类中启用 TLS1.2。我已经用不接受 TLS1.0 的服务器提供视频内容验证了它,它工作正常。

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

参考:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-17
    • 2019-04-29
    • 1970-01-01
    • 1970-01-01
    • 2018-08-23
    • 1970-01-01
    相关资源
    最近更新 更多