6

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?

5
  • Try this library? code.google.com/archive/p/crypto-js and also see by using hmacSha256 also Commented Jun 2, 2020 at 4:38
  • So are you saying that its generating different results for same input? Commented Jun 2, 2020 at 8:20
  • @Raj Yep, indeed correct. Commented Jun 2, 2020 at 14:42
  • ok. Did you try converting it to Base64? Commented Jun 2, 2020 at 14:46
  • @Raj I don't see why that would make any difference, that would only make the result worse. I just tried :) Commented Jun 2, 2020 at 14:51

3 Answers 3

3

Expo React Native:

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

Node js:

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#

enter image description here

Sign up to request clarification or add additional context in comments.

8 Comments

Here is an example of me trying it out with some input. Input: test2 React Native/Expo returns: 0x60303ae22b998861bce3b28f33eec1be758a213c86c93c076dbe9f558c11c752 C# returns: 0x39a2272982dc7e6e5d109ab36ec280f6cd3b4b7440af5c739ed808d4ec02aae4 The Correct one is C# as that's the one we're using in our Database too.
What could be causing this?
can you post input string here so I can identify which is the actual problem?
The input I use is "test2" without the " "
@Muhammed Numan Sorry to hear :/ But I still do appreciate your solutions! Without people like you, questions like these would never be answered ^_^
|
2
+500

Solution 1: Use UTF8 instead of Unicode

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.

Solution 2: What if you need to use Unicode?

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

11 Comments

I don't know what is wrong, but for me "StringIWantToHash" is always "0x029dbc4b54b39bed6d684175b2d76cc5622c60fe91f0bde9865b977d0d9a531d" on my end, while the JS is "0x29c506d0d69a16e413d63921b7de79525c43715931d8d93127dbeb46eacda2f9". The C# one "0x029dbc4b54b39bed6d684175b2d76cc5622c60fe91f0bde9865b977d0d9a531d" IS the CORRECT one. I wonder what is going wrong. Hmm
@KevinJensenPetersen I get 0x029dbc4b54b39bed6d684175b2d76cc5622c60fe91f0bde9865b977d0d9a531d too if I use Encoding.Unicode.GetBytes. If I use Encoding.UTF8.GetBytes I have this hash: 0x29c506d0d69a16e413d63921b7de79525c43715931d8d93127dbeb46eacda2f9 The hash that generated by using Unicode isn't correct. Use UTF8 hash instead.
I cannot use UTF8 since our Database is dependent on it being Unicode I guess. Since the correct one is 0x029dbc4b54b39bed6d684175b2d76cc5622c60fe91f0bde9865b977d0d9a531d (The Database handles THAT format)
@KevinJensenPetersen you should mention that in a question, wait some time, I already have a solution for you.
I mentioned that it would have to be the exact same as my C# sample (which uses Unicode) :)
|
0

after two days of research it works perfectly! Two different codes give the same result.

js

const sha1 = require('sha1');    
const getHash = str =>{
    const hashingBytes = Buffer.from(sha1(str), "hex");
    const base64Value = Buffer.from(hashingBytes).toString('base64');
    return base64Value;
}

c#

 System.Security.Cryptography.SHA1 sha = new System.Security.Cryptography.SHA1CryptoServiceProvider();
        byte[] bytes = System.Text.Encoding.ASCII.GetBytes(str);
        byte[] hashingbytes = sha.ComputeHash(bytes);
        var hash = Convert.ToBase64String(hashingbytes);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.