Amazon product lookup signature mismatch - api

This is the response I'm getting: The request signature we calculated does not match the signature you provided.
Here is the code I'm using to generate the signature:
static byte[] HmacSHA256(String data, byte[] key)
{
String algorithm = "HmacSHA256";
KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create(algorithm);
kha.Key = key;
return kha.ComputeHash(Encoding.UTF8.GetBytes(data));
}
static byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName)
{
byte[] kSecret = Encoding.UTF8.GetBytes(("AWS4" + key).ToCharArray());
byte[] kDate = HmacSHA256(dateStamp, kSecret);
byte[] kRegion = HmacSHA256(regionName, kDate);
byte[] kService = HmacSHA256(serviceName, kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
return kSigning;
}
When I use Amazon's test settings, I get the correct signature
key = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
dateStamp = '20120215'
regionName = 'us-east-1'
serviceName = 'iam'
However, when I put my live settings in, I get the not matching error.
Here is what I'm using to get my signature:
var reqSig = getSignatureKey("[my secret key]", dateStamp, "us-west-2","AWSECommerceService");
This is what I'm submitting (I'm just testing it in a browser, for now):
ecs.amazonaws.com/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=[my
access key that corresponds with the secret
key]&Operation=ItemLookup&IdType=UPC&ItemId=635753490879&Timestamp=2019-01-24T19:14:55.2644840Z&Signature=32BA07ECE67F3177BF2EA02923E624D612A45FAA144ED0E43BDDC0DF6574EAC3
I'm not sure if it has to do with the region -- us-west-2 in my case -- because there is no place on the request for a region. I'm not sure how Amazon can test my signature against my parameters if I can't specify what region it is and I've used the region to calculate the signature.
What am I missing?

Ok. After spending days going through this, here is what I had to do:
/*
DOCUMENTATION: https://docs.aws.amazon.com/AWSECommerceService/latest/DG/rest-signature.html#rest_detailedexample
*/
var itemID = "0679722769";
var accessKeyID = "AKIAIOSFODNN7EXAMPLE";
var timeStamp = DateTime.UtcNow.ToString("o");
var req = $"Service=AWSECommerceService&AWSAccessKeyId={accessKeyID}&Operation=ItemLookup&IdType=UPC&ItemId={itemID}&Version=2013-08-01&Timestamp={timeStamp}";
req = req.Replace(":", "%3A").Replace(",", "%2C"); //UrlDecode certain characters
var reqlist = req.Split('&').ToArray(); //we need to sort our key/value pairs
Array.Sort(reqlist);
req = String.Join("&", reqlist); //join everything back
var reqToSign = $#"GET
webservices.amazon.com
/onca/xml
{req}".Replace("\r", ""); //create the request for signing. We need to replace microsofts's crlf with just a lf; Make sure there are no leading spaces after the linefeeds.
var signage = getSignatureKey("1234567890",reqToSign);
req = $"http://webservices.amazon.com/onca/xml?{req}&Signature={signage}"; //create our request with the signature appended.
return req;
}
private static byte[] HmacSHA256(String data, byte[] key)
{
String algorithm = "HmacSHA256";
KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create(algorithm);
kha.Key = key;
return kha.ComputeHash(Encoding.UTF8.GetBytes(data));
}
private static string getSignatureKey(string key, string stringToSign)
{
byte[] kSecret = Encoding.UTF8.GetBytes(key.ToCharArray());
byte[] kSigning = HmacSHA256(stringToSign, kSecret);
return WebUtility.UrlEncode(Convert.ToBase64String(kSigning));
}
Contrary to most of the answers found here and elsewhere, this is the only way that works. The entire request has to be hashed, not just particular parameters. I can't speak to other Amazon services, but the Commerce Service has to be done like this.
Quite a few answers referenced this: https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html or this: Amazon API generating a request signature in C# .NET
As I stated in my question, this is most certainly not correct. If you're not passing a region parameter, how can Amazon create the same signature since it doesn't have all the information.
Anyway, this works now.

