1

I am new to any networking related programming, so I know I am diving in deep, but I have problems of getting a successful SSL-Handshake between my android HttpsURLConnection and my local XAMPP hosted website, well rather just a php script that returns Hello World! Let me first say that I can load this page from my computer(host) and from my android phone's browser (https).

The problem:

When I try to connect application with my server through an HttpsURLConnection I get the following error:

System.err﹕ javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x5e522ba8: Failure in SSL library, usually a protocol error
System.err﹕ error:1407743E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert inappropriate fallback (external/openssl/ssl/s23_clnt.c:744 0x5e5967e8:0x00000000)
System.err﹕ at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:449)
System.err﹕ at com.android.okhttp.Connection.upgradeToTls(Connection.java:146)
System.err﹕ at com.android.okhttp.Connection.connect(Connection.java:107)
System.err﹕ at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:294)
System.err﹕ at com.android.okhttp.internal.http.HttpEngine.sendSocketRequest(HttpEngine.java:255)
System.err﹕ at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:206)
System.err﹕ at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:345)
System.err﹕ at com.android.okhttp.internal.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:89)
System.err﹕ at com.android.okhttp.internal.http.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:161)
System.err﹕ at yellowgames.battlenetfriendfinder.ServerConnection$TaskExecuter.doInBackground(ServerConnection.java:118)
System.err﹕ at yellowgames.battlenetfriendfinder.ServerConnection$TaskExecuter.doInBackground(ServerConnection.java:94)
System.err﹕ at android.os.AsyncTask$2.call(AsyncTask.java:288)
System.err﹕ at java.util.concurrent.FutureTask.run(FutureTask.java:237)
System.err﹕ at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
System.err﹕ at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
System.err﹕ at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
System.err﹕ at java.lang.Thread.run(Thread.java:841)
System.err﹕ Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x5e522ba8: Failure in SSL library, usually a protocol error
System.err﹕ error:1407743E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert inappropriate fallback (external/openssl/ssl/s23_clnt.c:744 0x5e5967e8:0x00000000)
System.err﹕ at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
System.err﹕ at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:406)
System.err﹕ ... 16 more 

My Code:

My code is based on this: https://developer.android.com/training/articles/security-ssl.html

//Get Cert
try {
   InputStream CertInputStream = a_sContext.getResources().openRawResource(R.raw.server);

   //Read Cert
   CertificateFactory CertFactory = CertificateFactory.getInstance("X.509");
   Certificate Cert = CertFactory.generateCertificate(CertInputStream);
   String Result = "Ca=" + ((X509Certificate) Cert).getSubjectDN();
   Log.d("test", Result); //This Returns correct Cert information

   //Create a keystore
   KeyStore MyKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
   MyKeyStore.load(null, null);
   MyKeyStore.setCertificateEntry("ca", Cert);

   //Create a TrustManager
   TrustManagerFactory TMF = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
   TMF.init(MyKeyStore);

   //Create a SSLContext
   m_pSSLContext = SSLContext.getInstance("TLS");
   m_pSSLContext.init(null, TMF.getTrustManagers(), null);
   HttpsURLConnection.setDefaultSSLSocketFactory(m_pSSLContext.getSocketFactory());

   //Try and connect to the website
   URL MyURL = new URL(m_sSecureHttp + m_sWebsite + m_sTestPath);
   con = (HttpsURLConnection) MyURL.openConnection();
   con.connect();
   Result = new String();
   BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));;
   while((Line = reader.readLine())!= null) {
        Result += Line;
   }
}
catch (Exception e) { e.printStackTrace(); }

Server Side:

I generated a Certificate following: http://robsnotebook.com/xampp-ssl-encrypt-passwords

This seems to have worked since my local computer and the android browser can access it. Also, in XAMPP's ssl_request.log I can see all my connection attempts from my local computer or through my android browser, yet it doesn't even once mention a request from my application. This is the same for access.log.

I am testing the android application on Android 4.4.2

My actual question

Does anyone know how I can fix the SSL handshake error? Any tips are useful! I tried a lot of things I could find on google, but none worked so far.

