Dan
Dan

Reputation: 393

How to solve javax.net.ssl.SSLHandshakeException Error without Google Play Services installed?

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

Answers (1)

Dan
Dan

Reputation: 393

In my case it is Cipher Suites issue. To find out if it is your case also, you need:

  1. Run OpenSSL> s_client -connect YOUR_HOST_NAME.com:443
  2. Find the Certificate Authority CN = COMODO RSA Certification Authority
  3. On Android device go to 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.
  4. Find the Protocol and Cipher. In my case 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.

  5. Go to ssl labs and enter your host name YOUR_HOST_NAME.com into Hostname text field.
  6. After server IP will appear click on it
  7. Find Handshake Simulation section. In my case, Android 4.2 was not listed there.

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

Related Questions