Related

Autosuggestion on encrypted Data(AES-256 GCM Mode)

For PII purposes, we are encrypting the database fields like email etc.
Now for exact match queries we are also keeping a hashed form(HMAC) for the fields.
But how to run the autosuggestion from Solr / like queries from MySQL.
My encryption code is
public String encrypt(byte[] plaintext, byte[] dataKey, String version) throws Exception {
long startTime = System.currentTimeMillis();
// Generate Initialization Vector
byte[] IV = generateIV();
// Get Cipher Instance
Cipher cipher = getCipher();
// Store Version
byte[] versionArr = new byte[3];
versionArr = version.getBytes();
// Generate Key
SecretKeySpec keySpec = new SecretKeySpec(dataKey, "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_SIZE_BYTES * 8, IV);
// Initialize Cipher for ENCRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Encryption
byte[] cipherText = cipher.doFinal(plaintext);
int capacity = 3 + GCM_IV_SIZE_BYTES + plaintext.length + GCM_TAG_SIZE_BYTES;
// Create ByteBuffer & add IV & CipherText
ByteBuffer buffer = ByteBuffer.allocate(capacity);
buffer.put(versionArr);
buffer.put(IV);
buffer.put(cipherText);
long endTime = System.currentTimeMillis();
// return the final encrypted cipher txt
return Base64.getEncoder().encodeToString(buffer.array());
}
private static byte[] generateIV() {
final Random r = new SecureRandom();
byte[] IV = new byte[GCM_IV_SIZE_BYTES];
r.nextBytes(IV);
return IV;
}
private static Cipher getCipher() {
try {
return Cipher.getInstance("AES/GCM/NoPadding");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
return null;
}
Short answer, it’s possible, but quite difficult.
Long answer:
One of the basics of hashes is that the hash changes a lot, by changing the input only a little, so there is no way to know if a hash is close to matching the input value.
Now you might think inputting the hash into the search engine might work and with some customization to the tokenizer (the thing that takes the search input and splits it into small parts for the engine to match), this would actually work. However, you did just make it possible to reverse the hash, let me explain:
For autocomplete of a single field, the tokenizer would use an edge n-gram. What this does is splitting a single string into multiple tokens that can be exactly matched, for example john#gmail.com would be split into:
j, jo, joh, john, john#, …, john#gmail.c, john#gmail.co, john#gmail.com
Now the search engine can search for exact matches on all the tokens and recommend the closest matches as possible values.
If you would customize the tokenizer, to hash everything before it is stored, and then hased the input upon search, this would absolutely work, but now lets look at what happens when an attacker gets the hashed tokenized data.
As in the example with john#gmail.com the first stored value would be the hash of j, which by brute force would take a fraction of a second to reverse. Since you now know the first letter, the second letter would go just as fast and so on. Rendering the whole point of using the hash in the first case obsolete.
ps. A secure hashing algorithm with a salt might work, but the tokenization is almost always done by the search engine, so the compute part of the search engine has to be "secure"
Sources:
https://en.wikipedia.org/wiki/N-gram

Trying to do a structural replace in IntelliJ

I want to replace my annotations from #RequestMapping to #GetMapping, #PutMapping ... annotations. When I looked at the Structural Find/Replace in IntelliJ looked like it could do the job.
I tried adding the following in the search
#RequestMapping( $key$ = $value$)
Added a filter on the key. text=method.
Now I want to extract the from the value (RequestMethod.GET) , the word after . (period). and then in the replacement add
#[Word(TitleCase)]Mapping( [everything except the key,value that was extracted in the search])
Haven't been able to figure out how to go about this. Would be nice to know if this can't be done, or any suggestions on how to do this. Looked at some of the other questions here on SO, but didn't find anything that could help. Most of the answers are to use regex in those cases.
Before:
#RequestMapping(
value = "/channels/{channel_name}",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
public Channel updateChannel(
#PathVariable("channel_name") String channelName,
#Valid #RequestBody Channel channel) {
return channelService.updateChannel(channelName, channel);
}
#RequestMapping(
value = "/channels/{channel_name}",
method = RequestMethod.DELETE,
produces = MediaType.APPLICATION_JSON_VALUE)
public Channel deleteChannel(
#PathVariable("channel_name") String channelName) {
return channelService.deleteChannel(channelName);
}
After
#PostMapping(value = "/channels/{channel_name}",
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
public Channel updateChannel(
#PathVariable("channel_name") String channelName,
#Valid #RequestBody Channel channel) {
return channelService.updateChannel(channelName, channel);
}
#DeleteMapping(
value = "/channels/{channel_name}",
produces = MediaType.APPLICATION_JSON_VALUE)
public Channel deleteChannel(
#PathVariable("channel_name") String channelName) {
return channelService.deleteChannel(channelName);
}
I would do this dirty, with regex:
Replace RequestMethod.(.)(.+)(?=,) into RequestMethod.\U$1\L$2 (\L would turn the text into lowercase.)
Replace #RequestMapping\((\s+)(.+)(\s+?)(.+)RequestMethod.(.+?), into #$5Mapping\($1$2$3.
Then simplify this replacement chain:
Replace #RequestMapping\((\s+)(.+)(\s+?)(.+)RequestMethod.(\S)(.+?), into #\U$5\L$6\EMapping\($1$2
Update: Noticed the first parameter value is not specified whether in the line of #Mapping or a standalone line.
If you need it in the line of #Mapping, replace #RequestMapping\((\s+)(.+)(\s+?)(.+)RequestMethod.(\S)(.+?),\s into #\U$5\L$6\EMapping\($2$3.
If you need it to a standalone line, replace #RequestMapping\((\s+)(.+)(\s+?)(.+)RequestMethod.(\S)(.+?), into #\U$5\L$6\EMapping\($1$2.

