I'm trying to build an equivalent version of SHA256 ComputeHash (from C#, the EXACT same output from the below sample), to React Native/JavaScript. This is the following C#:
public static string Hash(string input)
{
if (string.IsNullOrWhiteSpace(input)) return "";
using (SHA256 hasher = SHA256.Create())
{
// Convert the input string to a byte array and compute the hash.
byte[] data = hasher.ComputeHash(Encoding.Unicode.GetBytes(input));
// Create a new Stringbuilder to collect the bytes
// and create a string.
StringBuilder sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("X2"));
}
// Return the hexadecimal string.
return $"0x{sBuilder.ToString().ToLower()}";
}
}
I tried the following, but it doesn't generate the same Hash:
import * as Crypto from 'expo-crypto';
const hash = await Crypto.digestStringAsync(
Crypto.CryptoDigestAlgorithm.SHA256,
"StringIWantToHash"
);
Would anyone know, either what's wrong with the JavaScript, or if there is an exact equivalent version of the C# one?
Solution 1: install sha256 by
yarn add sha256
import React, { Component } from "react";
import { Text, StyleSheet, View } from "react-native";
const sha256 = require("sha256");
const isNullOrWhitespace = (input) => {
if (typeof input === "undefined" || input == null) return true;
return input.replace(/\s/g, "").length < 1;
};
const getByteArray = (input) => {
let bytes = [];
for (var i = 0, k = 0; i < input.length; i++, k += 2) {
bytes[k] = input.charCodeAt(i);
bytes[k + 1] = 0;
}
return bytes;
};
const hash = async (input) => {
if (isNullOrWhitespace(input)) {
return "";
}
var bytes = getByteArray(input);
const hashString = "0x" + sha256(bytes, { asBytes: false });
return hashString;
};
export default class App extends Component {
state = {
encodedString: "",
};
async UNSAFE_componentWillMount() {
const encodedString = await hash("test2"); //0x39a2272982dc7e6e5d109ab36ec280f6cd3b4b7440af5c739ed808d4ec02aae4
this.setState({ encodedString: encodedString });
}
render() {
const { encodedString } = this.state;
return (
<View style={styles.container}>
<Text>{encodedString}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
});
Solution 2:
you are using ToString("X2")
in c# which means you have to convert hash to HEX (base 16)
here id demo: https://snack.expo.io/@nomi9995/expo-crypto
you need to convert hash to HEX (base 16) like this
await hash.toString(16);
try this it will give the same result as c#
Code :
import React, { Component } from "react";
import { Text, StyleSheet, View } from "react-native";
import * as Crypto from "expo-crypto";
const isNullOrWhitespace = (input) => {
if (typeof input === "undefined" || input == null) return true;
return input.replace(/\s/g, "").length < 1;
};
const hash = async (input) => {
if (isNullOrWhitespace(input)) {
return "";
}
let hash = await Crypto.digestStringAsync(
Crypto.CryptoDigestAlgorithm.SHA256,
input
);
const sBuilder = await hash.toString(16);
return `0x${sBuilder.toLowerCase()}`;
};
export default class App extends Component {
state = {
encodedString: "",
};
async UNSAFE_componentWillMount() {
const result = await hash("StringIWantToHash"); //here you can pass your string
this.setState({ encodedString: result });
}
render() {
const { encodedString } = this.state;
return (
<View style={styles.container}>
<Text>{encodedString}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
});
install crypto-js by
yarn add crypto-js
try this it will give the same result as c#
var CryptoJS = require("crypto-js");
const isNullOrWhitespace = (input) => {
if (typeof input === "undefined" || input == null) return true;
return input.replace(/\s/g, "").length < 1;
};
const hash = (input) => {
if (isNullOrWhitespace(input)) {
return "";
}
let hash = CryptoJS.SHA256(input);
const sBuilder=hash.toString(CryptoJS.enc.Hex);
return `0x${sBuilder.toLowerCase()}`;
};
const result=hash("StringIWantToHash");
console.log(result,"result"); // it will give the same result as C#
Well, this is an Encoding problem.
Encoding.Unicode
is Microsoft's misleading name for UTF-16 (a double-wide encoding, used in the Windows world for historical reasons but not used by anyone else). http://msdn.microsoft.com/en-us/library/system.text.encoding.unicode.aspx (see this answer)
You should be using Encoding.UTF8.GetBytes
instead.
Using js-sha256 library like this:
const jssha = require('js-sha256')
function hash(input)
{
const hashString = "0x" + jssha.sha256(input)
return hashString;
}
const hashResult = hash("StringIWantToHash")
// Output: 0x29c506d0d69a16e413d63921b7de79525c43715931d8d93127dbeb46eacda2f9
We can achieve pretty similar in C# just using UTF8 encoding:
public static string Hash(string input)
{
if (string.IsNullOrWhiteSpace(input)) return "";
using (SHA256 hasher = SHA256.Create())
{
// Convert the input string to a byte array and compute the hash.
byte[] data = hasher.ComputeHash(Encoding.UTF8.GetBytes(input)); // Note that UTF8 here
// Create a new Stringbuilder to collect the bytes
// and create a string.
StringBuilder sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("X2"));
}
// Return the hexadecimal string.
return $"0x{sBuilder.ToString().ToLower()}";
}
}
static void Main()
{
var hashResult = Hash("StringIWantToHash");
// Output: 0x29c506d0d69a16e413d63921b7de79525c43715931d8d93127dbeb46eacda2f9
}
Also, I believe that other JS/React Native libraries that help to compute SHA256 hash use UTF8 encoding too, so I think you can use any other crypto libraries for that.
In that case, you need to manually represent C# encoding in JS code.
While you are using Unicode encoding, after every single byte in the string goes '0' byte if it is a plain Latin character. For other symbols (with more than 255 number) Unicode required 2 bytes for that.
var input = "StringIWantToHash";
var encodedInput = Encoding.Unicode.GetBytes(input);
// Output: [83, 0, 116, 0, 114, 0, 105, 0, 110, 0, ...]
So we need to represent that in our JS code:
const jssha = require('js-sha256')
function hash(input)
{
var bytes = [];
for (var i = 0; i < input.length; i++)
{
const code = input.charCodeAt(i);
bytes = bytes.concat([code & 0xff, code / 256 >>> 0]);
}
const hashString = "0x" + jssha.sha256(bytes)
return hashString;
}
const hashResult = hash("StringIWantToHash")
// Output: 0x029dbc4b54b39bed6d684175b2d76cc5622c60fe91f0bde9865b977d0d9a531d
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With