NodeJS:如何生成像C#这样的Rfc2898DeriveBytes?

时间:2018-06-16 10:16:15

标签: c# node.js

我正在尝试使用NodeJS加密库和C#Rfc2898DeriveBytes生成相同的密码哈希。使用从C#生成的salt时,NodeJs实现不会生成相同的密钥。我究竟做错了什么? 在C#中:

public static string HashPassword(string password)
    {
        // random khóa 
        using (var rngCryp = new RNGCryptoServiceProvider())
        {
            var salt = new byte[SaltBytes];
            rngCryp.GetBytes(salt);

            // Hash the password and encode the parameters
            byte[] hash = Rfc2898Deriver(password, salt, Pbkdf2Iterations, HashBytes);

            return Pbkdf2Iterations + ":" + Convert.ToBase64String(salt) + ":" + Convert.ToBase64String(hash);
        }
    }
private static byte[] Rfc2898Deriver(string password, byte[] salt, int iterations, int outputMaxByte)
    {
        using (var deriveBytes = new Rfc2898DeriveBytes(password, salt))
        {
            deriveBytes.IterationCount = iterations;
            return deriveBytes.GetBytes(outputMaxByte);
        }
    }

在NodeJs中:

export const hash = (text, salt) => new Promise((resolve, reject) => {
  crypto.pbkdf2(text, salt, iterations, bytes, 'sha256', function (err, derivedKey) {
if (err) { reject(err) }
else {
  //return Pbkdf2Iterations + ":" + Convert.ToBase64String(salt) + ":" + Convert.ToBase64String(hash);
  var hash = new Buffer(derivedKey).toString('base64');
  var pass = `${iterations}:${salt}:${hash}`
  resolve(pass);
}});})

并使用那样:

var a = Buffer.from("qcMqVYE0EzAU9Uz+mQxBaKFICG1vR1iq", 'base64')
var a0 = new Buffer("qcMqVYE0EzAU9Uz+mQxBaKFICG1vR1iq")
var pas1 = new Buffer('AL7h8Jx4r8a8PjS5', 'base64')
hash(pas1,a0).then(pass => {
    console.log("pass: ", pass)
    const hashes = crypto.getHashes();
    console.log(hashes); // ['DSA', 'DSA-SHA', 'DSA-SHA1', ...]
    res.send(pass + "\n1000:qcMqVYE0EzAU9Uz+mQxBaKFICG1vR1iq:RkdpgAcpijFqYgVxBCvJugMXqnt4j5f3")
})

如你所见,hass传入C#并且Nodejs是不同的。 节点 - >

  

1000:qcMqVYE0EzAU9Uz + mQxBaKFICG1vR1iq:D19SUxg6AQxgSLe7YXISPWPvgIoR6BEw

C# - >

  

1000:qcMqVYE0EzAU9Uz + mQxBaKFICG1vR1iq:RkdpgAcpijFqYgVxBCvJugMXqnt4j5f3

2 个答案:

答案 0 :(得分:0)

我有一个非常相似的问题,实际上您的发现对我有很大帮助。 看来您遇到的唯一问题是传递给pbkdf2函数的错误的哈希算法。

类似Rfc2898DeriveBytes的外观默认情况下使用SHA1。因此,您应该在节点中使用过类似的方法:

crypto.pbkdf2(text, salt, iterations, bytes, 'sha1', (err, key) => {
    console.log(key.toString('hex'));
});

答案 1 :(得分:0)

在这里输入固定盐,您还可以生成16的随机字符串。您也可以更改长度,而不是我的“ 32”。

var crypto = require('crypto');
salt = '1234123412341234';
saltString = new Buffer(salt).toString('hex');
var password = 'welcome';
var nodeCrypto = crypto.pbkdf2Sync(new Buffer(password), new Buffer(saltString, 'hex'), 1000, 32, 'sha1');
var hashInHex="00"+saltString+nodeCrypto.toString('hex').toUpperCase();
var FinalHash = Buffer.from(hashInHex, 'hex').toString('base64')
console.log("saltInHex: "+saltString);
console.log("FinalHashInBase64: "+FinalHash);

要将存储的哈希密码与用户输入密码匹配,请使用以下代码:

// NodeJS implementation of crypto, I'm sure google's 
// cryptoJS would work equally well.
var crypto = require('crypto');

// The value stored in [dbo].[AspNetUsers].[PasswordHash]
var hashedPwd = "AGYzaTk3eldHaXkxbDlkQmn+mVJZEjd+0oOcLTNvSQ+lvUQIF1u1CNMs+WjXEzOYNg==";
var hashedPasswordBytes = new Buffer(hashedPwd, 'base64');

var hexChar = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];

var saltString = "";
var storedSubKeyString = "";

// build strings of octets for the salt and the stored key
for (var i = 1; i < hashedPasswordBytes.length; i++) {
    if (i > 0 && i <= 16) {
        saltString += hexChar[(hashedPasswordBytes[i] >> 4) & 0x0f] + hexChar[hashedPasswordBytes[i] & 0x0f]
    }
    if (i > 0 && i > 16) {
        storedSubKeyString += hexChar[(hashedPasswordBytes[i] >> 4) & 0x0f] + hexChar[hashedPasswordBytes[i] & 0x0f];
    }
}

// password provided by the user
var password = 'vish@123';

// TODO remove debug - logging passwords in prod is considered 
// tasteless for some odd reason
console.log('cleartext: ' + password);
console.log('saltString: ' + saltString);
console.log('storedSubKeyString: ' + storedSubKeyString);

// This is where the magic happens. 
// If you are doing your own hashing, you can (and maybe should)
// perform more iterations of applying the salt and perhaps
// use a stronger hash than sha1, but if you want it to work
// with the [as of 2015] Microsoft Identity framework, keep
// these settings.
var nodeCrypto = crypto.pbkdf2Sync(new Buffer(password), new Buffer(saltString, 'hex'), 1000, 256, 'sha1');

// get a hex string of the derived bytes
var derivedKeyOctets = nodeCrypto.toString('hex').toUpperCase();

console.log("hex of derived key octets: " + derivedKeyOctets);

// The first 64 bytes of the derived key should
// match the stored sub key
if (derivedKeyOctets.indexOf(storedSubKeyString) === 0) {
    console.info("passwords match!");
} else {
    console.warn("passwords DO NOT match!");
}