使用AES256和Node.js解密长度超过15个字符的输入数据时出错 [英] Error when decrypt input data longer than 15 chars with AES256 and Node.js

查看:1235
本文介绍了使用AES256和Node.js解密长度超过15个字符的输入数据时出错的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用Node.js的加密模块和AES-256-CBC密码算法编写自己的安全类。

I'm writing my own security class using the crypto module of Node.js and the AES-256-CBC cipher algorithm.

但是当我尝试解密从超过15个字符的输入数据加密的加密字符串将失败并显示以下错误:

But when I try to decrypt an encrypted string, encrypted from input data longer than 15 characters, fails with this error:

crypto.js:153
var ret = this._handle.final();
Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt



问题是加密或IV生成,事实上,加密的十六进制字符串总是32个字符长。

I think that the problem is with the encryption or the IV generation, in fact, the encrypted hex string is always 32 chars long.

让我们一起来看看代码:

Let's review the code together:

var crypto = require("crypto"),
    password = "mySecureKey",
    salt = "mySaltKey";

//generate the IV
crypto.pbkdf2(password , salt, 4096, 8, "sha1", function(err, key) {
    if (err) throw err;   
    var cipher_iv = new Buffer(key.toString('hex'));

    //encrypt the string
    var input = "helloPrettyWorld";
    cipher = crypto.createCipheriv("aes-256-cbc", new Buffer(password), cipher_iv);
    cipher.update(input, "utf8", "hex");
    var encrypted = cipher.final("hex"); //i.e: input = "hello"; encrypted = "2300743605fbdaf0171052ccc6322e96"

    //decrypt the string
    cipher = crypto.createDecipheriv("aes-256-cbc", new Buffer(password), cipher_iv); /* THE ERROR IS THROWN HERE */
    cipher.update(encrypted, "hex", "utf8")     
    var decrypted = cipher.final("utf8");
});

我尝试调整密码/盐长度,甚至使用固定长度的字符串(32,16

I tried with resizing the password/salt lengths and even using string with fixed-length (32, 16, etc..), but does not to resolve the problem.

输入数据类似于:helloNiceWorld(14个字符)将被完全加密和解密,而类似helloPrettyWorld(16个字符)的输入数据不会。

An input data like: "helloNiceWorld" (14 chars) will be encrypted and decrypted perfectly, while an input data like "helloPrettyWorld" (16 chars) will not.

推荐答案

TL; DR 不使用 cipher.update()的结果,丢弃了明文和密文的一部分

AES是一种只对16字节(块大小)的块工作的块密码。 CBC模式将其扩展为长度为块大小的倍数的明文。然后需要填充(默认为PKCS#7填充)以填充明文到块大小的下一个倍数。

AES is a block cipher that only works on blocks of 16 bytes (block size). CBC mode extends this to plaintexts with a length of a multiple the block size. A padding (PKCS#7 padding by default) is then needed to fill up the plaintext to the next multiple of the block size.

这意味着填充是在完成加密之前的最后操作。填充应用于 cipher.final() 功能。

This means that the padding is part of the last operation before finishing the encryption. Padding is applied in the cipher.final() function.

cipher.update() 返回密文的一部分,但不处理(在内部缓存)最后一个字节,以便应用填充。加密结束时 cipher.final()

由于PKCS#7填充总是添加填充,因此您必须调用此属性才能将填充添加到最后一个缓存的字节,当明文是16到31字节长时,将得到一个两块填充的明文。现在,问题是你不存储来自导致不完全密文的 cipher.update()调用的结果。如果你的消息小于单个块,则 cipher.update()将返回一个空(字符串或缓冲区),你将得到从<$ cc $ c> cipher.final()。

Since the PKCS#7 padding always adds padding, you will get a two block padded plaintext when the plaintext is 16 to 31 bytes long. Now, the problem is that you're not storing the result from the cipher.update() call which results in incomplete ciphertext. If your message is smaller than a single block, then cipher.update() would return something empty (string or Buffer) and you'll get the complete ciphertext from cipher.final().

不要忘记连接不同的密文和明文部分:

Don't forget to concatenate the different ciphertext and plaintext parts:

cipher = crypto.createCipheriv("aes-256-cbc", new Buffer(password), cipher_iv);
var encrypted = cipher.update(input, "utf8", "hex");
encrypted += cipher.final("hex");

//decrypt the string
cipher = crypto.createDecipheriv("aes-256-cbc", new Buffer(password), cipher_iv);
var decrypted = cipher.update(encrypted, "hex", "utf8")     
decrypted += cipher.final("utf8");






其他注意事项:



password =mySecureKey如果这确实是一些文本,那么它不安全,而不是一个键,密码。不要直接使用密码作为密钥。


Other considerations:

password = "mySecureKey" If this is indeed some text, then it's not secure and not a key, but as the variable names says, it's a password. Don't use passwords directly as keys. Derive the key from a password with a randomly generated salt (per password).

此外,在每次加密期间生成一个新的随机IV,并将其放在密文的前面。 IV不必是秘密,但它需要是不可预测的。如果重新使用IV,那么观察您的密文的攻击者可以确定是否再次发送先前发送的消息。如果你使用随机IV,你获得语义安全。

Additionally, generate a new random IV during each encryption and simply put it in front of the ciphertext. The IV doesn't have to be secret, but it needs to be unpredictable. If you re-use the IV, then an attacker who observes your ciphertexts may determine whether you send previously sent messages again. If you use random IV, you get semantic security.

此外,使用MACAC-SHA256等具有强MAC的加密MAC地址验证你的密文。这使您能够检测对密文的任何(恶意)修改,并防止攻击如padding-oracle攻击。

Furthermore, authenticate your ciphertexts with an encrypt-then-MAC scheme with a strong MAC like HMAC-SHA256. This enables you to detect any (malicious) modifications of the ciphertext and prevents attacks such as the padding-oracle attack.

这篇关于使用AES256和Node.js解密长度超过15个字符的输入数据时出错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