Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

accept self-signed SSL Certificates-> where to set default TrustManager

First I had to admit that I know that accepting all certs can be considered as having no security. We have "real" Certs but only on our live systems. The certs on our test systems are self-signed. So as long we're developing, we have to use test-servers, which forces me to kind of disable certificates.

I saw a lot of toppics here on Stackoverflow and all over the web which are all trying do do the same: Accepting SSL Certificates. However None of these answers seem to apply to my Problem, as I'm not messing with HTTPSUrlConnections.

If I'm doing a request the code usually looks like this (commented for clearification):

//creates an HTTP-Post with an URL
HttpPost post = createBaseHttpPost();
//loads the request Data inside the httpPost
post.setEntity(getHttpPostEntity());
//appends some Headers like user-agend or Request UUIDs
appendHeaders(post);

HttpClient client = new DefaultHttpClient();
//mResponse is a custom Object which is returned 
//from the custom ResponseHandler(mResponseHandler) 
mResponse = client.execute(post, mResponseHandler);
return mResponse;

I read that I should inject my own TrustManager and X509HostnameVerivier. I created them like this:

private static final TrustManager[] TRUST_ALL_CERTS = new TrustManager[]{
        new X509TrustManager() {

            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[]{};
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
            }

            public void checkClientTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
            }
        }

    };

    private static X509HostnameVerifier ACCEPT_ALL_HOSTNAMES = 
            new X509HostnameVerifier() {

                public void verify(String host, String[] cns, String[] subjectAlts)
                        throws SSLException {
                }

                public void verify(String host, X509Certificate cert) throws SSLException {
                }

                public void verify(String host, SSLSocket ssl) throws IOException {
                }

                public boolean verify(String host, SSLSession session) {
                    return true;
                }
            };

If I inject the HostnameVerifier inside my request like this (client is DefaultHttpClient from above)

SSLSocketFactory ssl = (SSLSocketFactory)client.getConnectionManager().getSchemeRegistry().getScheme("https").getSocketFactory();
ssl.setHostnameVerifier(ACCEPT_ALL_HOSTNAMES);

the response turns from "hostname ** didn't match" to "Bad request". I guess I have to set the TrustManager, but I'm clueless where to set it inside my request, as I'm not using HttpsUrlConnections mentioned everywhere I looked it up.

like image 815
Rafael T Avatar asked Dec 17 '12 16:12

Rafael T


1 Answers

No, it doesn't force you to disable validation, it forces you to implement validation properly. Do not blindly accept all certificates. And no, your case is not any different, you just need to trust a certificate that Android doesn't trust by default.

You are using HttpClient, so the APIs for setting the trust manager are somewhat different than HttpsURLConnection, but the procedure is the same:

  1. Load a keystore file with trusted certificates (your server's self-signed certificates)
  2. Initialize a KeyStore with it.
  3. Create a SocketFactory using the KeyStore from 2.
  4. Set your HTTP client library to use it when creating SSL sockets.

This is described in Android's documentation: http://developer.android.com/reference/org/apache/http/conn/ssl/SSLSocketFactory.html

A more detailed article on the subject, shows how to create the trust store file: http://blog.crazybob.org/2010/02/android-trusting-ssl-certificates.html

Some background information and example code: http://nelenkov.blogspot.com/2011/12/using-custom-certificate-trust-store-on.html

This is the code you need to initialize HttpClient:

KeyStore localTrustStore = KeyStore.getInstance("BKS");
InputStream in = getResources().openRawResource(R.raw.mytruststore);
localTrustStore.load(in, TRUSTSTORE_PASSWORD.toCharArray());

SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory
                .getSocketFactory(), 80));
SSLSocketFactory sslSocketFactory = new SSLSocketFactory(localTrustStore);
schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));
HttpParams params = new BasicHttpParams();
ClientConnectionManager cm = 
    new ThreadSafeClientConnManager(params, schemeRegistry);

HttpClient client = new DefaultHttpClient(cm, params); 

At this point, you have no excuses for trusting all certificates. If you do, it all on you :)

like image 129
Nikolay Elenkov Avatar answered Sep 18 '22 23:09

Nikolay Elenkov