我正在将一个C# .NET 4.8 Framework代码库迁移到云。新的体系结构使用nodejs firebase函数。这些函数只允许nodejs代码。
下面的代码用于使用存储在数据库中的哈希值验证输入文本密码。该应用程序正在迁移到SSO解决方案,但将允许用户通过验证其以前的凭据来从其旧帐户中索取数据。
下面的代码使用RFC2898(现在称为PBKDF 2)生成密钥和初始化向量,并将其提供给Rijndael对象以设置加密。
此方法出于某种原因将密码与salt组合在一起,可能是为了增加密码的长度。salt始终为36个字符。然后,它使用了胡椒的地方,盐通常会去。最后,加密值以base64字符串的形式输出。
using System.Security.Cryptography;
using System.Text;
var password = "password123";
var salt = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
var pepper = "yyyyyyyy";
var enc = new UTF8Encoding();
var keyGen = new Rfc2898DeriveBytes(password + salt, enc.GetBytes(pepper));
var key = keyGen.GetBytes(32);
var iv = keyGen.GetBytes(16);
var cipher = new RijndaelManaged
{
Key = key,
IV = iv
};
Console.WriteLine("Key: " + Convert.ToBase64String(key));
Console.WriteLine("IV: " + Convert.ToBase64String(iv));
var plainText = enc.GetBytes(password + salt);
using var encryptor = cipher.CreateEncryptor();
using var ms = new MemoryStream();
using var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);
cs.Write(plainText, 0, plainText.Length);
cs.FlushFinalBlock();
Console.WriteLine("Hash: " + Convert.ToBase64String(ms.ToArray()));
字符串
使用nodejs 18和crypto包,我能够使用下面的代码创建与上面的C#代码等效的密钥和iv。
const generatePbkdf2Values = (password, pepper) => {
const pepperBytes = Buffer.from(pepper, 'utf8');
const iterations = 1000;
const length = 32 + 16;
const digest = 'sha1';
const keyPlusIv = crypto.pbkdf2Sync(
password,
pepperBytes,
iterations,
length,
digest
);
return {
key: keyPlusIv.subarray(0, 32),
iv: keyPlusIv.subarray(32, length),
};
};
型
我读过Rijndael是一个具有固定块大小的variant of AES。因此,我希望能够使用crypto包的createCipheriv方法来加密文本,但我很难做到这一点。我担心C#设置或默认值不兼容,或者我没有正确配置它们。我想听听你的建议。
一些假设
- IV是一个16字节/128位密钥,需要
aes-128-cbc
算法。但是128算法会抛出密钥长度错误,因此它必须基于需要aes-256-cbc
算法的32字节/256位密钥。
1.默认情况下,C# Rijndael算法使用PKCS 7填充,crypto.createCipheriv
通过cipher也使用PKCS 7填充。setAutoPadding(true) - nodejs方法中的key和iv创建的值与C#等效
下面是我用来散列文本的nodej。key
、iv
、password
和salt
来自上面的代码段。
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
const passwordAndSalt = Buffer.from(password + salt, 'utf8');
const encoding = 'hex';
const inputEncoding = encoding;
const outputEncoding = encoding
let result = cipher.update(passwordAndSalt, inputEncoding, outputEncoding);
result += cipher.final(outputEncoding);
console.log(result.toString('base64'));
型
这会提供比C#方案多32个字符的字串,而且不共用任何模式。
具有上述值的正确哈希值为DC07eaMXkaeK26KTIt3ldtzTdISTH5j9cmrg3OzFF16MvfKkEi9ihEkI8VDLzNvU
作为B计划,我尝试使用rinjndael-js npm包。下面的代码使用与上面相同的密钥和iv,生成的哈希值几乎正确,但不完全正确。
import Rijndael from 'rijndael-js';
const cipher = new Rijndael(key, 'cbc');
const text = Buffer.from(password + salt, 'utf8');
return Buffer.from(cipher.encrypt(text, 16, iv)).toString('base64')
型
这会产生长度相同且68%相等的杂凑。前43个字符匹配,后21个字符不匹配。这将是我的首选使用内置的加密包来创建相同的散列。有可能吗?你能发现我的错误吗?先谢了。
1条答案
按热度按时间vngu2lb81#
好的,我发现了bug,它与编码、响应类型和连接有关。
工作溶液
字符串
原因
上面的结果长了50%,因为调用
.final('hex')
并追加到result对象是将两个hex对象组合在一起。我假设这会导致在尝试将结果显示为.toString('base64')
时出现问题。你甚至能够.toString('base64')
十六进制值吗?如果未提供输出编码,则会传回Buffer。因此,连接缓冲区并对其进行base64编码可提供正确的哈希值!
编辑
这简化了返回base64的过程,并得到了@Topaco的建议。
型