【问题标题】:Encryption with Crypto in Node.js does not give expected result在 Node.js 中使用 Crypto 进行加密没有给出预期的结果
【发布时间】:2021-10-25 01:47:25
【问题描述】:

我有一个旧的嵌入式设备,它需要一个具有给定密码、密钥和 IV 的加密密码。一个用 .NET 框架编写的现有应用程序会创建一个 AES-128-CBC 加密字符串,我正在使用 Crypto 库在 Node.js 中重写它。

但是,当我在 Node.js 中使用相同的密钥和 IV 加密字符串时,输出与 .NET 中的不同。我已经使用在线工具仔细检查了加密,它们产生的结果与 .NET 相同,因此 Node.js Crypto 是一个奇怪的结果。

.NET 的 C# 代码(密钥和 IV 已替换为演示目的):

byte[] encrypted;
        
        using (AesManaged aes = new AesManaged())
        {
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.Zeros;
            aes.Key = new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
            aes.IV = new byte[] { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99 };

            ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        swEncrypt.Write("admin");
                    }
                    encrypted = msEncrypt.ToArray();
                }
            }
        } 
        return encrypted;

这给出了 Base64 字符串 gE1twb8LV/44oO/6UKwwCg==。 我用在线工具测试了相同的密码、密钥和 IV,它们给出了相同的输出。

我的 Node.js 代码:

const crypto = require('crypto');

const key = Buffer.from('00112233445566778899aabbccddeeff', 'hex');
const iv = Buffer.from('aabbccddeeff00112233445566778899', 'hex');

const plaintext = 'admin';
var cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
cipher.update(Buffer.from('admin'));
var encrypted = cipher.final().toString('base64');

这给出了 Base64 字符串 4TT9E4WDH/TmlKjJdVFeHQ==

我已经尝试了我能想到的一切,但没有运气。 我注意到 .NET 代码指定了一个零填充,但我无法找到在 Node.js Crypto 中设置它的方法。我猜这可能是它没有给出相同输出的原因。

有人知道如何在 Node.js Crypto 中指定零填充吗?

【问题讨论】:

标签: node.js encryption aes cryptojs


【解决方案1】:

正如您已经注意到的,填充不匹配。在 C# 代码中指定了零填充,在 NodeJS 代码中默认使用 PKCS#7。由于 NodeJS 的 crypto 模块不支持零填充,因此您必须自己实现这一点并禁用默认填充。

此外,必须为 update()final() 调用定义输入和输出编码,并且必须连接这些部分。在以下 NodeJS 代码中,UTF-8 被指定为输入编码,Base64 被指定为输出编码:

const crypto = require('crypto');

const key = Buffer.from('00112233445566778899aabbccddeeff', 'hex');
const iv = Buffer.from('aabbccddeeff00112233445566778899', 'hex');

const plaintext = 'admin';
var cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
cipher.setAutoPadding(false); // Disable default PKCS#7 padding
var paddedPlaintext = zeroPad(plaintext, 16); // Zero pad plaintext
var encrypted = cipher.update(paddedPlaintext, 'utf8', 'base64'); // Specify input-/outputencoding
encrypted += cipher.final('base64'); // Specify outputencoding and concatenate
console.log(encrypted); // gE1twb8LV/44oO/6UKwwCg==

function zeroPad(text, bs){
    var padLength = text.length;
    if (text.length % bs > 0){
      padLength += bs - text.length % bs;
    }
    return text.padEnd(padLength, '\0');
}

输出:

gE1twb8LV/44oO/6UKwwCg==

与 C# 代码的输出相匹配。


请注意,静态 IV 是不安全的。为每次加密使用随机生成的 IV 更安全。当然,出于测试目的,这很好。
此外,零填充是不可靠的。更好的选择是 PKCS#7 填充(这是 crypto 默认值)。

【讨论】:

  • 感谢您提供准确和精心设计的答案。这解决了我的问题。是的,我知道围绕零填充和 IV 的安全问题。不幸的是,我必须支持嵌入式设备固件的向后兼容性,所以我无法绕过它......
猜你喜欢
  • 2019-02-22
  • 1970-01-01
  • 2020-05-29
  • 1970-01-01
  • 2019-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-09
相关资源
最近更新 更多