Having trouble generating a proper signature using Amazon advertising API with PHP - php

I'm following several examples I've found online and I'm trying to recreate the signature they have in the official API documentation here. However, I'm failing to generate the same signature.
They state the string to sign is as follows:
GET
webservices.amazon.com
/onca/xml
AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&ItemId=0679722769&Operation=I
temLookup&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReview
s&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&
Version=2009-01-06
It states to "Calculate an RFC 2104-compliant HMAC with the SHA256 hash algorithm using the string above with our "dummy" Secret Access Key: 1234567890."
I do this using the following code:
$private_key = "1234567890";
$string_to_sign = "GET
webservices.amazon.com
/onca/xml
AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&ItemId=0679722769&Operation=I
temLookup&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReview
s&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&
Version=2009-01-06";
$signature = base64_encode(hash_hmac("sha256",$string_to_sign, $private_key, True));
This yields the following signature:
LM5S6MrycUETu1p94QDnLurKIpwiqKnCxm3B73a0QiE=
Amazon's signature is:
M/y0+EAFFGaUAp4bWv/WEuXYah99pVsxvqtAuC8YN7I=
I've followed a bunch of examples I found via Google and they all appear to do this the same way. However, I can't arrive at the same signature that Amazon gets and I can't figure out why.
Any help is appreciated.

I just burned a few hours of my life trying to figur out why my signature wasn't matching. In case this helps somebody out, use Amazon's example as mentioned by the OP. I started having trouble at steps 6 and 7. Amazon says to prepend the following three lines (including line breaks) to your string:
Get
webservices.amazon.com
/onca/xml
On my windows 7 platform, here's what I did for those steps:
$string_to_sign = "AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&AssociateTag=mytag-20&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=Images%2CItemAttributes%2COffers%2CReviews&Service=AWSECommerceService&Timestamp=" . "2014-08-18T12%3A00%3A00Z&Version=2013-08-01";
$prepend = "GET\nwebservices.amazon.com\n/onca/xml\n";
$string_to_sign = $prepend . $string_to_sign;
Then, step 8 threw me off too because I was using the sample's secret key:
Secret Access Key: "1234567890"
instead of the correct key:
Secret Access Key: 1234567890
Hope these little mistakes don't burn anybody else.

Do you really have all those linebreaks in your actual code? Because I\ntemLookup is not the same as ItemLookup. That GET URI should be a single long string, not a multi-line string.
GET webservices.amazon.com /onca/xmlAWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReviews&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-06
Notice how it's all on the SAME line

Related

shopify embedded app hmac verification failed php

Hi shopify guru's I know this question has come around a thousand times, I know this because I have read every single thread I could find.
My app verificaton was working fine, but now that I switched to embedded app, I can't seem to verify the hmac.
//Remove hmac from hash comparison
$hmac = $data['hmac'];
unset($data['hmac']);
//sort the values alphabetically
ksort($data);
$data = urldecode(http_build_query($data));
$hash = hash_hmac('sha256', $data, $this->ci->get('settings')['shopify']['api_secret']);
This code will keep returning a different hash from the hmac shopify sends me, I'm guessing there is a problem with encoding or escaping, I have tried every thing I could think of (htmlspecialchars, urldecode, strreplace, doublecheck secret etc..)
the string looks like this after the urldecode line:
locale=en&protocol=https://&shop=mystorehandle.myshopify.com&timestamp=1539901099
Any help would be appreciated, I wanted to get some work done on my app, but the past 3 hours have been filled with trying to get my hmac in sync with shopify's :(
I'm quiet certain I'm doing it right, but have no clue as to why it won't work then.
I'm buying a pie for whoever knows the answer first
.
Ok so... don't ask me how or why this works, but it does. After 2-3 hours on this, I took a complete stab in the dark, and it worked. I'm pretty sure it only works because there is a bug on the Shopify side.
My embedded app was being fed GET params hmac/shop/timestamp/protocol/locale in that order.
For some reason, building a query string of shop=[myshop]&timestamp=[timestamp] worked.
In other words, I removed hmac, but ALSO protocol and locale.
Using this code though, BROKE the TEST version of my app, which was actually working fine with protocol and locale included.
My only conclusion here is that the hmac is derived from the GET parameters following hmac, which are in alphabetical order, stopping when the following parameter is not in that order.
So if GET params are hmac/shop/timestamp/protocol/locale - generate your hash string using shop & timestamp.
If GET params are hmac/locale/protocol/shop/timestamp - generate your hash string using locale, protocol, shop & timestamp.
So strange. Would be really interested to know if this works for you also!
Here's my code:
parse_str($_SERVER['QUERY_STRING'], $queryStringArray);
$providedHmac = $_GET['hmac'];
unset($queryStringArray['hmac']);
$amp = '';
$i = 0;
foreach($queryStringArray as $key => $value)
{
$keyFirstLetter = substr($key, 0, 1);
if($i == 0 || $keyFirstLetter > $lastKeyFirstLetter)
{
$newQueryString .= $amp . $key . '=' . $value;
$amp = '&';
}
$lastKeyFirstLetter = $keyFirstLetter;
$i++;
}
$calculatedHmac = hash_hmac('sha256', $newQueryString, SHOPIFY_APP_SHARED_SECRET);
$hmacValid = false;
if($calculatedHmac == $providedHmac)
{
$hmacValid = true;
}

