Recreating a CryptoJS Hmac using python - javascript

The scenario is that I have a JS script that creates a HMAC for a user provided input and I want to compute the same HMAC for the same input using python. To make things clearer, consider the following JS and Python code snippets.
Javascript
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/hmac-sha256.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/enc-base64.min.js"></script>
<script>
var secretAccessKey = "bAvW5O18eSrxke4I7eFcrnrDJkN+wKQmx9aSHuMZQ0w=";
var stringtoSign = "Test";
// Generate HMAC SHA256 signature
var secretAccessKeyBase64 = CryptoJS.enc.Base64.parse(secretAccessKey);
var hash = CryptoJS.HmacSHA256(stringtoSign, secretAccessKeyBase64);
var signature = CryptoJS.enc.Base64.stringify(hash);
</script>
Python
stringToSign = "Test"
secretAccessKey = "bAvW5O18eSrxke4I7eFcrnrDJkN+wKQmx9aSHuMZQ0w="
secretAccessKeyBase64 = base64.b64decode(secretAccessKey).hex()
keyBytes = bytes(secretAccessKeyBase64, 'utf-8')
stringToSignBytes = bytes(stringToSign, 'utf-8')
signatureHash = hmac.new(keyBytes, stringToSignBytes, digestmod=hashlib.sha256).digest()
signature = base64.b64encode(signatureHash)
print(signature)
The Javascript code gives me b+1wRzDODA85vyDZkXByPIKO5qmnjCRNF5gZFi33/Ic=, while python gives me the value b'SsZ4bcYe3op1nGU6bySzlSc9kgg9Kgp37qzF15s2zNc='
Why is my python code generating a different HMAC for (seemingly) identical inputs that was provided to the JS script? Is there anyway to obtain the HMAC value outputted by the JS code using python?

You are using a Base64 encoded value as secret in Javascript, whereas in Python you use the plain text secret.
<script>
var secretAccessKeyBase64 = "secret";
var hash = CryptoJS.HmacSHA256("Message", secretAccessKeyBase64);
var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
document.write(hashInBase64);
</script>
This prints out the same value as the Python code:
qnR8UCqJggD55PohusaBNviGoOJ67HC6Btry4qXLVZc=
Edit:
base64 returns a byte-object there is no need to convert it to hex():
stringToSign = "Test"
secretAccessKey = "bAvW5O18eSrxke4I7eFcrnrDJkN+wKQmx9aSHuMZQ0w="
secretAccessKeyBase64 = base64.b64decode(secretAccessKey)
keyBytes = secretAccessKeyBase64
stringToSignBytes = bytes(stringToSign, 'utf-8')
signatureHash = hmac.new(keyBytes, stringToSignBytes, digestmod=hashlib.sha256).digest()
signature = base64.b64encode(signatureHash)
print(signature)
Prints correctly:
b'b+1wRzDODA85vyDZkXByPIKO5qmnjCRNF5gZFi33/Ic='

Related

Need JS equivalent

Python Code:
signature = hmac.new(bytearray.fromhex(key), data.encode('utf-8'), hashlib.sha256).hexdigest()
Solutions That I have tried
var compute_hmac = crypto.createHmac('sha256', key).update(data).digest('hex');
var compute_hmac = crypto.createHmac('sha256', Buffer.from(key, 'hex').toString()).update(data).digest('hex');
const hmac = crypto.createHmac('sha256', Buffer.from(key, 'hex'))
Trying to validate webhook signatures of the following API
https://developer.close.com/topics/webhooks/
data is the payload received, the same thing is passed to python and JS code. But somehow, hex digest of python code is validated and hex code of JS code is entirely different.
Please refer to API link mentioned above (webhook signatures) to understand what I'm trying to achieve
Pass directly the keybuffer instead of adding .toString() to it
var compute_hmac = crypto.createHmac('sha256', Buffer.from(key, 'hex')).update(data).digest('hex');
py code
import hashlib
import hmac
key ="A1FF92";
data = "hello"
signature = hmac.new(bytearray.fromhex(key), data.encode('utf-8'), hashlib.sha256).hexdigest()
//78a1151ddd4f298a134e4625362af2ab8ef4bd49719e17053ec1eadd4cbf1bab
node code
var crypto = require("crypto")
var key = "A1FF92"
var data="hello";
var compute_hmac = crypto.createHmac('sha256', Buffer.from(key, 'hex')).update(data).digest('hex');
// 78a1151ddd4f298a134e4625362af2ab8ef4bd49719e17053ec1eadd4cbf1bab

