PHP -- calculating correct HMAC signature as nodejs script - php

So I am working on a PHP script that queries an API which uses HMAC authentication headers. However, I have been banging my head trying to encode the HMAC signature correctly. I have a preexisting nodejs script to work from as a template.
In the nodejs script, the HMAC signature is calculated using the following:
var crypto = require('crypto');
var hmac = [];
hmac.secret = 'ODc0YTM3YzUxODFlMWQ1YTdhMGQwY2NiZmE1N2Y1ODdjYzM5NTgyMDJhZjVkYTE4MmQxYzQ5ODk0M2QzNWQxYw==';
hmac.timestamp = 1457326475000;
hmac.path = '/account/';
hmac.message = hmac.path +'\n' + hmac.timestamp;
var sig = crypto.createHmac('sha512', new Buffer(hmac.secret, 'base64'));
hmac.signature = sig.update(hmac.message).digest('base64');
console.log(hmac);
This correctly calculates the HMAC signature as:
bWjIFFtFmWnj0+xHLW2uWVa6M6DpbIV81uyUWwRFCJUg+0Xyt40QWZWQjGvfPUB/JbjGZHUoso0Qv5JHMYEv3A==.
Meanwhile, in PHP, I am using:
<?php
$hmac['secret'] = 'ODc0YTM3YzUxODFlMWQ1YTdhMGQwY2NiZmE1N2Y1ODdjYzM5NTgyMDJhZjVkYTE4MmQxYzQ5ODk0M2QzNWQxYw==';
$hmac['nonce'] = '1457326475000';
$hmac['path'] = '/account/';
$hmac['message'] = $hmac['path']."\n".$hmac['nonce'] ;
$hmac['signature'] = base64_encode(hash_hmac('sha512',$hmac['message'],
$hmac['secret'], true));
print_r($hmac);
The above code, will calculate the HMAC signature as:
vqP49m/bk9nA4S3nMqW2r+kc2+yBfwhY/jWGUfz6dlKJUMkC2ktiPnuCcymdSWl4XezZT5VKCATYfus86Hz/Gg==
Working from the principle that "one million monkeys hacking away at a million keyboards" might one day be able to encode a valid HMAC signature, I have even tested a loop that iterates through all the permutations of the above PHP code (with/without base64 encoding the message, secret; with/without binary encoding of the HMAC, etc.)... to no avail.
Any suggestions for this here, one exhausted simian?

The problem is that you're not decoding your $hmac['secret'] first before passing it to hash_hmac().
Try:
$hmac['secret'] = base64_decode($hmac['secret']);
$hmac['signature'] = base64_encode(
hash_hmac('sha512', $hmac['message'], $hmac['secret'], true)
);

Related

hmac in python not matching hmac in PHP and node

I create an HMAC in Node like below:
const check = crypto.createHmac("sha1", "mysecret");
check.update(JSON.stringify("mybody"));
const digest = check.digest("hex");
Then I create the HMAC in PHP:
hash_hmac("sha1","mybody","mysecret")
Then I create the HMAC in Python 3
key = bytes("mysecret", 'utf-8')
message = bytes("mybody", 'utf-8')
digester = hmac.new(key, message, hashlib.sha1).hexdigest():
Then HMAC from Node and PHP match, no issues. But the HMAC from Python is always different and I cant figure out why.
Any help will be appreciated.

Encrypt Decrypt Python PHP convert AES

