Saturday, July 25, 2015

Securing Google Cloud Endpoints API for Android App With HMAC

Google cloud endpoints can be secured by using Oauth protocol provided via Google accounts. Although that feature is provided by the Google cloud endpoints developers don't want to use Oauth sometimes. It's also possible to protect Google Cloud Endpoint APIs with the HMAC authentication. This blog post demonstrate how to protect APIs with HMAC. Example backend APIs have been written by using Java programming language.

Following implementation details will be more described in paragraphs below.
  • How to access HttpServletRequest instance within the API methods
  • How to calculate HMAC in java
  • How to read http authorization header by using  HttpServletRequest instance
  • How to set HMAC in authorization header by using Google Cloud Endpoint client library for Android

System Requirement

Android app send a name of a user. Server should reply with Hi, <name of user>. For example, if app send nipun it should reply with Hi, Nipun. Aforementioned requirement already implemented when Google Cloud Endpoint module is added with Android Studio. Codes from Reference [1] is on content of this post.

User Interface

Android client UI has a Button and a EditText. EditText has the id nameUiValue. nameUiValue has been assigned to
 
private EditText nameEditText;
 
When a user click on the button it calls the method.
 
public void sendName(View view)


Before securing the APIs Endpoint class and client AsyncTask class is show below.

Cloud


MyEndpoint.java

/*   For step-by-step instructions on connecting your Android application to this backend module,
   see "App Engine Java Endpoints Module" template documentation at 
   https://github.com/GoogleCloudPlatform/gradle-appengine-templates/tree/master/HelloEndpoints*/
package com.example.nipun.myapplication.backend;
import com.google.api.server.spi.config.Api; 
import com.google.api.server.spi.config.ApiMethod; 
import com.google.api.server.spi.config.ApiNamespace; 
 
import javax.inject.Named; 
import javax.servlet.http.HttpServletRequest;
/** * An endpoint class we are exposing */@Api(name = "myApi", version = "v1",
        namespace = @ApiNamespace(ownerDomain = "backend.myapplication.nipun.example.com",
                   ownerName = "backend.myapplication.nipun.example.com", packagePath = ""))
public class MyEndpoint {

    /**     * A simple endpoint method that takes a name and says Hi back     */

    @ApiMethod(name = "sayHi")
    public MyBean sayHi(HttpServletRequest request, @Named("name") String name) {
        MyBean response = new MyBean();        response.setData("Hi, " + name);
        return response;    }

}  

Android Client AsyncTask


class EndpointsAsyncTask extends AsyncTask<Pair<Context, String>, Void, String> {
    private MyApi myApiService = null;    private Context context;
    @Override    protected String doInBackground(Pair<Context, String>... params) {
        if(myApiService == null) {
            MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(), 
                new AndroidJsonFactory(), null)

                    .setRootUrl("http://10.0.2.2:8080/_ah/api/")
                    .setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
                        @Override
                        public void initialize(AbstractGoogleClientRequest<?> 
                                            abstractGoogleClientRequest) throws IOException {
                            abstractGoogleClientRequest.setDisableGZipContent(true);
                        }
                    });

            myApiService = builder.build();        }

        context = params[0].first;        String name = params[0].second;
        try {
            return myApiService.sayHi(name).execute().getData();        } catch (IOException e) {
            return e.getMessage();        }
    }

    @Override    protected void onPostExecute(String result) {
        Toast.makeText(context, result, Toast.LENGTH_LONG).show();    }
} 

Securing Google Cloud Endpoint With HMAC

How to access HttpServletRequest instance within the API methods


In order to get the HMAC value from http authorization header HttpServletRequest must be accessed from the API method. It can be done by using following way.

public MyBean sayHi(HttpServletRequest request, @Named("name") String name)

ApiMethod has included HttpServletRequest as an parameter.  

How to calculate HMAC in java


Hmac has been generated according to reference 3 and 4. A new hmac is calculated by using at the endpoint by using AuthManager.java class.

package com.example.nipun.myapplication.backend.auth;
import java.io.UnsupportedEncodingException;import java.security.InvalidKeyException;