Similar Encrypt code in javascript as in C#

I use some remote api, they use such C# code:
SHA256Managed sha256Managed = new SHA256Managed();
byte[] passwordSaltBytes = Encoding.Unicode.GetBytes("zda");
byte[] hash = sha256Managed.ComputeHash(passwordSaltBytes);
string result = Convert.ToBase64String(hash);
Console.WriteLine("result = " + result); // result = NUbWRkT8QfzmDt/2kWaikNOZUXIDt7KKRghv0rTGIp4=
I need to get the same result in my javascript frontend code. Does somebody can help with such problem?
The answer is:
var utf8arr = CryptoJS.enc.Utf16LE.parse("zda");
var hash = CryptoJS.SHA256(utf8arr);
var base64 = CryptoJS.enc.Base64.stringify(hash);
console.log(base64);
Not quite obvious, but Unicode in C# is using UTF-16LE enconding.
So you can use CryptoJS to achieve the same result:
var utf16 = CryptoJS.enc.Utf16LE.parse("zda");
var hash = CryptoJS.SHA256(utf16);
var base64 = CryptoJS.enc.Base64.stringify(hash);
console.log(base64);

Uncaught URIError: URI malformed error with jQuery

I'm using node forge to encrypt a form before sending it to the server using AES.
The code for the crypto part for now is
const bigInt = require("big-integer");
const forge = require('node-forge');
function generateParams() {
// Cryptographic random number generator
var array = new Uint32Array(2);
var _key = bigInt(window.crypto.getRandomValues(array)[0]).toString();
var _iv = bigInt(window.crypto.getRandomValues(array)[1]).toString();
// generate random key and IV
var key = forge.util.encode64(_key);
var iv = forge.util.encode64(_iv);
const params = {
key: key,
iv: iv
}
return params;
}
function encrypt(params) {
var cipher = forge.rc2.createEncryptionCipher(params.key);
cipher.start(params.iv);
// Encrypting "testing"
cipher.update(forge.util.createBuffer("testing"));
cipher.finish();
return cipher.output;
}
function decrypt(params, encrypted) {
var cipher = forge.rc2.createDecryptionCipher(params.key);
cipher.start(params.iv);
cipher.update(encrypted);
cipher.finish();
return cipher.output;
}
and the jQuery function is (not posting yet)
$('#recordForm').submit(function(event) {
// Stop form from submitting normally
event.preventDefault();
// Grab form data
// Crypto
const params = generateParams();
const encryptedForm = {
test: encrypt(params),
}
console.log("Encrypted: " + encryptedForm.test);
const decryptedForm = {
test: decrypt(params, encryptedForm.id).data,
}
console.log("Decrypted: " + decryptedForm.test);
});
My problem is that I keep getting back (cryptob.js is the name of my file, generated with browserify)
Uncaught URIError: URI malformed
at decodeURIComponent (<anonymous>)
at Object.util.decodeUtf8 (cryptob.js:24437)
at ByteStringBuffer.util.ByteStringBuffer.toString (cryptob.js:23490)
at HTMLFormElement.<anonymous> (cryptob.js:1282)
at HTMLFormElement.dispatch (jquery-3.1.1.slim.min.js:3)
at HTMLFormElement.q.handle (jquery-3.1.1.slim.min.js:3)
when calling encrypt().
There is this answer here which recommends including a special meta tag. I have done that but it still doesn't work. Since some resources online say it is related to UTF-8 encoding, I tried replacing
cipher.update(forge.util.createBuffer("testing"));
with
cipher.update(forge.util.createBuffer(encodeURIComponent("testing")));
or
cipher.update(forge.util.createBuffer("testing", 'utf8'));
but it didn't work either (based on encodeURIComponent(str)).
You can test forge here, and if you run this code (which is essentially what I'm doing)
var forge = require("node-forge")
// generate a random key and IV
var key = forge.util.encode64("12354523465");
var iv = forge.util.encode64("2315");
// encrypt some bytes
var cipher = forge.rc2.createEncryptionCipher(key);
cipher.start(iv);
cipher.update(forge.util.createBuffer("testing"));
cipher.finish();
var encrypted = cipher.output;
console.log(encrypted);
// decrypt some bytes
var cipher = forge.rc2.createDecryptionCipher(key);
cipher.start(iv);
cipher.update(encrypted);
cipher.finish();
console.log(cipher.output.data)
it works fine.
How can I solve this?
It looks like this error is actually happening in the toString, where you generate your _key and _iv.
Try testing with some hard-coded strings, as used in the example code you posted. Then, use a method to generate random byte strings for the key and IV.
Also, for AES-256, the key should have 32 bytes (not bits) of entropy. The IV should have 16 bytes of entropy.

hash_hmac() with RAW Binary OUTPUT in JavaScript

I have the php code to generate hash_hmac
key = base64_encode(hash_hmac('sha1',$public_key, $private_key,TRUE));
I've tried the CryptoJS library to solve it.
According to the documentation:
var public_key = 'msg',
private_key = 'key';
var hash = CryptoJS.HmacSHA1(public_key, private_key)
I don't know how to set the Raw Output to Binary like set $raw_output to true in php.
Can anyone help me?
Thanks:)
php code
echo base64_encode(hash_hmac('SHA1', 'shanghai', '0', true).'beijing');
php output
xvBv49PpaYvXAIfy3iOSDWNQj89iZWlqaW5n
node code
var crypto = require('crypto');
var buf1 = crypto.createHmac("sha1", "0").update("shanghai").digest();
var buf2 = Buffer.from('beijing');
console.log(Buffer.concat([buf1, buf2]).toString('base64'));
node output
xvBv49PpaYvXAIfy3iOSDWNQj89iZWlqaW5n