I have this issue where something is encrypted in python using aes 256 cbc encryption as shown in the python codes encrypt method .
I am trying to create a Decrypt method in php to actually decrypt whats encrypted using the python class .
Here is my attempt to convert the python decryption method to php does it look right or am I missing something in my conversion as every time i use the php version to decrypt it says hmac failed ?
anyhelp in converting the python class to php i will appreciate.
public function decrypt(){
$encrypt_method ="AES-256-CBC";
$secret_key =base64_decode('samekeyusedintheencryption');
$encrypted=(string)'some encrypted text to be decrypted';
$data=json_decode(base64_decode($encrypted),true);
$secret_iv =base64_decode($data['iv']);
$output = \openssl_decrypt($data['value'],
$encrypt_method,$secret_key,0,$secret_iv);
return json_encode($output);
}
def decrypt(self, payload):
data = json_c.decode(base64.b64decode(payload))
value = base64.b64decode(data['value'])
iv = base64.b64decode(data['iv'])
crypt_object=AES.new(self.key,AES.MODE_CBC,iv)
plaintext = crypt_object.decrypt(value)
return loads(plaintext)
OK, I got it to work!
function decrypt($encryptedText, $secret_key){
$secret_key = base64_decode($secret_key);
$encrypt_method ="AES-256-CBC";
$data = json_decode(base64_decode($encryptedText),true);
$data['iv'] = base64_decode($data['iv']);
$data['value'] = base64_decode($data['value']);
return openssl_decrypt($data['value'], $encrypt_method, $secret_key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $data['iv']);
}
Some things I learned:
If the options in the openssl function are set to '0' it expects a base64_encoded input for the cipher text. Also, if the default options is set to '0' the padding default is set to PKCS#7. This, I think, is why we were getting the bad block size error.
So, the cipher text needs to be base64_decoded and we need to set both options for the padding.
I was able to decrypt your provided cipher text and see the email addresses.
You are provided the MAC in the Data array so this would allow you to check the MAC in the PHP script. This allows you to make sure the data has not been tampered with.
I recently did an encryption project and started with the open ssl, but ended up changing to the libSodium library. I highly recommend you check it out for any further projects.
Cheers!

NodeJS crypto.createHmac SHA512 not working as expected

I'm trying to implement authentication in API request but I am having weird issue with while signing data. The output from crypto.createHmac in NodeJS is different from that of hash_hmac in PHP. I am trying to implement this on PHP and response from NodeJs is what I expected.
Code for NodeJS
var crypto = require('crypto');
var hmac = crypto.createHmac('sha512', new Buffer(key, 'base64'));
var digest = hmac.update(salt + '\n' + returnUrl).digest();
var signature = digest.toString('base64');
console.log(signature);
Code for PHP
$signature = base64_encode(hash_hmac("sha512",$salt.'\n'.$returnUrl, $key));
echo $signature;
For NodeJS Application I got
VZ+gH+BVdZl6GIILfN5TFAxEIUqstiD5YwuQYTdk3R4LSxVzsAXPpPpn5MwHDoRllz5ll5iBpAOuO2DQ9nvtGA==
which is expected response but PHP gave me different result
NzNlNmFhNWUwOTg2OGQ3NzdhNjU0MWZmOTZhZDZjYmFiYjE4ZjMxZTc5OTk3MDI5ZGI1M2M3YmU3MjRiYTdiNjJmODM4NDA1NmMzNWZlOWE2NTVkZjhmZTg4MDg0MjA3ZmYzMjg1MGQxYzFiZDNkODA2M2MzZGVhOTRiZWMzNTA=
which is entirely different than expected. Is there a way I can recreate NodeJS crypto functionality in PHP getting same response.
In the following example, I use node.js and PHP to get signing data, and both of them get the same output. You can take that as a reference.
Code for Node.js
var crypto = require('crypto');
var hmac = crypto.createHmac('sha512', 'sharedpassword');
var digest = hmac.update('12345\nhttps://google.com').digest('hex');
var signature = new Buffer(digest).toString('base64');
console.log(signature);
Then I get:
OTllY2U2NTU4ZWVkYzczZmNlMTQ2YzdlMmI4NjI5MTdhODRhYWNmOGEyNmYzM2M5ZjQ5ZjE1NTM5MjQ1ZDAzYWU1MzlkYTViODY4MjcwM2UyMTA5MjVhMzcyNWJhOWU3OTU3MjY5NWI4ZjkzZGY1Zjg1NTNmYzI3MWU0ZTIyZWE=
Code for PHP
$signature = base64_encode(hash_hmac('sha512', "12345\nhttps://google.com", 'sharedpassword'));
echo $signature;
Got:
OTllY2U2NTU4ZWVkYzczZmNlMTQ2YzdlMmI4NjI5MTdhODRhYWNmOGEyNmYzM2M5ZjQ5ZjE1NTM5MjQ1ZDAzYWU1MzlkYTViODY4MjcwM2UyMTA5MjVhMzcyNWJhOWU3OTU3MjY5NWI4ZjkzZGY1Zjg1NTNmYzI3MWU0ZTIyZWE=
Also note that PHP only interprets escaped characters when in double quotes ("). So \n must be in double quotes!

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

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