Calling AWS API – Signature, Authentication Header – using OkHTTP in android

I am trying to sign a HTTP request to AWS API in android using OkHTTP. I have used code from this Question. The documentation of AWS was not at all helpful. I am very confused. This is the only code I have found so far which is easy to understand and implement. But it is also not working for my problem. I am not passing any data with API its just simple API which I have to call and receive a message.

My Code

AWSCredentials credentials =  new BasicAWSCredentials(access_key_id.trim(), secret_access_key.trim());
String API_GATEWAY_SERVICE_NAME = "execute-api";

Request requestAws = new DefaultRequest(API_GATEWAY_SERVICE_NAME);
URI uri = URI.create(url);
requestAws.setEndpoint(uri);
requestAws.setResourcePath(url);
requestAws.setHttpMethod(HttpMethodName.POST);

AWS4Signer signer = new AWS4Signer();
signer.setServiceName(API_GATEWAY_SERVICE_NAME);
signer.setRegionName("us-east-2");
signer.sign(requestAws, credentials);

OkHttpClient httpClient = new OkHttpClient();
Map<String, String> headers = requestAws.getHeaders();
List<String> key = new ArrayList<String>();
List<String> value = new ArrayList<String>();

for (Map.Entry<String, String> entry : headers.entrySet()) {
   key.add(entry.getKey());
   value.add(entry.getValue());
}

okhttp3.Request request = new okhttp3.Request.Builder()
.url(url)
.addHeader(key.get(0), value.get(0))
.addHeader(key.get(1), value.get(1))
.addHeader(key.get(2), value.get(2))
.build();

Response response = httpClient.newCall(request).execute();

Error I get

{"message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.nnThe Canonical String for this request should have beenn'GETn/**2nd last part of url ***/**last part of url ***/nnhost:***.execute-api.us-east-2.amazonaws.comnx-amz-date:20210306T082609Znnhost;x-amz-date***signature***'nnThe String-to-Sign should have beenn'AWS4-HMAC-SHA256n20210306T082609Zn20210306/us-east-2/execute-api/aws4_requestn***signature***'n"}

Its a simple code, but I cant find any issue. Server error is also not helpful.

From server side i am not using any content-type thats why i have not passed this in header. can this be an issue? I also think there is some issue with my requesAws.

May be there is issue in this line. I dont know what exactly this mean so I have passed same API url in it as in referred code.

requestAws.setResourcePath(url);

Answer

I don’t recommend writing your own SigV4 signer. Instead, try pulling one is as a library dependency.

Try babbel’s OkHttp signer:

dependencies {
    implementation 'com.github.babbel:okhttp-aws-signer:1.0.1'
}
val signer = OkHttpAwsV4Signer("us-east-1", "execute-api")

val client = OkHttpClient.Builder()
    .addInterceptor { chain ->
        val original = chain.request()
        val signed = signer.sign(original, accessKeyId, accessKey)
        chain.proceed(signed)
    }
    .build()

Or Ghedeon’s:

repositories {
    maven {
        url "http://dl.bintray.com/ghedeon/maven"
    }
}

...

dependencies {
    implementation 'com.ghedeon:aws-interceptor:0.6'
}
val interceptor = AwsInterceptor(credentialsProvider, serviceName, region)
 
val okHttpClient = new OkHttpClient.Builder()
    .addInterceptor(interceptor)
    .build()

The latter uses the AWS Android SDK under the hood. You could supply AWSMobileClient.getInstance() for the credentialsProvider argument, if you’re using Amazon Cognito.

val okHttpClient = new OkHttpClient.Builder()
    .addInterceptor(AwsInterceptor(
        AWSMobileClient.getInstance(), "execute-api", "us-east-1"
    ))
    .build()

Leave a Reply

Your email address will not be published. Required fields are marked *