How to generate HMAC-SHA-256 sign in javascript for datatrans? - javascript

I have an Angular project in which I have to implement datatrans payment. But I am not able to generate sign for payment.
I am following process given on this link (enter link description here) to generate sign.
But i am not able to achive it.
I am using angular library crypto-js to generate HMAC-SHA-256 signed string.
Here is my javascript code.
const merchantId = 'xxxxxxx';
const refNo = '1234567890';
const amount = 0;
const currency = 'CHF';
const theme = 'DT2015';
const paymentmethod = 'VIS';
const stringSs = merchantId+amount+currency+refNo;
const base = 16;
// My Hmac Key
const s = 'fa3d0ea1772cf21e53158283e4f123ebf1eb1ccfb15619e2fc91ee6860a2e5e48409e902b610ce5dc6f7f77fab8affb60d69b2a7aa9acf56723d868d36ab3f32';
// Step 1: Code to generate hex to byte of hmac key
const a = s.replace(/../g, '$&_').slice (0, -1).split ('_').map ((x) => parseInt (x, base));
// Step 3: Sign the string with HMAC-SHA-256 together with your HMAC key
const signedString = HmacSHA256(a, stringSs);
// Step 4: Translate the signature from byte to hex format
const signString = enc.Hex.stringify(signedString);
Can you help me into this to suggest what i am doing wrong or in what way it can be achieved.

You can do it with crypto (no need of extra libraries to install)
// Typescript
import * as crypto from 'crypto';
function signKey (clientKey: string, msg: string) {
const key = new Buffer(clientKey, 'hex');
return crypto.createHmac('sha256', key).update(msg).digest('hex');
}
// Javascript
const crypto = require('crypto')
function signKey (clientKey, msg) {
const key = new Buffer(clientKey, 'hex');
return crypto.createHmac('sha256', key).update(msg).digest('hex');
}
signKey(s, stringSs)