Gravity Forms Signature - From PHP to Python

I need to translate some existing PHP code to Python. This job connects to gravity forms and queries for certain data. In order to make the query, a signature must be calculated in order to verify the connection.
The Gravity Forms web api gives good PHP directions here.
The PHP method is as follows:
function calculate_signature( $string, $private_key ) {
$hash = hash_hmac( 'sha1', $string, $private_key, true );
$sig = rawurlencode( base64_encode( $hash ) );
return $sig;
}
Based on my understanding of Python and the information about hash-hmac and rawurlencoded from php2python.com, I wrote the following:
import hmac, hashlib, urllib, base64
def calculate_signature(string, private_key):
hash_var = hmac.new(private_key, string, hashlib.sha1).digest()
sig = urllib.quote(base64.b64encode(hash_var))
return sig
However, the two signatures are not equivalent, and thus Gravity Forms returns a HTTP 403: Bad Request response.
Am I missing something within my translation?
Update (11/04/15)
I have now matched my php and python urls. However, I still receive a 403 error.
The reason the php and python signatures did not match had nothing to do with their calculate_signature() methods.
The issue was caused by differing expires variables. Php used strtotime("+60 mins") which resulted in a UTC time 60 minutes from now. Whereas Python used datetime.date.now() + timedelta(minutes=60). This is also 60 minutes from now, but in your current timezone.
I always want to calculate the expire variable in UTC so I replaced my Python calculation with datetime.datetime.utcnow() + timedelta(minutes=60).
You're almost there. urllib.quote does not encode slashes, for example, as PHP's rawurlencode does. You can use urllib.quote_plus to achieve the desired effect:
import hmac, hashlib, urllib, base64
def calculate_signature(string, private_key):
hash_var = hmac.new(private_key, string, hashlib.sha1).digest()
sig = urllib.quote_plus(base64.b64encode(hash_var))
return sig

PHP DSA Signature Conversion from DER ASN.1 to XMLDSIG P1363

I am writing a PHP app (acting as a SAML IdP) which is trying to do a login via a SAML Response to a server (acting as the SAML SP. I am currently stuck with the server rejecting the request (I just get a 500 Bad Request).
I have written a test app (in Java/openSAML - which I'm pretty sure the server is using), and can see that the problem is that the SAML SignatureValidator validate generates
org.apache.xml.security.signature.XMLSignatureException: Invalid XMLDSIG format of DSA signature
Looking at the SAML SignatureValidator code I can see that it checks that the XMLDISG signature is exactly 40 bytes long (P1363 format?) - whereas the generated signature is 46-48 bytes long (DER ASN.1 format?).
The signature is being generated by PHP openssl_sign as below.
openssl_sign($canonicalized_signedinfo,
$signature,
$private_key,
OPENSSL_ALGO_DSS1))
An example signature (displayed as binary to hex for clarity) is as below. This is 46 bytes, but I notice it varies (depending on the random key?) from 46 to 48 bytes.
302c02146e74afeddb0fafa646757d73b43bca688a12ffc5021473dc0ca572352c922b80abd0662965e7b866416d
I can successfully verify this signature using PHP openssl_verify as below.
openssl_verify ($canonicalized_signedinfo,
$signature ,
$public_key,
OPENSSL_ALGO_DSS1))
But in my test app when I do a SignatureValidator validate (as below) I get the XMLSignatureException: Invalid XMLDSIG format of DSA signature exception.
BasicCredential credential = new BasicCredential();
credential.setPublicKey(publicKey);
credential.setUsageType(UsageType.SIGNING);
SignatureValidator sigValidator = new SignatureValidator(credential);
sigValidator.validate(signature);
Does anyone know how to do the PHP signature conversion from the 46-48 DER ASN.1 format generated by PHP openssl_sign to the 40 byte P1363 format expected by openSAML?
That resource from code project has explanations about how to convert ASN.1 format into P1363 with code examples. It may be useful to write a Java validation method.
And I propose you use this C++ code to generate a DSIG compliant signature from PHP: http://xmlsig.sourceforge.net/
By the way, it sounds more complex than simply generate the signature and validate it. You may be interested in XMLBlackbox

