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

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!

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

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

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.

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");

CryptoJS to Pycrypto AES [duplicate]

I have a password which is encrypt from JavaScript via
var password = 'sample'
var passphrase ='sample_passphrase'
CryptoJS.AES.encrypt(password, passphrase)
Then I tried to decrypt the password comes from JavaScript in Python:
from Crypto.Cipher import AES
import base64
PADDING = '\0'
pad_it = lambda s: s+(16 - len(s)%16)*PADDING
key = 'sample_passphrase'
iv='11.0.0.101' #------> here is my question, how can I get this iv to restore password, what should I put here?
key=pad_it(key) #------> should I add padding to keys and iv?
iv=pad_it(iv) ##
source = 'sample'
generator = AES.new(key, AES.MODE_CFB,iv)
crypt = generator.encrypt(pad_it(source))
cryptedStr = base64.b64encode(crypt)
print cryptedStr
generator = AES.new(key, AES.MODE_CBC,iv)
recovery = generator.decrypt(crypt)
print recovery.rstrip(PADDING)
I checked JS from browser console, it shows IV in CryptoJS.AES.encrypt(password, passphrase) is a object with some attributes( like sigBytes:16, words: [-44073646, -1300128421, 1939444916, 881316061]). It seems generated randomly.
From one web page, it tells me that JS has two way to encrypt password
(reference link ):
a. crypto.createCipher(algorithm, password)
b. crypto.createCipheriv(algorithm, key, iv)
What I saw in JavaScript should be option a. However, only option b is equivalent to AES.new() in python.
The questions are:
How can I restore this password in Python without changing JavaScript code?
If I need IV in Python, how can I get it from the password that is used in JavaScript?
You will have to implement OpenSSL's EVP_BytesToKey, because that is what CryptoJS uses to derive the key and IV from the provided password, but pyCrypto only supports the key+IV type encryption. CryptoJS also generates a random salt which also must be send to the server. If the ciphertext object is converted to a string, then it uses automatically an OpenSSL-compatible format which includes the random salt.
var data = "Some semi-long text for testing";
var password = "some password";
var ctObj = CryptoJS.AES.encrypt(data, password);
var ctStr = ctObj.toString();
out.innerHTML = ctStr;
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>
<div id="out"></div>
Possible output:
U2FsdGVkX1+ATH716DgsfPGjzmvhr+7+pzYfUzR+25u0D7Z5Lw04IJ+LmvPXJMpz
CryptoJS defaults to 256 bit key size for AES, PKCS#7 padding and CBC mode. AES has a 128 bit block size which is also the IV size. This means that we have to request 32+16 = 48 byte from EVP_BytesToKey. I've found a semi-functional implementation here and extended it further.
Here is the full Python (tested with 2.7 and 3.4) code, which is compatible with CryptoJS:
from Cryptodome import Random
from Cryptodome.Cipher import AES
import base64
from hashlib import md5
BLOCK_SIZE = 16
def pad(data):
length = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
return data + (chr(length)*length).encode()
def unpad(data):
return data[:-(data[-1] if type(data[-1]) == int else ord(data[-1]))]
def bytes_to_key(data, salt, output=48):
# extended from https://gist.github.com/gsakkis/4546068
assert len(salt) == 8, len(salt)
data += salt
key = md5(data).digest()
final_key = key
while len(final_key) < output:
key = md5(key + data).digest()
final_key += key
return final_key[:output]
def encrypt(message, passphrase):
salt = Random.new().read(8)
key_iv = bytes_to_key(passphrase, salt, 32+16)
key = key_iv[:32]
iv = key_iv[32:]
aes = AES.new(key, AES.MODE_CBC, iv)
return base64.b64encode(b"Salted__" + salt + aes.encrypt(pad(message)))
def decrypt(encrypted, passphrase):
encrypted = base64.b64decode(encrypted)
assert encrypted[0:8] == b"Salted__"
salt = encrypted[8:16]
key_iv = bytes_to_key(passphrase, salt, 32+16)
key = key_iv[:32]
iv = key_iv[32:]
aes = AES.new(key, AES.MODE_CBC, iv)
return unpad(aes.decrypt(encrypted[16:]))
password = "some password".encode()
ct_b64 = "U2FsdGVkX1+ATH716DgsfPGjzmvhr+7+pzYfUzR+25u0D7Z5Lw04IJ+LmvPXJMpz"
pt = decrypt(ct_b64, password)
print("pt", pt)
print("pt", decrypt(encrypt(pt, password), password))
Similar code can be found in my answers for Java and PHP.
JavaScript AES encryption in the browser without HTTPS is simple obfuscation and does not provide any real security, because the key must be transmitted alongside the ciphertext.
[UPDATE]:
You should use pycryptodome instead of pycrypto because pycrypto(latest pypi version is 2.6.1) no longer maintained and it has vulnerabilities CVE-2013-7459 and CVE-2018-6594 (CVE warning reported by github). I choose pycryptodomex package here(Cryptodome replace Crypto in code) instead of pycryptodome package to avoid conflict name with Crypto from pycrypto package.

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