Создайте 16-байтовый ключ для шифрования AES из соли и пароля в Node JS Crypto
Я пытаюсь сопоставить шифрование AES 256 CBC, реализованное в C#, используя криптомодуль узла JS.
Это мой код C#
using System;
using System.Security.Cryptography;
using System.Text;
public class Program
{
public static void Main()
{
Console.WriteLine(EncryptExt("Hello World"));
Console.WriteLine(DecryptExt(EncryptExt("Hello World")));
}
public static string EncryptExt(string raw)
{
using (var csp = new AesCryptoServiceProvider())
{
ICryptoTransform e = GetCryptoTransformExt(csp, true);
byte[] inputBuffer = Encoding.UTF8.GetBytes(raw);
byte[] output = e.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
string encrypted = Convert.ToBase64String(output);
return encrypted;
}
}
public static string DecryptExt(string encrypted)
{
using (var csp = new AesCryptoServiceProvider())
{
var d = GetCryptoTransformExt(csp, false);
byte[] output = Convert.FromBase64String(encrypted);
byte[] decryptedOutput = d.TransformFinalBlock(output, 0, output.Length);
string decypted = Encoding.UTF8.GetString(decryptedOutput);
return decypted;
}
}
private static ICryptoTransform GetCryptoTransformExt(AesCryptoServiceProvider csp, bool encrypting)
{
csp.Mode = CipherMode.CBC;
csp.Padding = PaddingMode.PKCS7;
var passWord = Convert.ToString("AvbSkj3BVbf4o6mdlAofDp0/SD0susEWo0pKdmqas");
var salt = Convert.ToString("ABj4PQgf3j5gblQ0iDp0/Gb07ukQWo0a");
String iv = Convert.ToString("aAB1jhPQ89o=f619");
var spec = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(passWord), Encoding.UTF8.GetBytes(salt), 65536);
byte[] key = spec.GetBytes(16);
csp.IV = Encoding.UTF8.GetBytes(iv);
csp.Key = key;
if (encrypting)
{
return csp.CreateEncryptor();
}
return csp.CreateDecryptor();
}
}
И это моя реализация Node JS
const crypto = require('crypto'),
algorithm = 'aes-128-cbc',
password = 'AvbSkj3BVbf4o6mdlAofDp0/SD0susEWo0pKdmqas',
salt = 'ABj4PQgf3j5gblQ0iDp0/Gb07ukQWo0a',
iv = 'aAB1jhPQ89o=f619',
inputEncoding = 'utf8',
outputEncoding = 'base64';
function encrypt(text) {
let cipher = crypto.createCipheriv(algorithm,createHashPassword(), iv);
let encrypted = cipher.update(text, inputEncoding, outputEncoding)
encrypted += cipher.final(outputEncoding);
return encrypted;
}
function createHashPassword(){
let nodeCrypto = crypto.pbkdf2Sync(Buffer.from(password), Buffer.from(salt), 65536, 16, 'sha1');
return nodeCrypto || nodeCrypto.toString('hex');
};
function decrypt(encrypted) {
let decipher = crypto.createDecipheriv(algorithm, Buffer.from(createHashPassword(),"hex"), iv)
let dec = decipher.update(encrypted, outputEncoding, inputEncoding)
dec += decipher.final(inputEncoding);
return dec;
}
console.log(encrypt('Hello World'));
console.log(decrypt(encrypt('Hello World')));
Зашифрованные данные из обеих этих опций отличаются друг от друга, следовательно, не в состоянии решить эту проблему.
Пока что я видел,
- Метод crypto createCipheriv узла принимает только 32-байтовый буфер, и если я передаю ему 16-байтовый буфер, он говорит, недопустимая длина.
- Если я преобразую 16-байтовый ключ в шестнадцатеричную строку, зашифрованное значение изменится и не будет соответствовать реализации C#.
- Я не могу изменить реализацию C#, так как она уже используется и используется несколькими приложениями.
- Таким образом, кажется, есть проблема с генерацией ключа из соли и пароля в узле js, совпадающего с тем, что сделано в C#, и я не могу понять это.
Код можно протестировать по следующей ссылке: Реализация C#: https://dotnetfiddle.net/bClrpW Узел Реализация JS: https://runkit.com/a-vi-nash/5c062544509d8200156f6111
2 ответа
Кажется, вы создаете AES-128
экземпляр в вашем C#
код, потому что вы используете 16 байтов keylen.
AES-256
длина ключа составляет 32 байта, а не 16 байтов.
Ошибки в коде:
- Так как вы установили 16 байтов для ключа в
C#
, оно используетAES-128
неAES-256
, Так что вам нужно изменитьnode.js
вAES-128
или измените сгенерированный ключ на 32 байта в обе стороны. - Так как вы используете текстовую строку соль и пароль (не
base64
закодировано), вашnode.js
сторона использует неверноpbkdf2Sync
параметры. IV
лен дляAES
алгоритм в 16 байтов, и вы не можете использовать более короткие.
Так как вы хотели AES-256
вот ваши поменялись на обе стороны:
C#
боковая сторона:
String iv = Convert.ToString("SOME_IV_SOME_IV_"); // 16 bytes IV
....
byte[] key = spec.GetBytes(32); // 32 bytes key
node.js
боковая сторона:
iv = 'SOME_IV_SOME_IV_' // 16 bytes IV similar to C#
...
// Bugs in this function
function createHashPassword(){
// Change parameters to `base64` only if salt and password are base64. it may be true for salt, but it is can rarely be correct for password.
let nodeCrypto = crypto.pbkdf2Sync(Buffer.from(password), Buffer.from(salt), 65536, 32, 'sha1');
return nodeCrypto;
};
ВАЖНЫЕ ЗАМЕТКИ:
- Помни что
IV
должен быть выбран в качестве случайного буфера (ни фиксированного, ни текстового), и, поскольку кажется, что вы отправляете его по сети, вам необходимо отправитьIV
с этим тоже. SALT
должен быть случайным буфером (не текстовым) и фиксированным с обеих сторон.- я предлагаю использовать более 100000 итераций для
PBKDF2
по крайней мере.
Если вы работаете с паролями длиной 41 символ, почему бы вам не использовать настоящий ключ? 256-битный ключ в кодировке base64 будет иметь длину 44 символа.
Цель соли и итерации для отклонения состоит в том, чтобы противостоять общей проблеме слишком коротких паролей. Но зачем проходить через все трудности реализации этого в обоих концах без дополнительных преимуществ, но с более чем одним недостатком - например, больше кода и более медленное решение.