import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;import javax.crypto.spec.SecretKeySpec;
/** * Created by nipun on 7/25/15. */public class AuthManager {
    private String KEY = "E34swDN1Fk1G44zmw9bL2N5B8R85w290";    private String ALGO = "HmacSHA1";
    public boolean authenticationSuccessful(String dataForHmac, String authHeader){

        if (authHeader == null)
            return false;
        String tempHmacParts[] = authHeader.split(" ");        if (tempHmacParts.length != 2)
            return false;
        tempHmacParts = tempHmacParts[1].split(":");
        if (tempHmacParts.length != 2)
            return false;
        if (tempHmacParts[1].equals(hmacDigest(dataForHmac, KEY, ALGO))){
            return true;        }else {
            return false;        }
    }

    /*    *    * This method is taken from 
    * http://www.supermind.org/blog/1102/generating-hmac-md5-sha1-sha256-etc-in-java 
    */
    private String hmacDigest(String msg, String keyString, String algo) {
        String digest = null;        try {
            SecretKeySpec key = new SecretKeySpec((keyString).getBytes("UTF-8"), algo);
            Mac mac = Mac.getInstance(algo);            mac.init(key);
            byte[] bytes = mac.doFinal(msg.getBytes("ASCII"));
            StringBuffer hash = new StringBuffer();
            for (int i = 0; i < bytes.length; i++) {
                String hex = Integer.toHexString(0xFF & bytes[i]);
                if (hex.length() == 1) {
                    hash.append('0');                }
                hash.append(hex);            }
            digest = hash.toString();        } catch (UnsupportedEncodingException e) {
        } catch (InvalidKeyException e) {
        } catch (NoSuchAlgorithmException e) {
        }
        return digest;    }


}

The method

private String hmacDigest(String msg, String keyString, String algo)

is used to calculate hmac. That method is used to check whether authentication is successful by using this method.

public boolean authenticationSuccessful(String dataForHmac, String authHeader)

Client has class HmacUtil.java to calculate hmac before send it to the server.

package com.blogspot.nipunswritings.hiname;
import java.io.UnsupportedEncodingException;import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;import javax.crypto.spec.SecretKeySpec;
/** * Created by nipun on 7/25/15. */

public class HmacUtil {

    private static String KEY = "E34swDN1Fk1G44zmw9bL2N5B8R85w290";
    private static String ALGO = "HmacSHA1";

    /*    *    * This method is taken from 
    * http://www.supermind.org/blog/1102/generating-hmac-md5-sha1-sha256-etc-in-java 
    * but parameters have change here 
    * */
     public static String hmacDigest(String msg) {
        String digest = null;        try {
            SecretKeySpec key = new SecretKeySpec((KEY).getBytes("UTF-8"), ALGO);
            Mac mac = Mac.getInstance(ALGO);
            mac.init(key);
            byte[] bytes = mac.doFinal(msg.getBytes("ASCII"));
            StringBuffer hash = new StringBuffer(); 
            for (int i = 0; i < bytes.length; i++) {
                String hex = Integer.toHexString(0xFF & bytes[i]); 
                if (hex.length() == 1) {
                    hash.append('0');                }
                hash.append(hex);            }
            digest = hash.toString();        } catch (UnsupportedEncodingException e) {
        } catch (InvalidKeyException e) {
        } catch (NoSuchAlgorithmException e) {
        }
        return digest;    }

}

Android client use below method to calculate hmac

public static String hmacDigest(String msg) 

How to read http authorization header by using  HttpServletRequest instance


@ApiMethod(name = "sayHi")
public MyBean sayHi(HttpServletRequest request, @Named("name") String name) throws 
                                                                        HmacMismatchException{

    String authHeader = request.getHeader("Authorization"); 
    AuthManager authManager = new AuthManager(); 
    String digestData = request.getMethod()+"+/myApi/v1/sayHi/"+name;

    if (!authManager.authenticationSuccessful(digestData, authHeader)){
        throw new HmacMismatchException();    }

    MyBean response = new MyBean();    response.setData("Hi, " + name);
    return response;}

It has added new parameter with the type HttpServletRequest and it has name request. Developers can use getHeader() to get http header. After getting Authorization header data for hmac generation is assigned to digestData. Then it is passed with Authorization header value to 

public boolean authenticationSuccessful(String dataForHmac, String authHeader) 

of AuthManager. If the authentication is not successful Endpoint send HmacMismatchException to client. HmacMismatchException.java is shown below.

package com.example.nipun.myapplication.backend.auth;
/** * Created by nipun on 7/25/15. */

public class HmacMismatchException extends Exception {

