在C#中重现JS PBKDF2哈希 [英] Reproducing JS PBKDF2 Hash in C#

查看:144
本文介绍了在C#中重现JS PBKDF2哈希的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我必须为现有数据库实现一些新的安全功能。他们曾经使用来自数据库的盐在客户端端哈希密码。他们使用此代码在将密码发送到服务器之前对密码进行哈希:

I had to implement some new security features for an Existing Database. They used to hash passwords on the side of the client with a salt from the DB. They used this code to hash the passwords before sending them to the server:

var hash = CryptoJS.PBKDF2(password, USER_SALT, {
    keySize: 4,
    iterations: 1000
});

现在你已经可以猜到了,这是非常不安全的,因为攻击者获得了PW就像它一样如果用户从服务器获取Salt并且哈希在客户端完成,则以纯文本形式发送。这就是我需要在服务器方面做的原因。

Now as you can already guess, this is highly insecure, cause an attacker gets the PW as if it was sent in plain text if the user gets the Salt from the server and the hasing is done client side. That's why I need to do it server side.

现在,数据库有几千个用户使用哈希密码的条目,使用这个数据库。我尝试在C#中使用来自互联网的许多引用来实现类似的方法,但是我无法获得与存储在数据库中相同的哈希值。

Now the DB has several thousand entries with hashed passwords of users, that use this DB. I tried implementing a similar method in C# with many references from the internet, but I couldn't get the same Hashes as stored in the DB.

    public static string HashPassword(string password, string salt)
    {
        byte[] saltBytes = Encoding.UTF8.GetBytes(salt);
        using (var rfc2898 = new Rfc2898DeriveBytes(password, saltBytes, 1000))
        {
            byte[] hash = rfc2898.GetBytes(16);
            string hashString = string.Empty;
            foreach (byte x in hash)
            {
                hashString += String.Format("{0:x2}", x);
            }
            return hashString;
        }
    }

这是我使用的方法。我想重现CryptoJS的密钥4,所以我得到4x = 32位长密码哈希值。我确实得到了它们,但它们与我在JS中使用CryptoJS获得的不同。

This was the method I used. I wanted to reproduce the keysize 4 of CryptoJS so I'd get 4x = 32 digit long password hashes. I do get them, but they're different than the ones I would get in JS with CryptoJS.

有谁知道如何获得与CryptoJS版本相同的结果?因为数据库中的所有密码都是以这种方式存储的,所以现在只能在服务器端再次以相同的方式生成它们。 (密码现在加密以防止攻击)。

Does anyone know how to get the same result as the CryptoJS version would? BEcause all passwords in the DB have been stored that way, it is crucial they are now generated the same way again only server side. (The Passwords are now encrypted against attacks).

推荐答案

使用 JSFiddle for CryptoJS / PBKDF2 (虽然我必须更正CDN链接),其相关代码是

Using a JSFiddle for CryptoJS/PBKDF2 (though I had to correct the CDN links), whose relevant code is

var password = $("[name='password']").val();

var iterations = 1000;
// sizes must be a multiple of 32
var keySize = 128;
var ivSize = 128;
var salt = CryptoJS.lib.WordArray.random(128/8);

$("#salt").html(salt.toString(CryptoJS.enc.Base64));
$("#iter").html(iterations);
$("#keysize").html(keySize);
$("#ivsize").html(ivSize);

var output = CryptoJS.PBKDF2(password, salt, {
    keySize: (keySize+ivSize)/32,
    iterations: iterations
});

// the underlying words arrays might have more content than was asked: remove insignificant words
output.clamp();

var key = CryptoJS.lib.WordArray.create(output.words.slice(0, keySize/32));
var iv = CryptoJS.lib.WordArray.create(output.words.slice(keySize/32));

$("#hasher").html("SHA1");

$("#key").html(key.toString(CryptoJS.enc.Base64));
$("#iv").html(iv.toString(CryptoJS.enc.Base64));

我得到一个样本输入/输出hello使用(base64)盐 0CD1HGFdkclqcWG5aV + rvw == (以及默认的哈希算法,具有指定的1000次迭代和32 + 32字节输出);产生 tRczLRRuFy / zFiPn1PBKmQ == / dhyeE + 0Dd9avSJbM / 4TcNw ==

I got as one sample input/output "hello" with a (base64) salt of 0CD1HGFdkclqcWG5aV+rvw== (and the default hash algorithm, with the specified 1000 iterations and 32+32 byte outputs); that produced tRczLRRuFy/zFiPn1PBKmQ== / dhyeE+0Dd9avSJbM/4TcNw==.

然后使用了以下C#发现代码:

The following C# discovery code was then utilized:

string password = "hello";
byte[] salt = Convert.FromBase64String("0CD1HGFdkclqcWG5aV+rvw==");
int iterations = 1000;

using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA1))
{
    Console.WriteLine("UTF-8 / SHA-1");
    Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
    Console.Write(' ');
    Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
}

using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256))
{
    Console.WriteLine("UTF-8 / SHA-2-256");
    Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
    Console.Write(' ');
    Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
}

byte[] utf16 = Encoding.Unicode.GetBytes(password);
using (var pbkdf2 = new Rfc2898DeriveBytes(utf16, salt, iterations, HashAlgorithmName.SHA1))
{
    Console.WriteLine("UTF-16LE / SHA-2-256");
    Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
    Console.Write(' ');
    Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
}

using (var pbkdf2 = new Rfc2898DeriveBytes(utf16, salt, iterations, HashAlgorithmName.SHA256))
{
    Console.WriteLine("UTF-16LE / SHA-2-256");
    Console.Write(Convert.ToBase64String(pbkdf2.GetBytes(16)));
    Console.Write(' ');
    Console.WriteLine(Convert.ToBase64String(pbkdf2.GetBytes(16)));
}

输出

UTF-8 / SHA-1
tRczLRRuFy/zFiPn1PBKmQ== dhyeE+0Dd9avSJbM/4TcNw==
UTF-8 / SHA-2-256
lkBtILt+xDNEQrX0aWUk3Q== ouOiijCw5sjfMcJo9YZ4Ug==
UTF-16LE / SHA-2-256
1T2gJFFECc5AnpvoiFrBwg== rmHsTuOQdM5YDsmzklMEUQ==
UTF-16LE / SHA-2-256
G4/Ik5vZAd2l8kwq45BKaw== Iqy61Eaf8jmoxO2TpA+rkg==






结论:CryptoJS.PBKDF2正在使用SHA-1和UTF-8,这意味着最可能的问题是如果你没有得到相同的答案,你加载错误的盐。如果它不是UTF-8字符串,它可能是十六进制数据或base64(但这完全取决于你的用法,这里没有正确的答案,因为盐是只是字节)。


Conclusion: CryptoJS.PBKDF2 is using SHA-1 and UTF-8, which means that the most likely problem is that you're loading in the wrong salt if you're not getting the same answer. If it's not a UTF-8 string it's probably either hexadecimal data or base64 (but that depends completely on your usage, there's no "the right answer is" here, as the salt is "just bytes").

这篇关于在C#中重现JS PBKDF2哈希的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