Trying to list Devpay S3 buckets - getting "SignatureDoesNotMatch" error

What I'm trying to do:
I'm trying to list Devpay S3 buckets.
References I'm using:
1. http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTServiceGET.html
2. http://docs.amazonwebservices.com/AmazonDevPay/latest/DevPayDeveloperGuide/QuickReferenceS3Products.html
3. http://docs.amazonwebservices.com/AmazonDevPay/2007-12-01/DevPayDeveloperGuide/DesktopAWSCalls.html
How I'm doing it:
A simple REST GET request using the PHP libcURL library:
GET / HTTP/1.1
Host: s3.amazonaws.com
Date: $date
X-Amz-Date: $date
X-Amz-Security-Token: $userToken, $productToken
Authorization: $signatureValue
where
$date // = timestamp at the time of request
$signatureValue //= Authorization: AWS $signature
$signature //= $userAccessKey:$signatureString
$signatureString //=base64 encoded HMAC-SHA1 of $stringToSign with $userSecretKey
$stringToSign //= utf8 encoded string:
this string -
GET\n
""\n
""\n
$date\n
$canonicalized_AMZ_Headers\n
/
I'm using curl_exec() to execute the GET request.
Error that I'm getting:
"SignatureDoesNotMatch"
"The request signature we calculated does not match the signature you provided. Check your key and signing method"
Thanks!
Any help would be greatly appreciated
In the returned error message from S3 will be a StringToSign element (use View Source to see it more clearly). Compare this carefully with the string you are appending together to sign the request — they must be exactly the same.

Trying to digitally sign via HMAC-SHA1 with PHP

I'm trying to setup some Google Maps Premier API action, and to do so, I need to sign my URLs to authenticate. If you go down to Signature examples, there is some Python, C# and Java code to show you how to do the signature via HMAC-SHA1. There is also an example so that I can to test my PHP implementation. However, I just can't seem to get it to work.
Here's my code:
$key = "vNIXE0xscrmjlyV-12Nj_BvUPaw=";
$data = "/maps/api/geocode/json?address=New+York&sensor=false&client=clientID";
$my_sign = hash_hmac("sha1", $data, base64_decode($key));
$my_sign = base64_encode($my_sign);
$valid_sign = "KrU1TzVQM7Ur0i8i7K3huiw3MsA=";
When, I run this, I get a signature of:
ZDRlNGMwZjIyMTA1MWM1Zjk0Nzc4M2NkYjlmNDQzNDBkYzk4NDI4Zg==
Which totally doesn't match.
Things I have thought about:
The key is in Modified URL encoded format, so changing - and _ to + and / also doesn't work
The Python example code does indeed work, so this is a valid example.
Completely rewriting our code-base in python instead of PHP (I inherited it).
You have 2 problems at least,
The Google uses special URL-safe Base64. Normal base64_decode doesn't work.
You need to generate the SHA1 in binary.
Try this,
$key = "vNIXE0xscrmjlyV-12Nj_BvUPaw=";
$data = "/maps/api/geocode/json?address=New+York&sensor=false&client=clientID";
$my_sign = hash_hmac("sha1", $data, base64_decode(strtr($key, '-_', '+/')), true);
$my_sign = strtr(base64_encode($my_sign), '+/', '-_');
A php example is available at http://gmaps-samples.googlecode.com/svn/trunk/urlsigning/UrlSigner.php-source
I assume your trying to sign the url for OAuth?
Try out this library: http://code.google.com/p/oauth/

Categories