    public HmacMismatchException(){
        super("Can't produce received hmac");    }
}

How to set HMAC to authorization header by using Google Cloud Endpoint client library for Android


Android client in this blog post send a string,a name, to endpoint. Android user interface use EditText and Button to get user data. When button is clicked EndpointsAsyncTask is executed. It's provided name of the user.

Below method is called when a user touch the button.

public void sendName(View view){
    String name = nameEditText.getText().toString();
    new EndpointsAsyncTask().execute(new Pair<Context, String>(this, name));
}

Code for  EndpointsAsyncTask with http headers setting

class EndpointsAsyncTask extends AsyncTask<Pair<Context, String>, Void, String> {
    private MyApi myApiService = null;    private Context context;
    @Override    protected String doInBackground(Pair<Context, String>... params) {

        context = params[0].first;        final String name = params[0].second;
        if(myApiService == null) {  
            MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(), 
                new AndroidJsonFactory(), null)

                    .setRootUrl("http://10.0.2.2:8080/_ah/api/")
                    .setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
                        @Override
                        public void initialize(AbstractGoogleClientRequest<?> 
                                           abstractGoogleClientRequest) throws IOException {
                            abstractGoogleClientRequest.setDisableGZipContent(true); 
                            HttpHeaders httpHeaders = 
                                         abstractGoogleClientRequest.getRequestHeaders(); 
                            httpHeaders.setAuthorization("hmac "
                                +"public:"+HmacUtil.hmacDigest("POST+/myApi/v1/sayHi/"+name));
                            abstractGoogleClientRequest.setRequestHeaders(httpHeaders); 
                        }
                    });
            myApiService = builder.build();        }

        try {
            return myApiService.sayHi(name).execute().getData();
        } catch (IOException e) {
            return e.getMessage();        }
    }

    @Override    protected void onPostExecute(String result) {
        Toast.makeText(context, result, Toast.LENGTH_LONG).show();    }
}

Code to set Authorization header is this.

public void initialize(AbstractGoogleClientRequest<?> 
                                  abstractGoogleClientRequest) throws IOException {
    abstractGoogleClientRequest.setDisableGZipContent(true); 
    HttpHeaders httpHeaders = abstractGoogleClientRequest.getRequestHeaders();


    httpHeaders.setAuthorization("hmac " 
                      +"public:"+HmacUtil.hmacDigest("POST+/myApi/v1/sayHi/"+name));
    abstractGoogleClientRequest.setRequestHeaders(httpHeaders);} 

References

  1.  https://github.com/GoogleCloudPlatform/gradle-appengine-templates/tree/master/HelloEndpoints
  2. http://stackoverflow.com/questions/15056830/getting-raw-http-data-headers-cookies-etc-in-google-cloud-endpoints
  3. http://www.supermind.org/blog/1102/generating-hmac-md5-sha1-sha256-etc-in-java
  4. http://restcookbook.com/Basics/loggingin/

Tuesday, July 21, 2015

Include HttpHeaders to Google Cloud API call request made from Android

When making Cloud Endpoint API call request from the Android client sometimes it's needed to add Http Headers to that request. This blog post say how to add Date http header to request that calls myApi.sayHi() method which is described here. Read the code of EndpointsAsyncTask class. The code where http headers must be set is shown below.



MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(),
                    new AndroidJsonFactory(), null)
                // options for running against local devappserver
                // - 10.0.2.2 is localhost's IP address in Android emulator
                // - turn off compression when running against local devappserver
                .setRootUrl("http://10.0.2.2:8080/_ah/api/")
                .setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
                    @Override
                    public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
                        abstractGoogleClientRequest.setDisableGZipContent(true);
                    }
                });
                // end options for devappserver

            myApiService = builder.build();



Now add the Date http header as below. The bold text are the code that is used to add http header to request.



                MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(),
                        new AndroidJsonFactory(), null)
                        // options for running against local devappserver
                        // - 10.0.2.2 is localhost's IP address in Android emulator
                        // - turn off compression when running against local devappserver
                        .setRootUrl("http://10.0.2.2:8080/_ah/api/")
                        .setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
                            @Override
                            public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
                                abstractGoogleClientRequest.setDisableGZipContent(true);


                                Calendar calendar = Calendar.getInstance();
                                SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");

                                HttpHeaders headers = abstractGoogleClientRequest.getRequestHeaders();                                Log.d(TAG, "Date: "+dateFormat.format(calendar.getTime()));
                                headers.setDate(dateFormat.format(calendar.getTime()));
                                abstractGoogleClientRequest.setRequestHeaders(headers);
                            }
                        });

                myApiService = builder.build();