To answer the question for crypto-js (see https://github.com/brix/crypto-js) as requested, the following will do the trick:
// Javascript; example from datatrans documentation using a random key
stringSs ='3000017692850CHF91827364';
key='1ca12d7c0629194a9f9d0dbbc957709dd3aed385925b077e726813f0b452de6a38256abd1116138d21754cfb33964b6b1aaa375b74d3580fcda916898f553c92';
expectedSign='d7dee9ae1e542bc02bcb063a3dd3673871b2e43ccb4c230f26e8b85d14e25901';
signedString = CryptoJS.HmacSHA256(stringSs, CryptoJS.enc.Hex.parse(key));
resultSign = CryptoJS.enc.Hex.stringify(signedString);
// now resultSign == expectedSign is true :-)
Ninja Turtles approach was almost correct except of step 1, hex to byte. Use a builtin function of Crypto-JS instead and everything works as expected.

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

Ruby and other languages(Python, JavaScript) different hmac sha256 result

I have Ruby 2.5.3 code that create hmac with sha256
require 'openssl'
key = '4629de5def93d6a2abea6afa9bd5476d9c6cbc04223f9a2f7e517b535dde3e25'
message = 'lucas'
hash = OpenSSL::HMAC.hexdigest('sha256', key, message)
hash => ba2e2505c6f302fb3c40bea4491d95bacd96c3d12e8fbe50197ca431165fcee2
But the result is different from Python & JavaScript code. What should I do in Ruby code to have the same result as from the others?
Python
import hmac
import hashlib
import binascii
message = "lucas"
key = "4629de5def93d6a2abea6afa9bd5476d9c6cbc04223f9a2f7e517b535dde3e25"
hash = hmac.new(
binascii.unhexlify(bytearray(key, "utf-8")),
msg=message.encode('utf-8'),
digestmod=hashlib.sha256
).hexdigest()
hash => 99427c7bba36a6902c5fd6383f2fb0214d19b81023296b4bd6b9e024836afea2
JavaScript
const crypto = require('crypto');
const message = 'lucas';
const key = '4629de5def93d6a2abea6afa9bd5476d9c6cbc04223f9a2f7e517b535dde3e25';
const hash = crypto.createHmac('sha256', Buffer.from(key, 'hex'))
.update(message)
.digest('hex');
hash => 99427c7bba36a6902c5fd6383f2fb0214d19b81023296b4bd6b9e024836afea2
In Python and JS you are using the "key" as hexstring, means that the hexstring is converted to a binary format. In Ruby the key is used without conversion. – Michael Fehr
This gave me the solution.
key = '4629de5def93d6a2abea6afa9bd5476d9c6cbc04223f9a2f7e517b535dde3e25'
message = 'lucas'
puts OpenSSL::HMAC.hexdigest('sha256', [key].pack('H*') , message)
hash => 99427c7bba36a6902c5fd6383f2fb0214d19b81023296b4bd6b9e024836afea2
thank you!

Using Node.js 'jsrsasign' library to verify signature generated by .NET Bouncy Castle library

I'm generating signatures in C# using the Bouncy Castle library as follows:
var privateKeyBase64 = "MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgg8/MbvGGTDMDpfje8lQBZ8st+l3SK7jRl7OWlyUl/VagCgYIKoZIzj0DAQehRANCAARkQIUpkKbxmJJicvG450JH900JjmJOGdlMCZl3BIXvPBBKkaTMsQc6l3O4vJA6Yc23nr3Ox/KwFUl6gdo5iTqV";
var publicKeyBase64 = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZECFKZCm8ZiSYnLxuOdCR/dNCY5iThnZTAmZdwSF7zwQSpGkzLEHOpdzuLyQOmHNt569zsfysBVJeoHaOYk6lQ==";
var plainText = "aaa";
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
// Sign
var privateKey = PrivateKeyFactory.CreateKey(Convert.FromBase64String(privateKeyBase64));
var signer = SignerUtilities.GetSigner(X9ObjectIdentifiers.ECDsaWithSha512.Id);
signer.Init(true, privateKey);
signer.BlockUpdate(plainTextBytes, 0, plainTextBytes.Length);
var signature = signer.GenerateSignature();
var signatureBase64 = Convert.ToBase64String(signature);
Console.WriteLine("Signature base64: {0}", signatureBase64);
// Verify
Console.WriteLine("-------------------- Verifying signature ");
Console.WriteLine("Public key base64: {0}", publicKeyBase64);
var publicKey = PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKeyBase64));
var verifier = SignerUtilities.GetSigner(X9ObjectIdentifiers.ECDsaWithSha512.Id);
verifier.Init(false, publicKey);
verifier.BlockUpdate(plainTextBytes, 0, plainTextBytes.Length);
Console.WriteLine("Signature valid?: {0}", verifier.VerifySignature(Convert.FromBase64String(signatureBase64)));
// Prints: MEUCIBEcfv2o3UwqwV72CVuYi7HbjcoiuSQOULY5d+DuGt3UAiEAtoNrdNWvjfdz/vR6nPiD+RveKN5znBtYaIrRDp2K7Ks=
On the node.js app, I'm using jsrsasign to verify the generated signature on same payload as follows:
let rs = require('jsrsasign');
let pem = `-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZECFKZCm8ZiSYnLxuOdCR/dNCY5iThnZTAmZdwSF7zwQSpGkzLEHOpdzuLyQOmHNt569zsfysBVJeoHaOYk6lQ== -----END PUBLIC KEY-----`;
let plainText = 'aaa';
let signature = 'MEUCIBEcfv2o3UwqwV72CVuYi7HbjcoiuSQOULY5d+DuGt3UAiEAtoNrdNWvjfdz/vR6nPiD+RveKN5znBtYaIrRDp2K7Ks=';
let signatureHex = Buffer.from(signature, 'base64').toString('hex');
var sig = new rs.Signature({alg: 'SHA512withECDSA'});
sig.init(pem);
sig.updateString(plainText);
var isValid = sig.verify(signatureHex);
console.log('Is signature valid: ', isValid); // <--- returns false always!
I'd be grateful if you could assist me in identifying what the issue could be.
I'd also accept suggestions for other Node.js libraries that can validate signatures generated using ECDSA with SHA512.
This is very likely a bug in the jsrsasign library where it generates wrong ECDSA signatures with hash functions that have an output larger than the bit length of n in bits. Awaiting the answer of the author, see more details here https://github.com/kjur/jsrsasign/issues/394.
Using another package elliptic curves package and generating + truncating the hash of the payload manually, I was able to verify that the signatures generated by Bouncy Castle in C# are valid:
let elliptic = <any>window.require('elliptic');
let hash = <any>window.require('hash.js')
let ec = new elliptic.ec('p256');
// Same key from my original post, just hex encoded
let keyPair = ec.keyFromPrivate("83CFCC6EF1864C3303A5F8DEF2540167CB2DFA5DD22BB8D197B396972525FD56");
let pubKey = keyPair.getPublic();
// The first 32 bytes (256 bits) of the SHA521 hash of the payload "aaa"
// sha512('aaa') => d6f644b19812e97b5d871658d6d3400ecd4787faeb9b8990c1e7608288664be77257104a58d033bcf1a0e0945ff06468ebe53e2dff36e248424c7273117dac09
let msgHash = 'd6f644b19812e97b5d871658d6d3400ecd4787faeb9b8990c1e7608288664be7'
// Same signature from original post above
let signatureBase64 = 'MEUCIBEcfv2o3UwqwV72CVuYi7HbjcoiuSQOULY5d+DuGt3UAiEAtoNrdNWvjfdz/vR6nPiD+RveKN5znBtYaIrRDp2K7Ks='
let signatureHex = Buffer.from(signatureBase64, 'base64').toString('hex');
let validSig = ec.verify(msgHash, signatureHex, pubKey);
console.log("Signature valid?", validSig); // <------- prints TRUE

