Node.js 中的 Crypto 加密未给出预期结果
Encryption with Crypto in Node.js does not give expected result
我有一个旧的嵌入式设备,它需要一个带有给定密码、密钥和 IV 的加密密码。用 .NET 框架编写的现有应用程序创建了一个 AES-128-CBC 加密字符串,我正在 Node.js 使用 Crypto 库重写它。
但是,当我在 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 加密中指定零填充吗?
正如您已经注意到的,填充不匹配。在 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, '[=10=]');
}
输出:
gE1twb8LV/44oO/6UKwwCg==
与 C# 代码的输出匹配。
请注意,静态 IV 是不安全的。为每次加密使用随机生成的 IV 更安全。当然,出于测试目的,这很好。
此外,零填充是不可靠的。更好的选择是 PKCS#7 填充(这是 crypto 默认值)。
我有一个旧的嵌入式设备,它需要一个带有给定密码、密钥和 IV 的加密密码。用 .NET 框架编写的现有应用程序创建了一个 AES-128-CBC 加密字符串,我正在 Node.js 使用 Crypto 库重写它。
但是,当我在 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 加密中指定零填充吗?
正如您已经注意到的,填充不匹配。在 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, '[=10=]');
}
输出:
gE1twb8LV/44oO/6UKwwCg==
与 C# 代码的输出匹配。
请注意,静态 IV 是不安全的。为每次加密使用随机生成的 IV 更安全。当然,出于测试目的,这很好。
此外,零填充是不可靠的。更好的选择是 PKCS#7 填充(这是 crypto 默认值)。