React Native/JS 的 SHA256 ComputeHash(来自 C#)的等效版本

Kev*_*sen 6 javascript c# hash react-native expo

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

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

import * as Crypto from 'expo-crypto';

const hash = await Crypto.digestStringAsync(
    Crypto.CryptoDigestAlgorithm.SHA256,
    "StringIWantToHash"
);
Run Code Online (Sandbox Code Playgroud)

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

pic*_*ino 2

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

嗯,这是一个编码问题

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

你应该改用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
Run Code Online (Sandbox Code Playgroud)

我们可以在 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
}
Run Code Online (Sandbox Code Playgroud)

另外,我相信其他有助于计算 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, ...]
Run Code Online (Sandbox Code Playgroud)

所以我们需要在 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
Run Code Online (Sandbox Code Playgroud)