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!
Related
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.
I need to decode a base64 token for an authentication string, and I found some working examples in Python, Perl and PHP, and I wrote the equivalent code in Node, but I ran into an issue. It seems the base64 decoder for Node doesn't work the same way as for the other 3 languages.
Running this in Python
token = 'BaSe64sTRiNghERe'
decoded_token = token.decode('base64')
print decoded_token
returns this string
???F#`?D^
Running this in Perl
my $token = 'BaSe64sTRiNghERe';
my $decoded_token = decode_base64($token);
print $decoded_token;
returns this string
???F#`?D^
Running this in PHP
$token = 'BaSe64sTRiNghERe';
$decoded_token = base64_decode($token, true);
echo $decoded_token;
returns this string
???F#`?D^
and finally, running this in a Node script
var token = 'BaSe64sTRiNghERe',
decoded_token = Buffer.from(token, 'base64').toString();
console.log(decoded_token);
returns this string
????F#`?D^
The question is, why the extra question mark in the decoded string? And how can I get the same result in Node as I get in Perl, Python and PHP?
UPDATE
running this in the command line
echo BaSe64sTRiNghERe | base64 --decode
gives me the same output as the perl, python and php scripts
but running the same command from node
var exec = require('child_process').exec;
exec('echo BaSe64sTRiNghERe | base64 --decode', function callback(error, stdout, stderr){
console.log(stdout);
});
I still get the wrong stuff.
The output is different since you have generated unprintable characters, and node seems to handle those unprintable characters differently from the other languages. You are also losing information:
>>> token = 'BaSe64sTRiNghERe'
>>> decoded_token = token.decode('base64')
>>> print decoded_token
???F#`?D^
>>> decoded_token[0] == decoded_token[1]
False
If you modify your python snippet to look like this:
import binascii
token = 'BaSe64sTRiNghERe'
decoded_token = binascii.hexlify(token.decode('base64'))
print(decoded_token)
Then modify your nodejs snippet to look like this:
var token = 'BaSe64sTRiNghERe',
decoded_token = Buffer.from(token, 'base64').toString('hex');
console.log(decoded_token);
You will avoid the differences in how they handle unprintable characters and see that the base64 decodes have the same byte values.
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)
);
I'm running node.js and php on windows and I use the included crypto module in node.js.
Php script:
hash_hmac("sha256", "foo", "bar", true) // the true enables binary output
outputs:
¶y3!è¬╝♂ï►ó│Ñ├Fä╚┘CA╝±G6▄rp¸t↑Q
Node.js script:
crypto.createHmac("sha256", "bar").update("foo").digest("binary");
outputs:
¶y3!?ª¼♂?►¢³¥ÃF?ÈÙCA¼ñG6Ürp÷t↑Q
I also wonder why some digits are the same but some others not.
I also tried getting the hex instead of the binary result, both of them output the same.
hash_hmac("sha256", "foo", "bar", false); // false outputs hex data
crypto.createHmac("sha256", "bar").update("foo").digest("hex"); // notice "hex"
This was not a solution because I failed to convert the hex data to binary:
var hmac = crypto.createHmac("sha256", "bar").update("foo").digest("hex");
var binary = new Buffer(hmac, "hex");
The variable binary outputs:
¶y3!???♂?►????F???CA??G6?rp?t↑Q
I came across the same problem when implementing a node js solution for OTP simplepay.
PHP Code:
base64_encode(hash_hmac('SHA384', $text, trim($key), true));
JS CODE:
function get_hash(key, text) {
const crypto = require("crypto");
const algorithm = "sha384";
var hmac = crypto.createHmac(algorithm, key).update(text);
return hmac.digest().toString('base64');
}
So both logged out / echoed - give the same result.
In your case, the binary output thus would be:
crypto.createHmac("sha256", "bar").update("foo").digest().toString('binary');
However, keep in mind that logging and echoing a binary string will give a slightly different view due to character encoding. You can see the same, but also different characters.
PHP echo
,cAW'B��o��傱�#�Vlάf�R#y�,?0�^1=Y�����u2
and
NODE console.log
,cAW'BÛåoº°å±¹#VlάfÞ꧸§u2
Are actually the same, they just look different.
see this github issue and addaleax's comment
calculateHmac(payload) {
const hmac = crypto.createHmac('sha256', KEY);
hmac.update(Buffer.from(payload).toString());
let bin = hmac.digest();
return Buffer.from(bin).toString('base64');
}
I need 2 sets of encrypt/decrypt functions to run under PHP and AS3.
I found AS3Crypto (for Flex) and mcrypt_encrypt (for PHP) and this article that shows how to use them for DES encryption:
http://www.zedia.net/2009/as3crypto-and-php-what-a-fun-ride/
I then tried to replace the DES encryption with AES-256, because the DES seems too vulnerable to brute force attacks.
The results from AES encryption in Flex and PHP are different.
Does anyone know (and tested) any equivalent functions for aes encryption in as3 and php?
If I wasn't clear enough, here is another post of an user having the same problem:
http://forum.openlaszlo.org/showthread.php?t=13709
Thanks!
I've done similar with different back end's and it can be a headache to get them to match.
I will tell you with a high degree of confidence from experience that most likely the issue you're running into is that the padding isn't matching up between the two or the encoding going back and forth (Hex).
Here's some working coldfusion code from an implementation I did a couple years ago, quickest I could dig up...
<cfsetting enablecfoutputonly="yes">
<cfparam name="form.k" default="1bbee91984f8b5bc032b6f67a665704e"/>
<cfscript>
textToEncrypt = 'printButton=No&saveButton=No';
encKey = ToBase64(BinaryDecode(form.k,"Hex"));
encryptedText = encrypt(textToEncrypt, encKey, 'AES', 'Hex');
</cfscript>
<cfoutput>settings=#encryptedText#</cfoutput>
(side note: isnt coldfusion awesome? hehe :P)
The decryption side in flex :
private function settingsEncResponse( e : ResultEvent ) : void {
// decrypt settings string
var o : Object = e.result;
var k:String = pKey; // key to decrypt with
var kdata:ByteArray;
kdata = Hex.toArray(k);
var txt:String = o.settings; // text to decrypt
var data:ByteArray;
data = Hex.toArray(txt);
var pad:IPad = new PKCS5;
var mode:ICipher = Crypto.getCipher("aes-ecb", kdata, pad);
pad.setBlockSize(mode.getBlockSize());
mode.decrypt(data);
currentInput = data;
var decryptedSettings : String = Hex.toString(Hex.fromArray(currentInput));