【问题标题】:Equivalent version of SHA256 ComputeHash (from C#) for React Native/JS用于 React Native/JS 的 SHA256 ComputeHash(来自 C#)的等效版本
【发布时间】:2020-09-18 04:38:26
【问题描述】:

我正在尝试为 React Native/JavaScript 构建一个等效版本的 SHA256 ComputeHash(来自 C#,与以下示例的输出完全相同)。 这是以下 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()}";
    }
}

我尝试了以下方法,但它没有生成相同的哈希:

import * as Crypto from 'expo-crypto';

const hash = await Crypto.digestStringAsync(
    Crypto.CryptoDigestAlgorithm.SHA256,
    "StringIWantToHash"
);

有人知道,JavaScript 出了什么问题,或者是否有与 C# 完全相同的版本?

【问题讨论】:

  • 试试这个库? code.google.com/archive/p/crypto-js 也可以使用hmacSha256 来查看
  • 那么你是说它为相同的输入产生不同的结果?
  • @Raj 是的,确实正确。
  • 好的。您是否尝试将其转换为Base64
  • @Raj 我不明白为什么这会产生任何影响,这只会使结果变得更糟。我刚试过:)

标签: javascript c# react-native hash expo


【解决方案1】:

Expo React Native:

解决方案 1: 通过

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

解决方案 2:

您在 c# 中使用 ToString("X2"),这意味着您必须将哈希转换为 HEX(以 16 为基数)

这里 id 演示:https://snack.expo.io/@nomi9995/expo-crypto

您需要像这样将哈希转换为 HEX(以 16 为基数)

await hash.toString(16);

试试这个,它会得到与 c# 相同的结果

代码:

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

节点js:

通过

安装crypto-js
yarn add crypto-js

试试这个,它会得到与 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#

【讨论】:

  • 这是我尝试输入的一个示例。 输入:test2 React Native/Expo returns: 0x60303ae22b998861bce3b28f33eec1be758a213c86c93c076dbe9f558c11c752 C# returns: 0x39a2272982dc7e6e5d109ab36ec280f6cd3b4b7440af5c739ed808d4ec02aae4 正确的是 C#,因为这也是我们在数据库中使用的。
  • 是什么原因造成的?
  • 你能把输入字符串贴在这里,这样我就可以确定哪个是实际问题?
  • 我使用的输入是“test2”,没有“”
  • @Muhammed Numan 很抱歉听到:/ 但我仍然非常感谢您的解决方案!没有你这样的人,这样的问题永远不会得到解答^_^
【解决方案2】:

解决方案 1:使用 UTF8 而不是 Unicode

嗯,这是一个编码问题

Encoding.Unicode 是 Microsoft 对 UTF-16 的误导性名称(一种双宽编码,出于历史原因在 Windows 世界中使用,但其他人未使用)。 http://msdn.microsoft.com/en-us/library/system.text.encoding.unicode.aspx(见this答案)

您应该改用Encoding.UTF8.GetBytes

像这样使用js-sha256 库:

const jssha = require('js-sha256')

function hash(input)
{
    const hashString = "0x" + jssha.sha256(input)
    return hashString;
}

const hashResult = hash("StringIWantToHash")
// Output: 0x29c506d0d69a16e413d63921b7de79525c43715931d8d93127dbeb46eacda2f9

我们可以在 C# 中使用 UTF8 编码实现非常相似的效果:

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
}

另外,我相信其他有助于计算 SHA256 哈希的 JS/React Native 库也使用 UTF8 编码,所以我认为您可以使用任何其他加密库。

解决方案 2:如果需要使用 Unicode 怎么办?

在这种情况下,您需要在 JS 代码中手动表示 C# 编码。
当您使用 Unicode 编码时,如果字符串中的每个字节都是纯拉丁字符,则在字符串中的每个字节都变为“0”字节之后。对于其他符号(超过 255 个数字),Unicode 需要 2 个字节。

var input = "StringIWantToHash";
var encodedInput = Encoding.Unicode.GetBytes(input);
// Output: [83, 0, 116, 0, 114, 0, 105, 0, 110, 0, ...]

所以我们需要在我们的 JS 代码中表示:

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

【讨论】:

  • 我不知道出了什么问题,但对我来说,“StringIWantToHash”总是“0x029dbc4b54b39bed6d684175b2d76cc5622c60fe91f0bde9865b977d0d9a531d”,而 JS 是“0x29c506d0d69a16e413d63921b7de79525c43715931d8d93127dbeb46eacda2f9”。 C# 之一“0x029dbc4b54b39bed6d684175b2d76cc5622c60fe91f0bde9865b977d0d9a531d”是正确的。我想知道出了什么问题。嗯
  • @KevinJensenPetersen 如果我使用Encoding.Unicode.GetBytes,我也会得到0x029dbc4b54b39bed6d684175b2d76cc5622c60fe91f0bde9865b977d0d9a531d。如果我使用Encoding.UTF8.GetBytes 我有这个哈希:0x29c506d0d69a16e413d63921b7de79525c43715931d8d93127dbeb46eacda2f9 使用Unicode 生成的哈希不正确。请改用UTF8 哈希。
  • 我不能使用 UTF8,因为我猜我们的数据库依赖于它是 Unicode。因为正确的是0x029dbc4b54b39bed6d684175b2d76cc5622c60fe91f0bde9865b977d0d9a531d (数据库处理那种格式)
  • @KevinJensenPetersen 您应该在问题中提到,请稍等,我已经为您提供了解决方案。
  • 我提到它必须与我的 C# 示例(使用 Unicode)完全相同:)
猜你喜欢
  • 2019-03-04
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 2015-06-09
  • 1970-01-01
  • 2018-10-11
  • 1970-01-01
相关资源
最近更新 更多