SHA512 not the same in CryptoJS and Closure

I have some troubles with a simple crypto-challenge.
I want to do following:
getting a url-encoded and base64-encoded value
do url-decoding
do base64-decoding
hash with Sha512
When working with CryptoJS, i use following code:
var parameter = "Akuwnm2318kwioasdjlnmn";
var urlDecoded = decodeURIComponent(parameter);
var base64Decoded = CryptoJS.enc.Base64.parse(urlDecoded);
var hashed = CryptoJS.SHA512(base64Decoded).toString(CryptoJS.enc.Base64);
//hashed = "UxupkI5+dkhUorQ+K3+Tqct1WNUkj3I6N76g82CbNQ0EAH/nWjqi9CW5Qec1vq/qakNIYeXeqiAPOVAVkzf9mA=="/eWTS2lUgCEe6NJDXhNfYvXMRQDvH6k2PHVmy6LJS7RloVvcQcpVjRNVU5lJpAg=="
When working with Closure, i use following code:
var parameter = "Akuwnm2318kwioasdjlnmn";
var urlDecoded = decodeURIComponent(parameter);
var byteArray = goog.crypt.base64.decodeStringToByteArray(urlDecoded);
var base64Decoded = goog.crypt.byteArrayToHex(byteArray);
var sha512 = new goog.crypt.Sha512();
sha512.update(base64Decoded);
var hashed = sha512.digest();
hashed = goog.crypt.byteArrayToHex(hashed);
//hashed = "bc2a878edfffb0937fbc6c0f9dbc9566edc59b74080d68d4c8bdfeb4027f17c4316a02285baaf446872d2df37b1144ac3ce18d62ab9c786b1f1fb18a53acea1d"
So, why are the hashes different?
I would be very happy if someone could tell me how to adapt the Closure-Code, to get the same hash as the CryptoJS code provides.
Thanks a lot!
PS:
I also tried:
var parameter = "Akuwnm2318kwioasdjlnmn";
var urlDecoded = decodeURIComponent(parameter);
var base64DecodedByteArray = goog.crypt.base64.decodeStringToByteArray(urlDecoded);
var sha512 = new goog.crypt.Sha512();
sha512.update(base64DecodedByteArray);
var hashed = sha512.digest();
hashed = goog.crypt.byteArrayToHex(hashed);
//hashed = "531ba9908e7e764854a2b43e2b7f93a9cb7558d5248f723a37bea0f3609b350d04007fe75a3aa2f425b941e735beafea6a434861e5deaa200f3950159337fd98"
but then, as you see, i get another hash. why??
The first hash value is identical to the third, except it is base64-encoded rather than hex-encoded. You can change to hex encoding and get the same value:
var hashed = CryptoJS.SHA512(base64Decoded).toString(CryptoJS.enc.Hex);
//hashed = "531ba9908e7e764854a2b43e2b7f93a9cb7558d5248f723a37bea0f3609b350d04007fe75a3aa2f425b941e735beafea6a434861e5deaa200f3950159337fd98"
The second approach you show has a different value because you are not hashing the same data; you are instead converting the byteArray to a hex string and then hashing that string representation, not the underlying values.

Categories