Sunday, June 21, 2015

Google Cloud Endpoints: Creating New Endpoint

This blog post is about using Google cloud endpoint and Adding new endpoint to existing backend project by using android studio. In order to learn more about Google cloud endpoints with Android use the references.

Integrate Cloud Endpoint Module To Project

  • File > New Module
  • Under Choose Module Type select Google Cloud Module and click Next
  • In next screen, select App Engine Java Endpoint Module for the Module type, give a Module Name and a Package name.
  • Click Finish
After Gradle sync finish new module can be seen with the name that has been given as Module Name. Google Cloud Endpoint module has example with MyBean.java, MyEndpoint.java classes. Select  Cloud endpoint module in run configuration and click run button. This endpoint can be seen in API Explorer.

After Android Studio Console display “INFO: Dev App Server is now running”.
  • Go to this http://localhost:8080/
  • Click on Google Cloud Endpoints API Explorer, It ll direct you to this link
    • https://apis-explorer.appspot.com/apis-explorer/?base=http%3A%2F%2Flocalhost%3A8080%2F_ah%2Fapi#p/
  • Above link is the address of API Explorer for the services that is running on the localhost
    • If services are not displayed click on the shield on Chromes address bar
    • It ll show a pop up with message “This page is trying to load scripts from unauthorized sources”, click on load unsafe scripts.
    • After that myApi API can be seen under services
  • API can be tested by clicking on the API.

Adding A New Endpoint

New endpoint is used to calculate sum of two floats. So This API need two java classes they are CalculatorEndpoint.java and ResultBean.Java. Both are under same package of Cloud Module.

ResultBean.java

package com.example.nipun.myapplication.backend;

/**
 * Created by nipun on 6/21/15.
 */
public class ResultBean {
    float result;

    public float getResult() {
        return result;
    }

    public void setResult(float result) {
        this.result = result;
    }
}

CalculatorEndpoint.java

package com.example.nipun.myapplication.backend;

/**
 * Created by nipun on 6/21/15.
 */

import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiNamespace;

import javax.inject.Named;

@Api(name = "calculatorApi",
        version = "v1",
        namespace = @ApiNamespace(
                ownerDomain = "backend.myapplication.nipun.example.com",
                ownerName = "backend.myapplication.nipun.example.com",
                packagePath = ""))
public class CalculatorEndpoint {


    @ApiMethod(name = "addTwoNumbers")
    public ResultBean addTwoNumbers(@Named("num1") float num1, @Named("num2") float num2){
        float result = num1 + num2;

        ResultBean resultBean = new ResultBean();
        resultBean.setResult(result);

        return resultBean;
    }


}

In order to use calculatorApi add fully qualified class name of  CalculatorEndpoint.java to the web.xml file of backend module as below. web.xml can be located in <backend_module_name>/src/main/webapp/WEB-INF/web.xml. backend_module_name is the name of the Google Cloud Module.

    <servlet>
        <servlet-name>SystemServiceServlet</servlet-name>
        <servlet-class>com.google.api.server.spi.SystemServiceServlet</servlet-class>
        <init-param>
            <param-name>services</param-name>
            <param-value>
                com.example.nipun.myapplication.backend.MyEndpoint,
                com.example.nipun.myapplication.backend.CalculatorEndpoint
            </param-value>
        </init-param>
    </servlet>

Run the cloud backend module again and go to API explorer. Now two numbers can be added with calculatorApi.

References

  • https://github.com/GoogleCloudPlatform/gradle-appengine-templates/tree/master/HelloEndpoints

Saturday, June 20, 2015

Rendering Problems: The following class could not found

Problem

In android studio, after a project was created a message was displayed with following content.

The following class could not found: android.support.v7.internal.widget.actionbaroverlaylayout


Solution

Buld > Rebuild Project

Go to build menu then rebuild the project.

Wednesday, April 22, 2015

NAND: could not write file Error

Error:

NAND: could not write file /tmp/android-nipun/emulator-kOjnli, File exists

Solution:

The hard-disk partition which has /tmp folder had not enough space when the Android virtual device (AVD) was started. Making free space that is more than needed by AVD resolve this error.