sending string parameter in action=track leanplum Rest Api not working

I want to send string parameters in Leanplum api using action script
Eg param:{"Element":"Hi"}
var request:URLRequest = new URLRequest("https://www.leanplum.com/api");
request.method = URLRequestMethod.GET;
var variables:URLVariables = urlVariables;
variables.userId = userId;
variables.event = eventName;
var params:Object = new Object();
params.Element = "Hi";
var paramStr:String = JSON.stringify(params);
variables.params = paramStr;
variables.appId = appId;
variables.clientKey = clientKeyProduction;
variables.apiVersion = apiVersion;
variables.action = "track";
variables.versionName = AppInfo.getInstance().appVersion;
request.data = variables;
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, function(e:Event):void {
trace(e.target.data);
});
loader.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void {
trace(e.target.data);
});
loader.load(request);
This is actual Request (App ID and ClientKey are dummy):
https://www.leanplum.com/api?clientKey=V42fKaJaBuE&userId=1010&params={"Element":"Ur"}&appId=HEVdDlXiBVLwk&event=Element_Opened&action=track&versionName=2.3.0&apiVersion=1.0.6&info=Lu
Encoded Request:
https://www.leanplum.com%2Fapi%3FclientKey%3DV42fKaJaBuE%26userId%3D1010%26params%3D%7B%22Element%22%3A%22Ur%22
%7D%26appId%3DHEVdDlXiBVLwk%26event%3DElement_Opened%26action%3Dtrack%26versionName%3D2.3.0%26apiVersion%3D1.0.6%26info%3DLu
if I run above request in any rest client I get the same status success : true .
I am getting the response {"response": [{"success": true}]} but I can't find the parameters with value string in Leanplum dashboard, its listing parameter name but not the String Value for parameter.
If you apply some combination of filters then you can see values of parameter you sent to leanplum. like First select the occurrence of some event then apply Group by parameter then select parameter you want to see the data for.
Its a little different from flurry, Google analytics etc.

Post values in URL are not set because of special characters in Web API

