How to SHA-256 hash text in a chrome extension with SubtleCrypto - javascript

I am hashing a text field with Subtle Crypto and getting an [object ArrayBuffer].
The relevant code is here:
async function asyncCall() {
var enc = new TextEncoder(); // always utf-8
var enc2 = new TextDecoder(); // always utf-8
var digest3 = enc.encode(localStorage.getItem("Item 1"));
const digest2 = await crypto.subtle.digest("SHA-256", (digest3));
localStorage.setItem("Item Hashed", (digest2));
field2.value = localStorage.getItem("Item Hashed");
};
When I hash any text ("Item 1" is localStorage text that is defined with a text field) with this, I get [object ArrayBuffer] as the result. Why am I not getting something that looks like a SHA256 hash?

Convert ArrayBuffer to a Hex string:
const hashArray = Array.from(new Uint8Array(digest2)); // convert buffer to byte array
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#converting_a_digest_to_a_hex_string

Related

Conversion from buffer to string gives different results in c# and Nodejs

I'm trying to convert this function from C# to node but I'm getting different results when I try to convert a buffer to a string.
string str = "34924979";
System.Security.Cryptography.SHA512 sha = new System.Security.Cryptography.SHA512Managed();
byte[] ba = System.Text.Encoding.ASCII.GetBytes(str);
byte[] data= sha.ComputeHash(ba);
Console.WriteLine(System.Text.Encoding.ASCII.GetString(data));
>> `?????gV)????∟?Z?s??v$We-??N?"?w????0??\i♠G???
that's what i'm trying to do.
const crypto = require('crypto')
const str = '34924979';
const sha = crypto.createHash('sha512').update(str).digest('utf8');
const hash = sha.toString('utf-8').replace(/\uFFFD/g, '?');
console.log(hash);
>> `????gV)????∟?Z?v$We-??N?"?w????0?\i♠Gߕ?
You're using different encodings in C# and JS.
Try changing the JS code to the following:
const sha = crypto.createHash('sha512').update(str).digest('ascii');
const hash = sha.toString('ascii');

How to convert a bit string to base64 in node.js?

I have a bit string that I want to convert to base64 but it doesn't look like there's a native function to do this and I couldn't find a node module either. ):
Input: 100110110101000110100011011001100010110100011011001100100110100011000001100000110000011000001100001001010100111110000011001111100101010011111010011100010110001001001001100000110100111010010100111110000111001000100000110001001000101100111110011001001001101011010001011001001101001010000011000100100100110000011010011
Output: base64 representation of that equivalent binary value
Maybe a better question is how to convert a bit string into a buffer? Not sure
As you guessed, the main thing is converting the string into something easier to convert to base64 and then converting that to base64.
In the code below, we do these conversion sequences:
bit string -> BigInt -> array of byte-sized ints -> binary string -> base64
base64 -> binary string -> array of byte-sized bit strings -> bit string
const encode = bitstr => {
const bytes = [];
// convert bit string to BigInt
let value = BigInt('0b' + bitstr);
// chop it up into bytes
while (value > 0n) {
bytes.unshift(Number(value & 0xffn));
value >>= 8n;
}
// convert to binary string and encode as base64
return btoa(String.fromCharCode.apply(null, bytes));
};
const decode = b64 => {
// decode base64 to binary string
const bstr = atob(b64);
// convert binary string to bit string
return new Array(bstr.length).fill(0).map(
(_,i) => bstr.charCodeAt(i).toString(2).padStart(8, i ? '0' : '')
).join('');
};
const bitstr = '100110110101000110100011011001100010110100011011001100100110100011000001100000110000011000001100001001010100111110000011001111100101010011111010011100010110001001001001100000110100111010010100111110000111001000100000110001001000101100111110011001001001101011010001011001001101001010000011000100100100110000011010011';
const encoded = encode(bitstr);
const decoded = decode(encoded);
console.log(bitstr);
console.log(encoded);
console.log(decoded);

Confused on how to implement HMACSHA1 in C# .NET coming from javascript

I am using the crypto-js library to implement the HMACSHA1 for my javascript code
the code looks like this
const hash1 = require("crypto-js");
let signature = "application_id=3610&auth_key=aDRceQyTXSYEdJU&nonce=6304033672&timestamp=1623098533&user[login]=john#mail.com&user[password]=123456789"
let key = "dBV2PdhYMnruSMb"
let hash = hash1.HmacSHA1(signature, key).toString()
console.log(hash)
//which prints
467280c4cb82fc97bd04c51d8a846446ad6e82e1
this obviously is pretty easy in javascript. But then I tried using the same exact string and key in c# and it prints out a completely different string. I am lost and don't know how to solve this issue.
Here is my attempt to implement this in C#
string signSession = "application_id=3610&auth_key=aDRceQyTXSYEdJU&nonce=6304033672&timestamp=1623098533&user[login]=john#mail.com&user[password]=123456789"
string key = "dBV2PdhYMnruSMb="
//convert the session signature string to a byte array
byte[] signature = Encoding.UTF8.GetBytes(signSession.ToString());
var apiKey = Convert.FromBase64String(key);
//Generate a HMACSHA1 signature
using(HMACSHA1 hmac = new HMACSHA1(apiKey))
{
byte[] signatureBytes = hmac.ComputeHash(signature);
string base64Signature = Convert.ToBase64String(signatureBytes);
Console.WriteLine(base64Signature);
session.Signature = base64Signature;
}
//And this prints
sztTnSTv2xvuA7pPXxk2cKMP0Eo=
//which is wrong. It should be the same as the javascript result
Im not sure what im doing wrong here and is my C# implementation right?
Your key is different. While crypto-js expects a string, C# expects a byte array. You shouldn't use FromBase64String() but Encoding.UTF8.GetBytes(). As #jps mentioned in the comment
Of course it's different, your JS implementation has a hex encoded
output but in your C# implementation you're base64 encoding the
result.
you should convert the byte-array to a hex-string like so
string signSession = "application_id=3610&auth_key=aDRceQyTXSYEdJU&nonce=6304033672&timestamp=1623098533&user[login]=john#mail.com&user[password]=123456789";
string key = "dBV2PdhYMnruSMb";
//convert the session signature string to a byte array
byte[] signature = Encoding.UTF8.GetBytes(signSession);
var apiKey = Encoding.UTF8.GetBytes(key);
//Generate a HMACSHA1 signature
using (HMACSHA1 hmac = new HMACSHA1(apiKey))
{
byte[] signatureBytes = hmac.ComputeHash(signature);
string hexSignature = BitConverter.ToString(signatureBytes).ToLowerInvariant().Replace("-", "");
Console.WriteLine(hexSignature);
session.Signature = hexSignature;
}