Python bytestring => Javascript?

I'm attempting to port some Python code to Javascript. Here is the Python code:
# Python
import codecs
from Crypto.Cipher import AES
key = b"\xc3\x99\xff\xff\xc3\x99\xff\xff\xc3\x99\xff\xff\xc3\x99\xff\xff"
...
aes = AES.new(key, AES.MODE_ECB)
token = aes.encrypt("HELLO\x00\x00".encode("utf-8"))
token_hex = codecs.encode(token, "hex").decode("utf-8")
I'm not exactly sure how to port my Python key variable. Should it be UInt16Array...or a string?
This is my Javascript so far:
// Javascript
const crypto = require('crypto');
const key = '???' // <-- This is one place I am stuck. String? Byte array?
....
const cipher = crypto.createCipher('aes-128-ecb', key);
let tokenHex = cipher.update('HELLO\x00\x00', 'utf8', 'hex');
tokenHex = tokenHex.toString('utf8')
I appreciate any insight you can provide as to how I can get a matching tokenHex in Javascript.
Thank you!
What you are after is Buffer, which represents a collection of bytes.
You probably want to instantiate the key variable similar to this:
let key = Buffer.from("c399ff...", "hex");

How to compute an SHA256 hash and Base64 String encoding in JavaScript/Node

I am trying to recreate the following C# code in JavaScript.
SHA256 myHash = new SHA256Managed();
Byte[] inputBytes = Encoding.ASCII.GetBytes("test");
myHash.ComputeHash(inputBytes);
return Convert.ToBase64String(myHash.Hash);
this code returns "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="
This is what I have so far for my JavaScript code
var sha256 = require('js-sha256').sha256;
var Base64 = require('js-base64').Base64;
var sha256sig = sha256("test");
return Base64.encode(sha256sig);
the JS code returns "OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA=="
These are the 2 JS libraries that I have used
js-sha256
js-base64
Does anybody know how to make it work ? Am I using the wrong libs ?
You don't need any libraries to use cryptographic functions in NodeJS.
const crypto = require('crypto');
const hash = crypto.createHash('sha256')
.update('test')
.digest('base64');
console.log(hash); // n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=
If your target user using modern browser such as chrome and edge, just use browser Crypto API:
const text = 'hello';
async function digestMessage(message) {
const msgUint8 = new TextEncoder().encode(message); // encode as (utf-8) Uint8Array
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8); // hash the message
const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
return hashHex;
}
const result = await digestMessage(text);
console.log(result)
Then you could verify the result via online sha256 tool.

Categories