如何为 Exoplayer enable/support TLS 1.1,1.2?
How to enable/support TLS 1.1,1.2 for Exoplayer?
错误:
SSL 握手中止:ssl=0x676a5680:SSL 库失败,通常是协议错误
根据这个 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 建议在这里提问。
"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;
}
}
但是请记住,这可能会在意想不到的地方改变您的应用程序的行为(极不可能,但谨慎一点也无妨),为了避免这种情况,您可以在使用后将默认的 SSLSocketFactory 恢复为旧的DefaultHttpDataSource
.
不过,还有一个更靠谱的方案。
您可以使用 OkHttpDataSource
,您可以在构造函数中传递 OkHttpClient
实例。这个 OkHttpClient
实例可以配置为使用我们的自定义 SSLSocketFactory
。它看起来像这样:
okhttpclient.sslSocketFactory(new MyCustomSSLSocketFactory());
DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter,
new OkHttpDataSource(okHttpClient, userAgent, null, bandwidthMeter));
经过一番挖掘,我所要做的就是使用 ProviderInstaller 在应用程序 class 中启用 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();
}
}
}
参考:
https://guides.codepath.com/android/Using-OkHttp#enabling-tls-v1-2-on-older-devices
错误: SSL 握手中止:ssl=0x676a5680:SSL 库失败,通常是协议错误
根据这个 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 建议在这里提问。
"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;
}
}
但是请记住,这可能会在意想不到的地方改变您的应用程序的行为(极不可能,但谨慎一点也无妨),为了避免这种情况,您可以在使用后将默认的 SSLSocketFactory 恢复为旧的DefaultHttpDataSource
.
不过,还有一个更靠谱的方案。
您可以使用 OkHttpDataSource
,您可以在构造函数中传递 OkHttpClient
实例。这个 OkHttpClient
实例可以配置为使用我们的自定义 SSLSocketFactory
。它看起来像这样:
okhttpclient.sslSocketFactory(new MyCustomSSLSocketFactory());
DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter,
new OkHttpDataSource(okHttpClient, userAgent, null, bandwidthMeter));
经过一番挖掘,我所要做的就是使用 ProviderInstaller 在应用程序 class 中启用 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();
}
}
}
参考:
https://guides.codepath.com/android/Using-OkHttp#enabling-tls-v1-2-on-older-devices