JelleFm
  • 25
  • 6
  • Also check: http://stackoverflow.com/questions/29916962/javax-net-ssl-sslhandshakeexception-javax-net-ssl-sslprotocolexception-ssl-han – Chepech Apr 07 '16 at 00:32

1 Answers1

0

There is a known bug on version Android 4.4.2 that is caused when accessing HTTPS using TLSv1.x protocol. Handshake fails because TLS is not supported so HttpsURLConnection falls back to SSLv3 protocol causing the error to happen on handshake. I haven't find a work around that doesn't involve reimplementing the HttpsURLConnection's TrustManagers to prevent the error or connecting insecurely.

This is what I did:

Feed a Custom trust manager to the your SSLContext:

// Trust manager that recognizes you acceptance criteria, that being ignoring handshake errors
        ResourceTrustManager trustManager = new ResourceTrustManager(sslKeyStores);
        TrustManager[] trustManagers = {trustManager};
        // use: org.apache.http.conn.ssl.AllowAllHostnameVerifier, this can be optional depending on your case.
        AllowAllHostnameVerifier resourceHostNameVerifier = new AllowAllHostnameVerifier(); 

        // Install the trust manager and host name verifier
        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, trustManagers, new java.security.SecureRandom());
            HttpsURLConnection
                    .setDefaultSSLSocketFactory(sc.getSocketFactory());
            HttpsURLConnection.setDefaultHostnameVerifier(resourceHostNameVerifier);
        } catch (Exception e) {
            Log.e(TAG, "Invalid algorithm used while setting trust store:" +e.getMessage());
            throw e;
        }

To implement your trust manager just overwrite:

  • public void checkClientTrusted
  • public void checkServerTrusted

Mine uses local keystores to try to authorize the cert:

public class ResourceTrustManager implements X509TrustManager {

private static final String TAG = ResourceTrustManager.class.getName();
protected ArrayList<X509TrustManager> x509TrustManagers = new ArrayList<X509TrustManager>();

public ResourceTrustManager(Collection<KeyStore> additionalkeyStores) {
    final List<TrustManagerFactory> factories = new ArrayList<TrustManagerFactory>();

    try {
        /**
         * Consolidates central and aditional keystore to be used as trust managers
         */
        final TrustManagerFactory original = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        original.init((KeyStore) null);
        factories.add(original);

        if(additionalkeyStores != null ) {
            for (KeyStore keyStore : additionalkeyStores) {
                final TrustManagerFactory additionalCerts = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                additionalCerts.init(keyStore);
                factories.add(additionalCerts);
            }
        }

    } catch (Exception e) {
        throw new RuntimeException(e);
    }

    for (TrustManagerFactory tmf : factories) {
        for (TrustManager tm : tmf.getTrustManagers()) {
            if (tm instanceof X509TrustManager) {
                x509TrustManagers.add((X509TrustManager) tm);
            }
        }
    }

    ResourceAssert.hasLength(x509TrustManagers, "Could not initialize with no trust managers");

}

public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    // The default Trustmanager with default keystore
    final X509TrustManager defaultX509TrustManager = x509TrustManagers.get(0);
    defaultX509TrustManager.checkClientTrusted(chain, authType);

}

public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    for( X509TrustManager tm : x509TrustManagers ) {
        try {
            tm.checkServerTrusted(chain,authType);
            return;
        } catch( CertificateException e ) {
            StringBuilder issuers = new StringBuilder();

            if(chain != null){
                for(X509Certificate cert :chain){
                    issuers.append( " " +cert.getIssuerDN().getName() );
                }
            }

            Log.e(TAG, "Untrusted host, connection is not secure "+ issuers + "\n\n" + e.getMessage());
        }
    }

}

public X509Certificate[] getAcceptedIssuers() {
    final ArrayList<X509Certificate> list = new ArrayList<X509Certificate>();

    for( X509TrustManager tm : x509TrustManagers ) {
        list.addAll(Arrays.asList(tm.getAcceptedIssuers()));
    }

    return list.toArray(new X509Certificate[list.size()]);
}


}

This is not elegant at all, but gets the job done.

Chepech
  • 5,258
  • 4
  • 47
  • 70