Reputation: 393
I am working on radio app using Exoplayer. In case of this podcast the Exoplayer throws SSLHandshakeException.
To fix this I tried:
1. Update security provider with Provider installer by calling ProviderInstaller.installIfNeededAsync
.
public class MainActivity extends Activity
implements ProviderInstaller.ProviderInstallListener {
private static final int ERROR_DIALOG_REQUEST_CODE = 1;
private boolean mRetryProviderInstall;
//Update the security provider when the activity is created.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ProviderInstaller.installIfNeededAsync(this, this);
}
/**
* This method is only called if the provider is successfully
updated (or is already up-to-date).
*/
@Override
protected void onProviderInstalled() {
// Provider is up-to-date, app can make secure network calls.
}
/**
* This method is called if updating fails; the error code indicates
* whether the error is recoverable.
*/
@Override
protected void onProviderInstallFailed(int errorCode, Intent recoveryIntent) {
GoogleApiAvailability availability = GoogleApiAvailability.getInstance();
if (availability.isUserRecoverableError(errorCode)) {
// Recoverable error. Show a dialog prompting the user to
// install/update/enable Google Play services.
availability.showErrorDialogFragment(
this,
errorCode,
ERROR_DIALOG_REQUEST_CODE,
new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
// The user chose not to take the recovery action
onProviderInstallerNotAvailable();
}
});
} else {
// Google Play services is not available.
onProviderInstallerNotAvailable();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ERROR_DIALOG_REQUEST_CODE) {
// Adding a fragment via GoogleApiAvailability.showErrorDialogFragment
// before the instance state is restored throws an error. So instead,
// set a flag here, which will cause the fragment to delay until
// onPostResume.
mRetryProviderInstall = true;
}
}
/**
* On resume, check to see if we flagged that we need to reinstall the provider.
*/
@Override
protected void onPostResume() {
super.onPostResume();
if (mRetryProviderInstall) {
// We can now safely retry installation.
ProviderInstaller.installIfNeededAsync(this, this);
}
mRetryProviderInstall = false;
}
private void onProviderInstallerNotAvailable() {
// This is reached if the provider cannot be updated for some reason.
// App should consider all HTTP communication to be vulnerable,
// and take appropriate action.
}
}
2. Replace https to http
In this case, Exoplayer throws an Exception with error code 308 (redirection exception).
3. Use NetCipher instead of HttpUrlConnection by calling HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl)
like described here
4. Use NoSSLv3SocketFactory like described here
5. Disable SSL certificate Checking by calling
private static void disableSSLCertificateChecking() {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
// Not implemented
}
@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
// Not implemented
}
} };
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
6. Create customTrust
To get certificates I run the open ssl command:
OpenSSL> s_client -connect simplecast.com:443
7. Download the .crt file from here and as Google suggests run
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(new
FileInputStream("my_downloaded.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// Tell the URLConnection to use a SocketFactory from our SSLContext
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
8. Enable TLSv1.2 only using a code from here
The device that I use for testing is Samsung SM-t111 Android 4.2.2. It has google play services installed, but I am trying to fix the issue without using Google Play services because the client's device does not have it.
Please suggest what should be the working solution?
Upvotes: 1
Views: 2278
Reputation: 393
In my case it is Cipher Suites issue. To find out if it is your case also, you need:
OpenSSL> s_client -connect YOUR_HOST_NAME.com:443
CN = COMODO RSA Certification Authority
Settings > Security > Trusted credentials > System
and check if certificate authority is listed as trusted certificate. COMODO is well known certificate authority so it was listed there, which means that the problem is not in certificate.Protocol : TLSv1.2
and Cipher : ECDHE-RSA-AES256-GCM-SHA384
Cipher suite is a combination of cryptographic algorithms used by a given SSL connection. During the negotiation process, the server and client must agree on a cipher suite that is available in both environments. Client sends a list of supported ciphers to the server, the server picks one, and encryption starts. If there is no such suite in common, no SSL connection can be established, and no data can be exchanged.
YOUR_HOST_NAME.com
into Hostname text field.To understand why Android 4.2 is not listed there, I found the list of all Cipher Suites supported by Android 4.2. I compared server's Cipher Suites with Android 4.2 and I did not find common Cipher Suites between them. I search for TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 in official list of cipher suites supported by Android system and they say that for this Cipher Suite supported from API level 20+ (it is actually API level 21 - Android 5.0 cause API level 20 is KITKAT_WATCH).
In my case the issue it cannot be resolved on a client side without an appropriate server configuration. 2. Using HTTP instead of HTTPS is not possible, cause podcast uses Proxy server, because of that we do not have the final link in the podcast. If just change HTTPS to HTTP of the link before redirects - exoplayer enters to the indefinite loop trying to open connection. 3. On devices without Google Play we cannot update the list of supported Cipher Suites.
If Cipher suite issue on Android device without Google Play services can be resolved on the client side and you know how please add answer bellow or live a comment.
Upvotes: 3