14

I have code here that allows me to connect to https servers and transfer data. It works just fine, but I want to know if I'm doing it the right way and I'm actually making a secure connection. Please check my work. Thanks.

public class HTTPSClient extends DefaultHttpClient
{

    public HTTPSClient() 
    {
    }

    @Override
    protected ClientConnectionManager createClientConnectionManager()
    {
        SchemeRegistry registry = new SchemeRegistry();

        HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
        final SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
        socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
        //socketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", socketFactory, 80));
        registry.register(new Scheme("https", socketFactory, 443));
        HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, "UTF-8");

        return new SingleClientConnManager(params, registry);
    }
}

I use this code like so:

HttpResponse response = mConnection.httpsClient.execute(new HttpHost("www.somehostname.com", 80), new HttpGet("https://someaddress")));

I then read the response from there. Thanks again.

RyanM
  • 5,680
  • 9
  • 45
  • 55

2 Answers2

7

Look at the official Custom SSL context tutorial from Apache HttpClient.

As Stephen C mentioned, you don't need to register port 80 for the https context. Register it instead for http (if neccessary at all). This means, when you call a https url, the appropriate socketFactory, as you specified, will be used.

NOTE: You will receive in most cases a "Certificate not trusted" or similar exception when you connect from Android devices to sites with custom certificates or certificates from not very well known issuers. If this is the case, you need to create a custom certificate store for your application, so that it will trust your server certificates. If you want to know hot to achieve this, you can look at my blog article

If you want to check, if your device is really communicating via a secured connection, you could make the call to the https endpoint from your android emulator and capture the traffic with Wireshark on your developer machine.

Hope this helps

saxos
  • 2,467
  • 1
  • 20
  • 21
  • Hi saxos. I followed your instructions and I still get "Not trusted server certificate" error followed by "TrustAnchor for CertPath not found." error followed by "java.security.cert.CertPathValidatorException: TrustAnchor for CertPath not found." error. I have a valid keystore, it's importing, but these errors occur. Thanks – RyanM Mar 30 '11 at 18:30
  • Hi Did you import all the required certificates in the correct order? The innermost Intermediate CA certificate first, the Root CA at last. Don't import the webserver certificate at all. Note: If your webserver is providing the certificate chain in the wrong order (for example first entry is Root CA, second entry the intermediate CA), you will still receive "Not trusted" errors. If this is the case, change the order on the webserver or look here: http://stackoverflow.com/questions/4115101/apache-httpclient-on-android-producing-certpathvalidatorexception-issuername/4199518#4199518 – saxos Mar 31 '11 at 15:41
  • Hi. I ended up just using a urlconnection to do my ssl communication. The certificates used by my server are well known and thus I didn't actually need to do any of this. urlconnection handles it for me. Thanks. – RyanM Mar 31 '11 at 23:29
4

I'm suspicious of this:

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

Particularly the 2nd line. Why would you register port 80 for the "https" scheme?

This is either harmless / redundant, or you are going to send "https" requests to port 80.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216