I am a salesforce developer, our company are planning to extend the service for global users, so we decided to use google translate to improve our customers' experience.
I have read the google api document, however, we met an issue when requesting GCP access token, the tutorial google provided in document which supports different languages, like java, c#, python etc. Since we are suing apex (a type of salesforce platform script), we weren't able to use your library to get GCP token.
Instead, we also checked "OAuth 2.0 for Server Accounts", unfortunately, neither worked from me.
Is there any suggestions?
I got token by below code, and the error I met was caused by special characters encode.
public with sharing class SwitchLanguageByGoogleAPIController {
private static final String ENDPOINT = '**';
private static final String TOKEN_ENDPOINT = 'https://accounts.google.com/o/oauth2/token';
private static final String SCOPE = 'https://www.googleapis.com/auth/cloud-platform';
private static final String PROJECT_ID = '**';
private static final String GLOSSARY_ID = '88';
private static final String LOCATION_ID = 'us-central1';
private static final String CLIENT_SECRET = '**';
private static final String PRIVATE_KEY = '**';
private static final String CLIENT_EMAIL = '**';
public static void translateByGlossary() {
Token token = getToken();
HttpRequest request = new HttpRequest();
request.setHeader('Content-Type', 'text/plain');
request.setEndpoint(ENDPOINT + PROJECT_ID + '/locations/' + LOCATION_ID + ':translateText?access_token=' + token.access_token);
request.setMethod('POST');
String contents = 'Personal Information, Middle Name e, first First Name';
String sourceLanguageCode = 'en';
String targetLanguageCode = 'zh';
request.setBody('{"sourceLanguageCode":"' + sourceLanguageCode + '","targetLanguageCode":"' + targetLanguageCode + '","contents":"'+ contents +'","glossaryConfig":{"glossary":"projects/' + PROJECT_ID +'/locations/' + LOCATION_ID + '/glossaries/' + GLOSSARY_ID + '"}}');
HTTP http = new HTTP();
HttpResponse reponse = http.send(request);
System.debug(reponse.getBody());
}
private static Token getToken() {
Http http = new Http();
HttpRequest req = new HttpRequest();
HttpResponse res = new HttpResponse();
//Making the call out
req.setEndpoint(TOKEN_ENDPOINT);
req.setMethod('POST');
req.setHeader('Content-Type','application/x-www-form-urlencoded');
string URLEncodedGrantType = encodingUtil.urlEncode('urn:ietf:params:oauth:grant-type:jwt-bearer','UTF-8');
string jwtSigned = generateJWT();
req.setBody('grant_type='+URLEncodedGrantType+'&assertion='+jwtSigned);
res = http.send(req);
system.debug('Response : '+res.getBody());
return (Token)JSON.deserialize(res.getBody(), Token.Class);
}
private static String generateJWT() {
Http http = new Http();
HttpRequest req = new HttpRequest();
HttpResponse res = new HttpResponse();
String JWTHeader = '{"typ":"JWT","alg":"RS256"}';
String Base64EncodedJWTHeader = EncodingUtil.base64Encode(Blob.valueOf(JWTHeader));
long issued_at = datetime.now().getTime()/1000;
long expires_at = datetime.now().addHours(1).getTime()/1000;
JWTClaimSet claimSet = new JWTClaimSet();
claimSet.iss = CLIENT_EMAIL;
claimSet.scope = SCOPE;
claimSet.aud = TOKEN_ENDPOINT;
claimSet.iat = issued_at;
claimSet.exp = expires_at;
String strClaimSetJSON = JSON.Serialize(claimSet);
String Base64EncodedClaimset = EncodingUtil.base64Encode(Blob.valueOf(strClaimSetJSON));
system.debug('Base64 Encoded Claimset::'+Base64EncodedClaimset);
Base64EncodedClaimset = PerformPostBase64Encode(Base64EncodedClaimset);
system.debug('persorm post Claimset::'+Base64EncodedClaimset);
string Base64EncodedString = Base64EncodedJWTHeader + '.' + Base64EncodedClaimset;
String algorithmName = 'RSA-SHA256';
Blob privateKey = EncodingUtil.base64Decode(PRIVATE_KEY);
Blob input = Blob.valueOf(Base64EncodedString);
Blob Blobsign = Crypto.sign(algorithmName, input , privateKey);
String base64EncodedSignature = EncodingUtil.base64Encode(Blobsign);
base64EncodedSignature = PerformPostBase64Encode(base64EncodedSignature);
system.debug('Base 64 encoded signature ::'+base64EncodedSignature );
system.debug('Encoded assertion : ' + Base64EncodedString+'.'+base64EncodedSignature);
string URLEncodedUTF8Assertion = encodingUtil.urlEncode(Base64EncodedString+'.'+base64EncodedSignature,'UTF-8');
return URLEncodedUTF8Assertion;
}
public static String PerformPostBase64Encode(String s)
{
s = s.Replace('+', '-');
s = s.Replace('/', '_');
s = s.Split('=')[0];
return s;
}
public class JWTClaimSet
{
public string iss {get;set;}
public string scope {get;set;}
public string aud {get;set;}
public Long exp {get;set;}
public Long iat {get;set;}
}
private class Token{
private String access_token;
private String token_type;
private String expires_in;
}
}
We are trying to call AWS API Gateway from C# Windows Service, for a background job. Which was supposed to trigger API Gateway periodically initialize request?
We used RestSharp to invoke API Endpoint, the class called AwsAuthenticator , which is inherited from RestSharp.Authenticators.IAuthenticator.
But when we invoke API Gateway we received with error as
"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."
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
namespace ConsoleApp3
{
public class AwsAuthenticator : RestSharp.Authenticators.IAuthenticator
{
public string AccessKeyId { get; }
public string AccessKeySecret { get; }
public string Region { get; }
public AwsAuthenticator(string accessKeyId, string accessKeySecret, string region)
{
AccessKeyId = accessKeyId;
AccessKeySecret = accessKeySecret;
Region = region;
}
private static HashSet<string> ignoredHeaders = new HashSet<string>() {
"authorization",
"content-length",
"content-type",
"user-agent"
};
public void Authenticate(RestSharp.IRestClient client, RestSharp.IRestRequest request)
{
DateTime signingDate = DateTime.UtcNow;
SetContentMd5(request);
SetContentSha256(request);
SetHostHeader(request, client);
SetDateHeader(request, signingDate);
SortedDictionary<string, string> headersToSign = GetHeadersToSign(request);
string signedHeaders = GetSignedHeaders(headersToSign);
string canonicalRequest = GetCanonicalRequest(client, request, headersToSign);
byte[] canonicalRequestBytes = System.Text.Encoding.UTF8.GetBytes(canonicalRequest);
string canonicalRequestHash = BytesToHex(ComputeSha256(canonicalRequestBytes));
string stringToSign = GetStringToSign(Region, signingDate, canonicalRequestHash);
byte[] signingKey = GenerateSigningKey(Region, signingDate);
byte[] stringToSignBytes = System.Text.Encoding.UTF8.GetBytes(stringToSign);
byte[] signatureBytes = SignHmac(signingKey, stringToSignBytes);
string signature = BytesToHex(signatureBytes);
string authorization = GetAuthorizationHeader(signedHeaders, signature, signingDate, Region);
request.AddHeader("Authorization", authorization);
}
public string GetCredentialString(DateTime signingDate, string region)
{
return AccessKeyId + "/" + GetScope(region, signingDate);
}
private string GetAuthorizationHeader(string signedHeaders, string signature, DateTime signingDate, string region)
{
return "AWS4-HMAC-SHA256 Credential=" + this.AccessKeyId + "/" + GetScope(region, signingDate) +
", SignedHeaders=" + signedHeaders + ", Signature=" + signature;
}
private string GetSignedHeaders(SortedDictionary<string, string> headersToSign)
{
return string.Join(";", headersToSign.Keys);
}
private byte[] GenerateSigningKey(string region, DateTime signingDate)
{
byte[] formattedDateBytes = System.Text.Encoding.UTF8.GetBytes(signingDate.ToString("yyyMMdd"));
byte[] formattedKeyBytes = System.Text.Encoding.UTF8.GetBytes("AWS4" + this.AccessKeySecret);
byte[] dateKey = SignHmac(formattedKeyBytes, formattedDateBytes);
byte[] regionBytes = System.Text.Encoding.UTF8.GetBytes(region);
byte[] dateRegionKey = SignHmac(dateKey, regionBytes);
byte[] serviceBytes = System.Text.Encoding.UTF8.GetBytes("execute-api");
byte[] dateRegionServiceKey = SignHmac(dateRegionKey, serviceBytes);
byte[] requestBytes = System.Text.Encoding.UTF8.GetBytes("aws4_request");
return SignHmac(dateRegionServiceKey, requestBytes);
}
private byte[] SignHmac(byte[] key, byte[] content)
{
HMACSHA256 hmac = new HMACSHA256(key);
hmac.Initialize();
return hmac.ComputeHash(content);
}
private string GetStringToSign(string region, DateTime signingDate, string canonicalRequestHash)
{
return "AWS4-HMAC-SHA256\n" +
signingDate.ToString("yyyyMMddTHHmmssZ") + "\n" +
GetScope(region, signingDate) + "\n" +
canonicalRequestHash;
}
private string GetScope(string region, DateTime signingDate)
{
string formattedDate = signingDate.ToString("yyyyMMdd");
return formattedDate + "/" + region + "/execute-api/aws4_request";
}
private byte[] ComputeSha256(byte[] body)
{
SHA256 sha256 = SHA256.Create();
return sha256.ComputeHash(body);
}
private string BytesToHex(byte[] checkSum)
{
return BitConverter.ToString(checkSum).Replace("-", string.Empty).ToLower();
}
public string PresignPostSignature(string region, DateTime signingDate, string policyBase64)
{
byte[] signingKey = this.GenerateSigningKey(region, signingDate);
byte[] stringToSignBytes = System.Text.Encoding.UTF8.GetBytes(policyBase64);
byte[] signatureBytes = SignHmac(signingKey, stringToSignBytes);
string signature = BytesToHex(signatureBytes);
return signature;
}
public string PresignURL(RestSharp.IRestClient client, RestSharp.IRestRequest request, int expires)
{
DateTime signingDate = DateTime.UtcNow;
string requestQuery = "";
string path = request.Resource;
requestQuery = "X-Amz-Algorithm=AWS4-HMAC-SHA256&";
requestQuery += "X-Amz-Credential="
+ this.AccessKeyId
+ Uri.EscapeDataString("/" + GetScope(Region, signingDate))
+ "&";
requestQuery += "X-Amz-Date="
+ signingDate.ToString("yyyyMMddTHHmmssZ")
+ "&";
requestQuery += "X-Amz-Expires="
+ expires
+ "&";
requestQuery += "X-Amz-SignedHeaders=host";
string canonicalRequest = GetPresignCanonicalRequest(client, request, requestQuery);
byte[] canonicalRequestBytes = System.Text.Encoding.UTF8.GetBytes(canonicalRequest);
string canonicalRequestHash = BytesToHex(ComputeSha256(canonicalRequestBytes));
string stringToSign = GetStringToSign(Region, signingDate, canonicalRequestHash);
byte[] signingKey = GenerateSigningKey(Region, signingDate);
byte[] stringToSignBytes = System.Text.Encoding.UTF8.GetBytes(stringToSign);
byte[] signatureBytes = SignHmac(signingKey, stringToSignBytes);
string signature = BytesToHex(signatureBytes);
// Return presigned url.
return client.BaseUrl + path + "?" + requestQuery + "&X-Amz-Signature=" + signature;
}
private string GetPresignCanonicalRequest(RestSharp.IRestClient client, RestSharp.IRestRequest request, string requestQuery)
{
LinkedList<string> canonicalStringList = new LinkedList<string>();
canonicalStringList.AddLast(request.Method.ToString());
string path = request.Resource;
if (!path.StartsWith("/"))
{
path = "/" + path;
}
canonicalStringList.AddLast(path);
canonicalStringList.AddLast(requestQuery);
canonicalStringList.AddLast("host:" + client.BaseUrl.Host);
canonicalStringList.AddLast("");
canonicalStringList.AddLast("host");
canonicalStringList.AddLast("UNSIGNED-PAYLOAD");
return string.Join("\n", canonicalStringList);
}
private string GetCanonicalRequest(RestSharp.IRestClient client, RestSharp.IRestRequest request,
SortedDictionary<string, string> headersToSign)
{
LinkedList<string> canonicalStringList = new LinkedList<string>();
canonicalStringList.AddLast(request.Method.ToString());
string[] path = request.Resource.Split(new char[] { '?' }, 2);
if (!path[0].StartsWith("/"))
{
path[0] = "/" + path[0];
}
canonicalStringList.AddLast(path[0]);
string query = "";
if (path.Length == 2)
{
var parameterString = path[1];
var parameterList = parameterString.Split('&');
SortedSet<string> sortedQueries = new SortedSet<string>();
foreach (string individualParameterString in parameterList)
{
if (individualParameterString.Contains('='))
{
string[] splitQuery = individualParameterString.Split(new char[] { '=' }, 2);
sortedQueries.Add(splitQuery[0] + "=" + splitQuery[1]);
}
else
{
sortedQueries.Add(individualParameterString + "=");
}
}
query = string.Join("&", sortedQueries);
}
canonicalStringList.AddLast(query);
foreach (string header in headersToSign.Keys)
{
canonicalStringList.AddLast(header + ":" + headersToSign[header]);
}
canonicalStringList.AddLast("");
canonicalStringList.AddLast(string.Join(";", headersToSign.Keys));
if (headersToSign.Keys.Contains("x-amz-content-sha256"))
{
canonicalStringList.AddLast(headersToSign["x-amz-content-sha256"]);
}
else
{
canonicalStringList.AddLast("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
}
return string.Join("\n", canonicalStringList);
}
private SortedDictionary<string, string> GetHeadersToSign(RestSharp.IRestRequest request)
{
var headers = request.Parameters.Where(p => p.Type.Equals(RestSharp.ParameterType.HttpHeader)).ToList();
SortedDictionary<string, string> sortedHeaders = new SortedDictionary<string, string>();
foreach (var header in headers)
{
string headerName = header.Name.ToLower();
string headerValue = header.Value.ToString();
if (!ignoredHeaders.Contains(headerName))
{
sortedHeaders.Add(headerName, headerValue);
}
}
return sortedHeaders;
}
private void SetDateHeader(RestSharp.IRestRequest request, DateTime signingDate)
{
request.AddHeader("x-amz-date", signingDate.ToString("yyyyMMddTHHmmssZ"));
}
private void SetHostHeader(RestSharp.IRestRequest request, RestSharp.IRestClient client)
{
request.AddHeader("Host", client.BaseUrl.Host + (client.BaseUrl.Port != 80 ? ":" + client.BaseUrl.Port : string.Empty));
}
private void SetContentSha256(RestSharp.IRestRequest request)
{
if (request.Method == RestSharp.Method.PUT || request.Method.Equals(RestSharp.Method.POST))
{
var bodyParameter = request.Parameters.Where(p => p.Type.Equals(RestSharp.ParameterType.RequestBody)).FirstOrDefault();
if (bodyParameter == null)
{
request.AddHeader("x-amz-content-sha256", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
return;
}
byte[] body = null;
if (bodyParameter.Value is string)
{
body = System.Text.Encoding.UTF8.GetBytes(bodyParameter.Value as string);
}
if (bodyParameter.Value is byte[])
{
body = bodyParameter.Value as byte[];
}
if (body == null)
{
body = new byte[0];
}
SHA256 sha256 = System.Security.Cryptography.SHA256.Create();
byte[] hash = sha256.ComputeHash(body);
string hex = BitConverter.ToString(hash).Replace("-", string.Empty).ToLower();
request.AddHeader("x-amz-content-sha256", hex);
}
else
{
request.AddHeader("x-amz-content-sha256", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
}
}
private void SetContentMd5(RestSharp.IRestRequest request)
{
if (request.Method == RestSharp.Method.PUT || request.Method.Equals(RestSharp.Method.POST))
{
var bodyParameter = request.Parameters.Where(p => p.Type.Equals(RestSharp.ParameterType.RequestBody)).FirstOrDefault();
if (bodyParameter == null)
{
return;
}
byte[] body = null;
if (bodyParameter.Value is string)
{
body = System.Text.Encoding.UTF8.GetBytes(bodyParameter.Value as string);
}
if (bodyParameter.Value is byte[])
{
body = bodyParameter.Value as byte[];
}
if (body == null)
{
body = new byte[0];
}
MD5 md5 = MD5.Create();
byte[] hash = md5.ComputeHash(body);
string base64 = Convert.ToBase64String(hash);
request.AddHeader("Content-MD5", base64);
}
}
}
////////////////////////
public class MainClass
{
public void Execute()
{
var client = new RestClient("https://nm47849kod.execute-api.ap-southeast1.amazonaws.com/samplegateway/");
var request = new RestRequest("/", Method.POST);
var postData = new { Mode = 4 };
request.AddParameter("application/json",JsonConvert.SerializeObject(postData),ParameterType.RequestBody); AwsAuthenticator awsAuthenticator = new AwsAuthenticator("AccessKeyXXXXX", "SECKEYxxxx12313123123123123", "apsoutheast-1");
awsAuthenticator.Authenticate(client,request);
IRestResponse response = client.Execute(request);
var content = response.Content; // raw content as string
Console.WriteLine(content);
Console.ReadLine();
}
}
Error Details:
{"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.\n\nThe Canonical String for this request should have been\n'POST\n/samplegateway/\n\ncontent-md5:rkT7BbUvFInBgrPCuA0UZw==\nhost:nm47849kod.execute-api.ap-southeast-1.amazonaws.com\nx-amz-content-sha256:0318f62547c9078687e73f987ec26fa557047b67f54bb99b8047c950990ae42c\nx-amz-date:20190601T102835Z\n\ncontent-md5;host;x-amz-content-sha256;x-amz-date\n0318f62547c9078687e73f987ec26fa557047b67f54bb99b8047c950990ae42c'\n\nThe String-to-Sign should have been\n'AWS4-HMAC-SHA256\n20190601T102835Z\n20190601/ap-southeast-1/execute-api/aws4_request\n8f89bd5010655fb26a8de5e29d48d6129ac7875e5eb6bc2faeb8e41864b4d49e'\n"}.
We identified the problem.
Below is the working code and this resolves my issue. I am sharing this so that the group can get benefitted. The above class is entirely rewritten and when invoked it worked.
public class ApiRequest
{
private const string ServiceName = "execute-api";
private const string Algorithm = "AWS4-HMAC-SHA256";
private const string ContentType = "application/json";
private const string SignedHeaders = "content-type;host;x-amz-date;x-api-key";
private const string DateTimeFormat = "yyyyMMddTHHmmssZ";
private const string DateFormat = "yyyyMMdd";
public AwsApiGatewayRequest AwsApiGatewayRequest;
public ApiRequest(AwsApiGatewayRequest request)
{
AwsApiGatewayRequest = request;
if (string.IsNullOrEmpty(AwsApiGatewayRequest.RequestMethod))
AwsApiGatewayRequest.RequestMethod = "POST";
if (string.IsNullOrEmpty(AwsApiGatewayRequest.xApiKey))
AwsApiGatewayRequest.xApiKey = "";
}
public WebResponse GetPostResponse()
{
var request = GetPostRequest();
return request.GetResponse();
}
public WebRequest GetPostRequest()
{
string hashedRequestPayload = CreateRequestPayload(AwsApiGatewayRequest.JsonData);
string authorization = Sign(hashedRequestPayload, AwsApiGatewayRequest.RequestMethod, AwsApiGatewayRequest.AbsolutePath, AwsApiGatewayRequest.QueryString);
string requestDate = DateTime.UtcNow.ToString(DateTimeFormat);
var webRequest = WebRequest.Create($"https://{AwsApiGatewayRequest.Host}{AwsApiGatewayRequest.AbsolutePath}");
webRequest.Timeout = AwsApiGatewayRequest.RequestTimeout.HasValue ? AwsApiGatewayRequest.RequestTimeout.Value : 50000;
webRequest.Method = AwsApiGatewayRequest.RequestMethod;
webRequest.ContentType = ContentType;
webRequest.Headers.Add("X-Amz-date", requestDate);
webRequest.Headers.Add("Authorization", authorization);
webRequest.Headers.Add("x-amz-content-sha256", hashedRequestPayload);
if (!string.IsNullOrEmpty(AwsApiGatewayRequest.AdditionalHeaders))
{
// parse apart and apply the additional headers
string[] headers = AwsApiGatewayRequest.AdditionalHeaders.Split(';');
foreach (string header in headers)
{
var headervalue = header.Split('=');
if (headervalue.Count() == 2)
webRequest.Headers.Add(headervalue[0], headervalue[1]);
}
}
if (!string.IsNullOrEmpty(AwsApiGatewayRequest.SessionToken))
webRequest.Headers.Add("X-Amz-Security-Token", AwsApiGatewayRequest.SessionToken);
webRequest.ContentLength = AwsApiGatewayRequest.JsonData.Length;
var encoding = new ASCIIEncoding();
var data = encoding.GetBytes(AwsApiGatewayRequest.JsonData);
using (var newStream = webRequest.GetRequestStream())
{
newStream.Write(data, 0, data.Length);
newStream.Close();
}
return webRequest;
}
private string CreateRequestPayload(string jsonString)
{
return HexEncode(Hash(ToBytes(jsonString)));
}
private string Sign(string hashedRequestPayload, string requestMethod, string canonicalUri, string canonicalQueryString)
{
var currentDateTime = DateTime.UtcNow;
var dateStamp = currentDateTime.ToString(DateFormat);
var requestDate = currentDateTime.ToString(DateTimeFormat);
var credentialScope = $"{dateStamp}/{AwsApiGatewayRequest.RegionName}/{ServiceName}/aws4_request";
var headers = new SortedDictionary<string, string> {
{ "content-type", ContentType },
{ "host", AwsApiGatewayRequest.Host },
{ "x-amz-date", requestDate },
{ "x-api-key", AwsApiGatewayRequest.xApiKey }
};
var canonicalHeaders = string.Join("\n", headers.Select(x => x.Key.ToLowerInvariant() + ":" + x.Value.Trim())) + "\n";
// Task 1: Create a Canonical Request For Signature Version 4
var canonicalRequest = $"{requestMethod}\n{canonicalUri}\n{canonicalQueryString}\n{canonicalHeaders}\n{SignedHeaders}\n{hashedRequestPayload}";
var hashedCanonicalRequest = HexEncode(Hash(ToBytes(canonicalRequest)));
// Task 2: Create a String to Sign for Signature Version 4
var stringToSign = $"{Algorithm}\n{requestDate}\n{credentialScope}\n{hashedCanonicalRequest}";
// Task 3: Calculate the AWS Signature Version 4
var signingKey = GetSignatureKey(AwsApiGatewayRequest.SecretKey, dateStamp, AwsApiGatewayRequest.RegionName, ServiceName);
var signature = HexEncode(HmacSha256(stringToSign, signingKey));
// Task 4: Prepare a signed request
// Authorization: algorithm Credential=access key ID/credential scope, SignedHeadaers=SignedHeaders, Signature=signature
var authorization = $"{Algorithm} Credential={AwsApiGatewayRequest.AccessKey}/{dateStamp}/{AwsApiGatewayRequest.RegionName}/{ServiceName}/aws4_request, SignedHeaders={SignedHeaders}, Signature={signature}";
return authorization;
}
private byte[] GetSignatureKey(string key, string dateStamp, string regionName, string serviceName)
{
var kDate = HmacSha256(dateStamp, ToBytes("AWS4" + key));
var kRegion = HmacSha256(regionName, kDate);
var kService = HmacSha256(serviceName, kRegion);
return HmacSha256("aws4_request", kService);
}
private byte[] ToBytes(string str)
{
return Encoding.UTF8.GetBytes(str.ToCharArray());
}
private string HexEncode(byte[] bytes)
{
return BitConverter.ToString(bytes).Replace("-", string.Empty).ToLowerInvariant();
}
private byte[] Hash(byte[] bytes)
{
return SHA256.Create().ComputeHash(bytes);
}
private byte[] HmacSha256(string data, byte[] key)
{
return new HMACSHA256(key).ComputeHash(ToBytes(data));
}
}
Execution Parameter:
var request = new AwsApiGatewayRequest()
{
RegionName = "",
Host = ,
AccessKey = "",
SecretKey = "",
RequestMethod = "POST",
AbsolutePath = ,
JsonData = "{\"Mode\":\"4\"}",
SessionToken = ""
};//Invoke this using RestClient...
The problem here is we failed to add an additional header which was required by AWS. In this version, we have added hence it rectified.
Thanks for your support.
How to send a proper http request from android using volly for a post REST URL created with using aws api gateway?
How to make a proper http request from android so that these values accept by api gateway.
Api gateway internal testing works properly.
This is the parameter need to pass through the request.
{
"id": "22",
"res": "10",
"rate": "75",
"index": "1"
}
//this method is used call http request
public static void newPost2(Context context){
String awsAccessKey = "awsAccessKey";
final String awsSecretKey = "awsSecretKey";
String dateStr = ClientSSLSocketFactory.getServerTime();
String scope = dateStr + "/us-east-1/execute-api/aws4_request,";
String headerNames = "host;x-amz-date";
final String credentialsAuthorizationHeader = "Credential=" + awsAccessKey + "/" + scope;
String signedHeadersAuthorizationHeader = "SignedHeaders=" + headerNames;
String signatureAuthorizationHeader = "Signature=" + "hex encoded signature";
final String authorization = "AWS4-HMAC-SHA256 "
+ credentialsAuthorizationHeader + ", "
+ signedHeadersAuthorizationHeader + ", "
+ signatureAuthorizationHeader;
final String url="https://xxxxxxx2.execute-api.us-east-1.amazoxxxxxxxxxxxxxxxx";
RequestQueue queue = Volley.newRequestQueue(context);
StringRequest sr = new StringRequest(Request.Method.POST,url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
// mPostCommentResponse.requestCompleted();
System.out.print("\n\n\n......post ok."+response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// mPostCommentResponse.requestEndedWithError(error);
System.out.print("\n\n\n.......post error."+error);
error.printStackTrace();
}
}){
#Override
protected Map<String,String> getParams(){
Map<String,String> params = new HashMap<String, String>();
// params.put("Content-Type", "application/json; charset=utf-8");
params.put("id","22");
params.put("rate","10");
params.put("res", "75");
params.put("index","1");
return params;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String,String> params = new HashMap<String, String>();
// params.put("Content-Type","application/x-www-form-urlencoded");
params.put("Host", "apigateway.us-east-1.amazonaws.com");
params.put("x-amz-date", ClientSSLSocketFactory.getawsnettime());
params.put("Authorization", authorization);
params.put("Content-Type", "application/x-amz-json-1.0");
return params;
}
};
queue.add(sr);
}
but shows error in log
...stringr..header......post error.com.android.volley.AuthFailureError
11-30 14:52:14.140 4663-4663/com.example.acernrc.noveleven W/System.err: com.android.volley.ServerError
11-30 14:52:14.140 4663-4663/com.example.acernrc.noveleven W/System.err: at com.android.volley.toolbox.BasicNetwork.performRequest(BasicNetwork.java:163)
I am having trouble fetching result from my amazon elastic search cluster using the amazon java SDK and an IAm user credential. Now the issue is that when the PATH string is equal to "/" then I am able to fetch the result correctly but when I try with a different path for e.g "/private-search" then I get a 403 forbidden error. Even when for the path that has public access I am getting a 403 forbidden error for this IAm user but it works if I remove "signer.sign(requestToSign, credentials);" line in performSigningSteps method(for public resource only).
My policy in AWS gives this IAM user access to everything in my elastic search service. And also what can I do to avoid hard-coding the access key and secret key in source code?
private static final String SERVICE_NAME = "es";
private static final String REGION = "region-name";
private static final String HOST = "host-name";
private static final String ENDPOINT_ROOT = "http://" + HOST;
private static final String PATH = "/private-search";
private static final String ENDPOINT = ENDPOINT_ROOT + PATH;
private static String accessKey = "IAmUserAccesskey"
private static String secretKey = "IAmUserSecretkey"
public static void main(String[] args) {
// Generate the request
Request<?> request = generateRequest();
// Perform Signature Version 4 signing
performSigningSteps(request);
// Send the request to the server
sendRequest(request);
}
private static Request<?> generateRequest() {
Request<?> request = new DefaultRequest<Void>(SERVICE_NAME);
request.setContent(new ByteArrayInputStream("".getBytes()));
request.setEndpoint(URI.create(ENDPOINT));
request.setHttpMethod(HttpMethodName.GET);
return request;
}
private static void performSigningSteps(Request<?> requestToSign) {
AWS4Signer signer = new AWS4Signer();
signer.setServiceName(requestToSign.getServiceName());
signer.setRegionName(REGION);
AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
signer.sign(requestToSign, credentials);
}
private static void sendRequest(Request<?> request) {
ExecutionContext context = new ExecutionContext();
ClientConfiguration clientConfiguration = new ClientConfiguration();
AmazonHttpClient client = new AmazonHttpClient(clientConfiguration);
MyHttpResponseHandler<Void> responseHandler = new MyHttpResponseHandler<Void>();
MyErrorHandler errorHandler = new MyErrorHandler();
Void response = client.execute(request, responseHandler, errorHandler, context);
}
public static class MyHttpResponseHandler<T> implements HttpResponseHandler<AmazonWebServiceResponse<T>> {
#Override
public AmazonWebServiceResponse<T> handle(com.amazonaws.http.HttpResponse response) throws Exception {
InputStream responseStream = response.getContent();
String responseString = convertStreamToString(responseStream);
System.out.println(responseString);
AmazonWebServiceResponse<T> awsResponse = new AmazonWebServiceResponse<T>();
return awsResponse;
}
#Override
public boolean needsConnectionLeftOpen() {
return false;
}
}
public static class MyErrorHandler implements HttpResponseHandler<AmazonServiceException> {
#Override
public AmazonServiceException handle(com.amazonaws.http.HttpResponse response) throws Exception {
System.out.println("In exception handler!");
AmazonServiceException ase = new AmazonServiceException("exception.");
ase.setStatusCode(response.getStatusCode());
ase.setErrorCode(response.getStatusText());
return ase;
}
#Override
public boolean needsConnectionLeftOpen() {
return false;
}
}
public static String convertStreamToString(InputStream is) throws IOException {
// To convert the InputStream to String we use the
// Reader.read(char[] buffer) method. We iterate until the
// Reader return -1 which means there's no more data to
// read. We use the StringWriter class to produce the string.
if (is != null) {
Writer writer = new StringWriter();
char[] buffer = new char[1024];
try {
Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
}
finally {
is.close();
}
return writer.toString();
}
return "";
}
Facebook requires a public url to POST to in order to deauthorize your app for a user.
I am behind a firewall and do not wish to test this on my production environment, therefore has anyone managed to mimic this POST in a c# (or other) integration test?
It would involve building up a form with a field signed_request and encrypted to base64 and the posting it to my local web app to handle...
Here is how I do it in PHP:
<?php
function generateSignedRequest($data, $secret) {
$payload = base64UrlEncode(json_encode($data));
$signature = base64UrlEncode(hash_hmac('sha256', $payload, $secret, $raw = true));
return "$signature.$payload";
}
function base64UrlEncode($input) {
$input = rtrim(base64_encode($input), '=');
return strtr($input, '+/', '-_');
}
$app_secret = "YOUR_APP_SECRET_HERE";
$data = array(
'algorithm' => 'HMAC-SHA256',
'issued_at' => 1332162396,
'user' => array(
'country' => 'bg',
'locale' => 'en_US'
),
'user_id' => '100003387159594'
);
$signed_request = generateSignedRequest($data, $app_secret);
?>
<form action="deauthorize_callback.php" method="post">
<input type="text" name="signed_request" value="<?php echo $signed_request?>" size="255" />
<input type="submit" value="Send Request" />
</form>
If you have PHP in your local environment you can try submiting the form to your backend and see if it works. You just have to replace your app secret and user id in the data array.
Hope this helps, it worked for me so far.
I managed to find something similar for building the payload in Ruby which I've converted, here it is with an integration test:
This class builds the string that you will POST as request.form["signed_request"]
public class SignedRequest
{
public static string SignedRequestFor(string applicationSecret, string json)
{
// encode payload
var payload = ToUrlBase64String(json);
// create sig
var Hmac = SignWithHmac(UTF8Encoding.UTF8.GetBytes(payload), UTF8Encoding.UTF8.GetBytes(applicationSecret));
var HmacBase64 = ToUrlBase64String(Hmac);
// concat and return
return HmacBase64.Concatenate(".", payload);
}
private static byte[] SignWithHmac(byte[] dataToSign, byte[] keyBody)
{
using (var hmacAlgorithm = new HMACSHA256(keyBody))
{
hmacAlgorithm.ComputeHash(dataToSign);
return hmacAlgorithm.Hash;
}
}
private static string ToUrlBase64String(string input)
{
byte[] encodedBytes = Encoding.UTF8.GetBytes(input);
return ToUrlBase64String(encodedBytes);
}
private static string ToUrlBase64String(byte[] input)
{
return Convert.ToBase64String(input).Replace("=", String.Empty)
.Replace('+', '-')
.Replace('/', '_');
}
}
Small value object used by the test:
public class RequestEnvelope
{
public long user_id { get; set; }
public string algorithm = "HMAC-SHA256";
}
The test:
[Test]
public void CanDeauthorizeUser()
{
var appSecret = ConfigurationManager.AppSettings["NewsletterSubscriptionFacebookAppSecret"];
var envelope = new RequestEnvelope { user_id = 123456 };
var signedRequest = SignedRequest.SignedRequestFor(appSecret, JsonConvert.SerializeObject(envelope));
var postData = "signed_request=" + signedRequest;
byte[] postBytes = Encoding.UTF8.GetBytes(postData);
var request = WebRequest.Create(ConfigurationManager.AppSettings["AppWebUrl"] + "/SubscriptionHandler.ashx?a=deactivate");
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = postBytes.Length;
using (var newStream = request.GetRequestStream())
{
// Send the data.
newStream.Write(postBytes, 0, postBytes.Length);
newStream.Close();
}
// Get the response.
using (var response = request.GetResponse())
{
var status = ((HttpWebResponse) response).StatusDescription;
// Get the stream containing all content returned by the requested server.
using (var dataStream = response.GetResponseStream())
{
// Open the stream using a StreamReader for easy access.
using (var reader = new StreamReader(dataStream))
{
// Read the content fully up to the end.
string responseFromServer = reader.ReadToEnd();
Console.WriteLine(responseFromServer);
Console.WriteLine("status: " + status);
}
}
}
}
Snippet from the Handler that I post to:
string signedRequestValue = HttpContext.Current.Request.Form["signed_request"];
var items = SignedRequestParser.Parse(ConfigurationManager.AppSettings["NewsletterSubscriptionFacebookAppSecret"], signedRequestValue);
And the code for parsing that (taken/modified from the codeplex facebook API):
public class SignedRequestParser
{
/// <summary>
/// Parses the signed request string.
/// </summary>
/// <param name="appSecret">The unique app hash known the provider and the developer</param>
/// <param name="signedRequestValue">The encoded signed request value.</param>
/// <returns>The valid signed request.</returns>
public static Dictionary<string, string> Parse(string appSecret, string signedRequestValue)
{
Check.Require(!String.IsNullOrEmpty(signedRequestValue));
Check.Require(signedRequestValue.Contains("."), "Invalid Signed Request");
string[] parts = signedRequestValue.Split('.');
var encodedValue = parts[0];
if (String.IsNullOrEmpty(encodedValue))
{
throw new InvalidOperationException("Invalid Signed Request");
}
var sig = Base64UrlDecode(encodedValue);
var payload = parts[1];
using (var cryto = new System.Security.Cryptography.HMACSHA256(Encoding.UTF8.GetBytes(appSecret)))
{
var hash = Convert.ToBase64String(cryto.ComputeHash(Encoding.UTF8.GetBytes(payload)));
var hashDecoded = Base64UrlDecode(hash);
if (hashDecoded != sig)
{
throw new InvalidOperationException("Invalid Signed Request.");
}
}
var payloadJson = Encoding.UTF8.GetString(Convert.FromBase64String(Base64UrlDecode(payload)));
var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(payloadJson);
return data;
}
/// <summary>
/// Converts the base 64 url encoded string to standard base 64 encoding.
/// </summary>
/// <param name="encodedValue">The encoded value.</param>
/// <returns>The base 64 string.</returns>
private static string Base64UrlDecode(string encodedValue)
{
Check.Require(!String.IsNullOrEmpty(encodedValue));
encodedValue = encodedValue.Replace('+', '-').Replace('/', '_').Trim();
int pad = encodedValue.Length % 4;
if (pad > 0)
{
pad = 4 - pad;
}
encodedValue = encodedValue.PadRight(encodedValue.Length + pad, '=');
return encodedValue;
}
}