SSL Issue with Android

7

I have a very specific SSL issue on my Android. If I try to visit a particular website via code, I get the following error:

SSL handshake failure: Failure in SSL library, usually a protocol error
error:140773F2:SSL routines:SSL23_GET_SERVER_HELLO: sslv3 alert unexpected message (external/openssl/ssl/s23_cInt.c:500 0xaf076228:0x00000000)

I get this regardless of the build... I've tried it on API levels 1.5, 1.6, 2.2, and 4.0 and gotten the same result each time.

After some troubleshooting I tried to visit the website through a browser and I got the following error:

Data connectivity problem
A secure connection could not be established.

Here's the thing, though... the website opens just fine on Windows browsers (tested on Firefox, IE, and Chrome). It also opens just fine on iOS devices which uses the same webkit as the Android, which is odd. The website also works without problems on the Opera Mini browser.

Here's the website.

I've tried workarounds by adding the client certificate to the keystore and by ignoring invalid client certificates, with no results. However it appears that the certificate itself is not the issue.

I'm at an impasse. Can anybody provide any guidance on how I can get this to work?

android
ssl
openssl
asked on Stack Overflow Jun 21, 2012 by Don Quixote

4 Answers

4

I found the solution (thank you Nikolay for pointing me in the right direction).

The problem was two-fold... one, it was returning a site certificate that Android didn't like, and two, it had only SSLv3 (and not TLS) enabled.

Here was my solution. First I had to create a custom socket factory class:

public class MySSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("SSLv3");

public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
    super(truststore);

    TrustManager tm = new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    };

    sslContext.init(null, new TrustManager[] { tm }, null);
}

@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
    SSLSocket S = (SSLSocket) sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    S.setEnabledProtocols(new String[] {"SSLv3"});
    return S;
}

@Override
public Socket createSocket() throws IOException {
    SSLSocket S = (SSLSocket) sslContext.getSocketFactory().createSocket();
    S.setEnabledProtocols(new String[] {"SSLv3"});
    return S;
}

}

Second, I had this custom HttpClient defined in my code:

public HttpClient getNewHttpClient() {
    try {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);

        MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
        sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", sf, 443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

        return new DefaultHttpClient(ccm, params);
    } catch (Exception e) {
        return new DefaultHttpClient();
    }
}

Third, I called the custom HttpClient and parsed the results:

public String test(String URIString) {
    HttpClient httpClient = getNewHttpClient();
    String result = "";
    URI uri;
    try {
        uri = new URI(URIString);
    } catch (URISyntaxException e1) {
        return "ERROR";
    }
    HttpHost host = new HttpHost(uri.getHost(), 443, uri.getScheme());
    HttpPost httppost = new HttpPost(uri.getPath());
    try {
        HttpResponse response = httpClient.execute(host, httppost);
        BufferedReader reader = new BufferedReader(
                new InputStreamReader(
                    response.getEntity().getContent()
                )
            );
            String line = null;
            while ((line = reader.readLine()) != null){
              result += line + "\n";
            }

            return result;
    } catch (ClientProtocolException e) {
        return "ERROR";
    } catch (IOException e) {
        return "ERROR";
    }
}
answered on Stack Overflow Jun 25, 2012 by Don Quixote
3

How are you accessing this site? Through the Android browser? WebView? Or HttpClient/HTTPSURLConnection? It seems it only responds to SSL3, you have to force your client to use it.

answered on Stack Overflow Jun 22, 2012 by Nikolay Elenkov • edited Aug 1, 2012 by Bart
0

After struggling for two days this final solution worked for me-

    private OkHttpClient.Builder getUnsafeOkHttpClient() {
    try {
        // Create a trust manager that does not validate certificate chains
        final TrustManager[] trustAllCerts = new X509TrustManager[]{new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }
            public X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[]{};
            }
        }};

        // Install the all-trusting trust manager
        final SSLContext sslContext = SSLContext.getInstance("SSL");
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        kmf.init(keyStore, "password".toCharArray());
        sslContext.init(kmf.getKeyManagers(), trustAllCerts, new java.security.SecureRandom());
        final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
        builder.hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });
        ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.COMPATIBLE_TLS)
                .allEnabledTlsVersions()
                .allEnabledCipherSuites()
                .build();
        ConnectionSpec spec1 = new ConnectionSpec.Builder(ConnectionSpec.CLEARTEXT)
                .build();

        builder.connectionSpecs(Arrays.asList(spec,spec1));

        return builder;
    } catch (Exception e) {
        Logger.e("[DownloadService][getUnsafeOkHttpClient] :", false);
       return new OkHttpClient.Builder();

    }
}
answered on Stack Overflow Mar 17, 2020 by Shivam Yadav
-2

Use this and call this method HttpsTrustManager.allowAllSSL()

It will solves the problem and its working fine for me.

public class HttpsTrustManager implements X509TrustManager {

private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};

@Override 
public void checkClientTrusted( 
        java.security.cert.X509Certificate[] x509Certificates, String s)
        throws java.security.cert.CertificateException {

} 

@Override 
public void checkServerTrusted( 
        java.security.cert.X509Certificate[] x509Certificates, String s)
        throws java.security.cert.CertificateException {

} 

public boolean isClientTrusted(X509Certificate[] chain) {
    return true; 
} 

public boolean isServerTrusted(X509Certificate[] chain) { 
    return true; 
} 

@Override 
public X509Certificate[] getAcceptedIssuers() {
    return _AcceptedIssuers; 
} 

public static void allowAllSSL() { 
    HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {

        @Override 
        public boolean verify(String arg0, SSLSession arg1) {
            return true; 
        } 

    }); 

    SSLContext context = null;
    if (trustManagers == null) {
        trustManagers = new TrustManager[]{new HttpsTrustManager()};
    } 

    try { 
        context = SSLContext.getInstance("TLS");
        context.init(null, trustManagers, new SecureRandom());
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (KeyManagementException e) {
        e.printStackTrace();
    } 

    HttpsURLConnection.setDefaultSSLSocketFactory(context
            .getSocketFactory());
} 

}

answered on Stack Overflow Mar 11, 2016 by Darshan S S

User contributions licensed under CC BY-SA 3.0