0

I am developing an android application and want to connect to a REST service using URLConnection. The resource is protected using Digest Authentication. I can successfully access the REST service via the browser. I do not want to use HttpClient because URLConnection is the more current way to connect for the future.

But when I try to access the resource via the code below, I get a 401 Error with an java.io.FileNotFoundException. I have researched this thoroughly but no success, solutions appreciated.

Note: My REST service has been developed using Grails, I am running this code in the android emulator with code developed in Eclipse on the windows 64 bit OS.

CODE

URL myurl = new URL("http://10.0.2.2:8080/MyREST/customers");

HttpURLConnection myurlConnection = (HttpURLConnection) myurl.openConnection();

String basicAuth =  "Basic " + (Base64.encode(userpass.getBytes(),android.util.Base64.DEFAULT)).toString();
myurlConnection.setRequestProperty ("Authorization", basicAuth);

try {
    int responseCode1 = ((HttpURLConnection) myurlConnection).getResponseCode();
    Log.i("MyLongOperation", "Check connection" +Integer.toString(responseCode1) );

    InputStream in = new BufferedInputStream(myurlConnection.getInputStream());
    readStream(in);
}
finally {
   myurlConnection.disconnect();
  }     

I have also tried setting authentication at a global level with no effect

Authenticator.setDefault(new Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication(username, password.toCharArray());                                    
        }
    }
   );

I have referred to this article - with no success. Connecting to remote URL which requires authentication using Java

Community
  • 1
  • 1
user1811107
  • 729
  • 3
  • 18
  • 39

2 Answers2

2

If the resource is protected by "Digest" then sending a "Basic" authorization scheme in your code will not work because the server would not recognize it.

Secondly, by using a "preemptive" authentication, setting the Authorization header w/o it being requested is kind of a security hole. You will be sending information to the server that it has not requested.

Thirdly, the "Authenticator.setDefault" most likely will not be requested as there was some significant back-and-forth caused by MicroSoft's implementation of HTTP digest authentication (YMMV may vary on my recollection of this). As such, Sun/Oracle decided to leave this behavior disabled by default as per this document.

That said, you may be better off looking into utilizing the Apache HTTP client bundled with Android to do this. There is a bundled implementation Digest Authentication included. There is an example of "preemptive" digest authentication located here.

Couple of caveats to be aware of:

  • Pay CLOSE attention to the "HttpHost" stored in "target" - this MUST MATCH EXACTLY the host name, protocol port, and protocol scheme used in the URL being retrieved.
  • The example provided is for HTTP Client 4.2.x. I am not 100% sure of the version included in Android but you should be able to locate working examples.

Update Submitter has provided additional comments with regard to the statement that it is recommended by Google to use the HttpURLConnection with articles here and here.

While I trust the statements made by Tim Bray with regard to the reasoning as to why you should be using the provided HttpURLConnection object for performing these calls, I do not agree that they should be immediately accepted on face value.

There is no indication as to the level of support of digest authentication provided by the implementation in Android. As I mentioned earlier, the HttpURLConnection does not support immediately as it has been known to be buggy.

If you are decided that you are going to use HTTP Digest Authentication, regardless of the fact that it has been deemed unstable by the majority of the community, I would attempt to set the following system properties in your application as EARLY as possible during the Android lifecycle:

  • http.auth.digest.validateServer="true"
  • http.auth.digest.validateProxy="true"

By doing so, this should enable the digest authentication scheme.

I am, again, going to re-iterate that the Apache HTTP Client bundled with Android was developed and designed specifically to address short-comings of the basic Java HttpURLConnection, providing much a much broader and robust client for dealing with HTTP(s) based data streams.

I would recommend trying a couple of things as well, see if you can configure your container to provide "Basic" authentication protection. The other, more complex option, would be to possibly provide X.509 Certificate Based authentication.

I hope that this clarification helps you get to your goal.

Dave G
  • 9,639
  • 36
  • 41
  • Thanks for the information, I do use the Apache Http client for my other projects to connect etc. successfully - what I was looking for is to use HttpURLConnection since it is the one that Google is recommending to use going forward. Having said that, It seems that I may be stuck with the Htttp client. – user1811107 Mar 16 '14 at 02:48
  • Can you clarify where you saw Google's recommendation on this? The point of Apache HTTP Client was to address significant short comings and functionality not provided by the standard HTTPUrlConnection. Specifically this particular case of digest authentication, I recall years ago that while it was to be the next generation auth mech it fell apart in the implementations. This is part of the reason why it is not enabled by default in the JVM. – Dave G Mar 16 '14 at 05:01
  • There are a couple of places. http://android-developers.blogspot.com/2011/09/androids-http-clients.html OR https://developer.android.com/training/basics/network-ops/connecting.html. Quote from android docs - "We recommend using HttpURLConnection for applications targeted at Gingerbread and higher" – user1811107 Mar 16 '14 at 21:04
  • At this point, I will have to be content with AndroidHttpClient, thanks for pointing me in the right direction. – user1811107 Apr 09 '14 at 02:26
0

change 'Base64.encode ...' to 'Base64.encodeToString ...'

gilad.chap
  • 31
  • 1
  • 2