从Node JS Crypto中的Salt和Password创建用于AES加密的16字节密钥

时间:2018-12-04 06:00:17

标签: node.js encryption aes cryptojs aescryptoserviceprovider

我正在尝试通过使用节点JS加密模块来匹配C#中实现的AES 256 CBC加密。

这是我的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')));

这两个选项的加密数据不同,因此无法解决。

到目前为止,我所看到的是

  • node crypto createCipheriv方法仅需要32个字节的缓冲区,如果我 向其传递16字节的缓冲区,长度无效。
  • 如果我将16个字节的密钥转换为十六进制编码的字符串,则加密的值会更改,并且与C#实现不匹配。
  • 我无法更改C#实现,因为它已经在生产中并且已被多个应用程序使用。
  • 因此在节点js中从盐和密码生成密钥,匹配C#中的操作似乎存在问题,而我无法弄清楚。

可以在以下链接中测试代码: C#实现:https://dotnetfiddle.net/bClrpW 节点JS实现:https://runkit.com/a-vi-nash/5c062544509d8200156f6111

2 个答案:

答案 0 :(得分:0)

似乎您正在使用AES-128代码创建一个C#实例,因为您使用的是16字节的keylen。

AES-256 keylen是32个字节,而不是16个字节。

错误代码:

  1. 由于您为C#输入了16个字节,因此它使用AES-128,而不是AES-256。因此,您需要将node.js更改为AES-128或将生成的密钥更改为双方的32个字节。
  2. 由于您正在使用文本字符串salt和密码(未经过base64编码),因此您的node.js端使用了错误的pbkdf2Sync参数。
  3. 对于IV算法,
  4. AES len为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;
};

重要说明:

  1. 请记住,IV必须被选作随机缓冲区(既不是固定的也不是文本的),并且由于似乎您是通过网络发送的,因此也需要发送IV
  2. SALT必须是随机缓冲区(不是文本),并且固定在两侧。
  3. 我建议至少为PBKDF2使用100000次迭代。

答案 1 :(得分:0)

如果您使用41个字符长的密码进行操作,为什么不使用实际密钥呢?用base64编码的256位密钥长为44个字符。

盐和偏差的迭代的目的是解决密码太短的常见问题。但是,为什么要在两端实现所有实现这一麻烦的工作,却没有带来任何额外的好处,却有多个弊端-例如更多的代码和较慢的解决方案。