我试图建立一个等价的SHA256 ComputeHash版本(从C#,从下面的示例中完全相同的输出),到React NativeJavaScript.这是下面的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()}";
}
}
我试了下面的方法,但没有产生相同的Hash。
import * as Crypto from 'expo-crypto';
const hash = await Crypto.digestStringAsync(
Crypto.CryptoDigestAlgorithm.SHA256,
"StringIWantToHash"
);
有谁知道,要么是JavaScript出了什么问题,要么是C#的完全等价版本?
嗯,这是一个 编码问题.
Encoding.Unicode
是微软对UTF-16(一种双宽编码,由于历史原因,在Windows世界中使用,但其他人没有使用)的误导性名称。http:/msdn.microsoft.comen-uslibrarysystem.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
我们可以在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哈希值的JSReact Native库也使用UTF8编码,所以我认为你可以使用其他任何加密库来实现。
在这种情况下,你需要在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
解决方案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:
你使用的是 ToString("X2")
在c#中,这意味着你必须将哈希转换为HEX(基数16)。
这里id演示。https:/snack.expo.io@nomi9995expo-crypto。
你需要将哈希转换为HEX(基数16),就像这样
await hash.toString(16);
试试这个,结果和c#一样。
代码:Node js
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",
},
});
安装 隐语 由
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#