如何在node.js中检查ASP.NET密码哈希

6
首先,我阅读了这篇文章Hashing a password using SHA256 and .NET/Node.js,但对我没有帮助。
我需要在node.js环境中验证在ASP.NET中创建的密码哈希值。我被告知密码是使用此算法生成的:What is default hash algorithm that ASP.NET membership uses?
我有一个示例密码哈希值和salt(第一行是密码,第二行是salt):
"Password": "jj/rf7OxXM263rPgvLan4M6Is7o=",
"PasswordSalt": "/Eju9rmaJp03e3+z1v5s+A==",

我知道哈希算法是SHA1,也知道上面的哈希是为输入test123生成的。但是我无法复现这个输入的相同哈希值。我尝试了以下方法:

Password = "jj/rf7OxXM263rPgvLan4M6Is7o="
PasswordSalt = "/Eju9rmaJp03e3+z1v5s+A=="
crypto = require("crypto")
sha1 = crypto.createHash("sha1")
PasswordSalt = new Buffer(PasswordSalt, 'base64').toString('utf8')
sha1.update(PasswordSalt+"test123", "utf8")
result = sha1.digest("base64")
console.log(Password)
console.log(result)

结果是:

jj/rf7OxXM263rPgvLan4M6Is7o=
xIjxRod4+HVYzlHZ9xomGGGY6d8=

我能够编写工作的C#算法:

using System.IO;
using System;
using System.Text;
using System.Security.Cryptography;

class Program
{

    static string EncodePassword(string pass, string salt)
    {
        byte[] bytes = Encoding.Unicode.GetBytes(pass);
        byte[] src = Convert.FromBase64String(salt);
        byte[] dst = new byte[src.Length + bytes.Length];
        Buffer.BlockCopy(src, 0, dst, 0, src.Length);
        Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
        HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
        byte[] inArray = algorithm.ComputeHash(dst);
        return Convert.ToBase64String(inArray);
    }

    static void Main()
    {
        string pass = "test123";
        string salt = "/Eju9rmaJp03e3+z1v5s+A==";
        string hash = Program.EncodePassword(pass,salt);
        Console.WriteLine(hash);
        // outputs jj/rf7OxXM263rPgvLan4M6Is7o=
    }
}

现在的问题只是将这个算法移植到node.js。问题在于c#似乎以某种神奇的方式操作字节,而我不知道如何在node中实现它。请考虑以下代码(它不使用任何盐值-仅从密码创建base64 sha1):

crypto = require("crypto")
pass = 'test123'
sha1 = crypto.createHash("sha1")
buf = new Buffer( pass, 'utf8')
sha1.update(buf)
result = sha1.digest("base64")
console.log(result)
// outputs cojt0Pw//L6ToM8G41aOKFIWh7w=

在C#中,
 using System.Text;
 using System.Security.Cryptography;
 string pass = "test123";
 byte[] bytes = Encoding.Unicode.GetBytes(pass);
 HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
 byte[] inArray = algorithm.ComputeHash(bytes);
 string hash = Convert.ToBase64String(inArray);
 Console.WriteLine(hash);
 // outputs Oc/baVMs/zM28IqDqsQlJPQc1uk=

我需要用Node.js编写代码,该代码应返回与C#中的代码相同的值。有什么想法吗?

你确定这就是.NET平台中使用盐的方式吗?看起来有点幼稚。你确定它不是HMAC吗?另外,你确定你添加的字节与+"test123"是你想要的,而不是UTF-16或者v8在底层使用的其他编码方式吗? - Jim Keener
我真的不知道 - 当前数据库(实际上我们正在从这个数据库迁移)的维护者只给了我这个链接 https://dev59.com/MXM_5IYBdhLWcg3w-4rw?answertab=active#tab-top - 你有什么想法吗? - user606521
我已经更新了问题 - 我提供了可工作的C#代码,需要将其移植到Node.js。 - user606521
3个回答

9

我终于在这里找到了正确的答案:https://gist.github.com/PalmerEk/1191651(只需将'ucs2'更改为'utf16le'即可):

function dotnet_membership_password_hash(pass, salt)
{
  var bytes = new Buffer(pass || '', 'utf16le');
  var src = new Buffer(salt || '', 'base64');
  var dst = new Buffer(src.length + bytes.length);
  src.copy(dst, 0, 0, src.length);
  bytes.copy(dst, src.length, 0, bytes.length);

  return crypto.createHash('sha1').update(dst).digest('base64');
}

1
注意,你的代码使用的是 'ucs2',而不是 'utf16le'(或者 'utf-16' 或者 'utf-16le' 或者 'utf16',我不知道哪个是正确的)。前者不支持补充字符编码点,而后者支持。 - Peter O.
谢天谢地,你救了我! - yangsibai

5

有一个Node.js模块可以为您完成所有的魔法。在我的情况下,StackOverflow上的任何函数都没有起作用,但是这个模块可以:

https://www.npmjs.com/package/aspnet-identity-pw

  var passwordHasher = require('aspnet-identity-pw');

  var hashedPassword = passwordHasher.hashPassword('SomePassword');

  var isValid = passwordHasher.validatePassword('SomePassword', hashedPassword);

它返回False,我正在使用C#生成一些密码,并使用您的代码验证加密后的密码,但它返回false。c3为“123456”生成的密码为“JWvppSSfnzOQ+uMd+BORpT/8aQorC8y05Bjbo/8w/9b/eiG4WLzUFRQSSiKZqo3C”。 - Juned Ansari

1

我仍然无法使其工作-我应该在哪里(以及如何)更改编码?我尝试只更改“utf8”为“utf16le”,但它仍然不起作用。您能否提供一些node.js代码片段,以修改node.js代码以获得与c#代码相同的结果? - user606521
我会在几个小时内给你赏金,因为你的回答虽然不完整,但帮助我找到了解决方案。谢谢。 - user606521

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接