How to remove "unneeded" bytes of a ciphertext?

I am using node-js and crypto library to encrypt/decrypt my strings with AES-128 encryption. But I have a problem, that's the pads to make a string multiple of 16 bytes. How can I remove those bytes? Because I can't use decrypted_string == "actual string" (because of the unneeded bytes at end of the string) in javascript. Here are the functions which I use to encrypt / decrypt strings in javascript:
const crypto = require("crypto");
function encrypt(str, key) {
let input = Buffer.from(str, "utf8");
let token = Buffer.from(key);
cipher = crypto.createCipheriv("aes-128-ecb", token, '');
result = cipher.update(input).toString("base64");
result += cipher.final().toString("base64");
return result;
}
function decrypt(str, key) {
let input = Buffer.from(str, "base64");
let token = Buffer.from(key);
decipher = crypto.createDecipheriv("aes-128-ecb", token, '');
decipher.setAutoPadding(false);
result = decipher.update(input).toString("utf8");
result += decipher.final().toString("utf8");
return result;
}

How to pass a binary string to `btoa` to encode the raw value

I need to modify this (working) code and replace CryptoJS functions with native JS.
The variable b64encoded has the correct value, but my attempt with native code (encodedIncorrect) is not. Can you help feed btoa the correct input?
Working, but 3rd party libs cannot be used on the platform I'm developing for:
const encrypted = CryptoJS.HmacSHA1("message", "secret");
// encrypted = "0caf649feee4953d87bf903ac1176c45e028df16" << Hexadecimal encoded string
const b64encoded = CryptoJS.enc.Base64.stringify(encrypted);
// b64encoded = "DK9kn+7klT2Hv5A6wRdsReAo3xY="
Not working, specifically the encoding of a binary string by btoa:
const encrypted_vendor = VENDOR.hmacSHA1("message", "secret") // Vendor-provided HMAC-SHA1 func
// encrypted_vendor = "0caf649feee4953d87bf903ac1176c45e028df16"
// Split hex string into two-character byte representations for binary conversion
function splitHex(hexString) {
let chunks = [];
for (var i = 0, charsLength = hexString.length; i < charsLength; i += 2) {
chunks.push(hexString.substring(i, i + 2));
}
}
// Convert hex characters to binary value
function hexCharToBinary(value) {
return (parseInt(value, 16).toString(2)).padStart(8, '0');
}
binaryChunks = splitHex(encrypted)
var binaryStr = ""
binaryChunks.forEach(i => binaryStr += hexCharToBinary(i))
// binaryStr = "0000110010101111011001001001111111101110111001001001010100111101100001111011111110010000001110101100000100010111011011000100010111100000001010001101111100010110"
encodedIncorrect = btoa(binaryStr)
// encodedIncorrect = "MDAwMDExMDAxMDEwMTExMTAxMTAwMTAwMTAwMTExMTExMTEwMTExMDExMTAwMTAwMTAwMTAxMDEwMDExMTEwMTEwMDAwMTExMTAxMTExMTExMDAxMDAwMDAwMTExMDEwMTEwMDAwMDEwMDAxMDExMTAxMTAxMTAwMDEwMDAxMDExMTEwMDAwMDAwMTAxMDAwMTEwMTExMTEwMDAxMDExMA=="
I've also verified that the binary output is correct using this site (https://cryptii.com/pipes/binary-to-base64), which encodes it to the correct base64 when set to binary input.
You are encoding the string representation of the binary digits, not the actual data.
You need a binary string, which you can construct e.g. using String.fromCharCode:
const hexString = "0caf649feee4953d87bf903ac1176c45e028df16"
// split into two character groups (one byte each), see e.g.
// https://stackoverflow.com/questions/6259515/how-can-i-split-a-string-into-segments-of-n-characters
// using /.{1,2}/g may be preferrable for potential error handling
// but if there is always an even amount of characters, it doesn't matter.
let hexCharacterPairs = hexString.match(/../g);
// parse two hex characters as number
let bytes = hexCharacterPairs.map(e => parseInt(e, 16));
// generate symbols from the bytes
let binaryStringSymbols = bytes.map(e => String.fromCharCode(e));
// get a string from all the symbols
let binaryString = binaryStringSymbols.join("");
// use the binary string to get a base64 encode
let base64 = btoa(binaryString);
console.log(base64);
// or in one line
console.log(btoa("0caf649feee4953d87bf903ac1176c45e028df16".match(/../g).map(e => String.fromCharCode(parseInt(e, 16))).join("")));
Note, that the above snippet has no error handling, e.g. for odd numbers of characters in the input, invalid characters, etc.

Categories

Resources