I'm trying to pass a string with special characters to your web api but is giving error.
Below the line where I pass the values ​​pro web api:
string listaParcelaSeparadoVirgula = null;
foreach (var item in listaParcelas)
{
listaParcelaSeparadoVirgula = listaParcelaSeparadoVirgula + ";;" + item;
}
var result = HttpUtility.UrlEncode(listaParcelaSeparadoVirgula);
var response = client.PostAsJsonAsync("api/LancamentoReceitaDespesa/AddLancamentoParcelar/" + result, lancamentoReceitaDespesa).Result;
the result is a string variable with values ​​separated by ";;". Below the contents of the string:
";;aaaaa 1/2||10/01/2014|100,00||;;aaaaa 2/2||10/02/2014|100,00||"
with UrlEncode:
"%3b%3baaaaa+1%2f2%7c%7c10%2f01%2f2014%7c100%2c00%7c%7c%3b%3baaaaa+2%2f2%7c%7c10%2f02%2f2014%7c100%2c00%7c%7c"
Error:
{"Error while copying content to a stream."}
How can I pass these values ​​pro web api?
Well you could try encode value with base64 since in url you could have special symbols
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(result);
var response = client.PostAsJsonAsync("api/LancamentoReceitaDespesa/AddLancamentoParcelar/" + System.Convert.ToBase64String(plainTextBytes), lancamentoReceitaDespesa).Result;
then in web
public void AddLancamentoParcelar(string base64EncodedData) {
var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData);
var result = System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
}
I am not sure if its the best solution but as you could have any symbol in url then its could be an solution.

Index (zero based) must be greater than or... Working with the Bit.ly API

I'm working (actually more like playing) around with the Bit.ly API, and keep getting the error in the title of this question. So I'm going to show you the code and hopefuly someone can help me resolve this. First the client side code.
var x = service.GetClicks(url, service.BitlyLogin, service.BitlyAPIKey);
Console.WriteLine(x);
Console.ReadLine();
And this is the code that's being called
public List<int> GetClicks(string url, string login, string key)
{
List<int> clicks = new List<int>();
url = Uri.EscapeUriString(url);
string reqUri =
String.Format("http://api.bit.ly/v3/clicks?" +
"login={0}&apiKey={1}&shortUrl={2}&format=xml" +
login, key, url);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(reqUri);
req.Timeout = 10000; // 10 seconds
Stream stm = req.GetResponse().GetResponseStream();
XmlDocument doc = new XmlDocument();
doc.Load(stm);
// error checking for xml
if (doc["response"]["status_code"].InnerText != "200")
throw new WebException(doc["response"]["status_txt"].InnerText);
XmlElement el = doc["response"]["data"]["clicks"];
clicks.Add(int.Parse(el["global_clicks"].InnerText));
clicks.Add(int.Parse(el["user_clicks"].InnerText));
return clicks;
}
As you can see it's very simple code, nothing complicated, and I can see nothing that causes this error. Anyone out there who has worked with(the full error is Index (zero based) must be greater than or equal to zero and less than the size of the argument list.) the Bit.ly API and can lend a hand?
Instead this
string reqUri =
String.Format("http://api.bit.ly/v3/clicks?" +
"login={0}&apiKey={1}&shortUrl={2}&format=xml" + login, key, url);
Use this
string reqUri = String.Format("http://api.bit.ly/v3/clicks?login={0}&apiKey={1}&shortUrl={2}&format=xml", login, key, url);
Notice that I just changed the plus sign with the comma before "login, key, url);" at the end of the String.Format().
I narrowed it down to a place where I was using string.Format to build an array and has less in the string.Format than what was supposed to. I had it go to Index 3 but only filled to Index 2
Not for your specific case, but I ran into this: make sure that, if you have multiple parameters, you send them as an array of objects instead of an IEnumerable:
IEnumerable<object> myArgs = ...;
string toFormat = "{0} xyz {1}";
String.Format(toFormat, myArgs);
// ERROR, since myArgs is one argument whereas the string template requires two
String.Format(toFormat, myArgs.ToArray());
// Valid, as the Format() accepts an array of objects to fill all arguments in the string