Node.js 用于'的Crypto.decipher.final;aes-256-cbc';密钥无效的算法解密失败

Node.js 用于'的Crypto.decipher.final;aes-256-cbc';密钥无效的算法解密失败,node.js,openssl,cryptography,aes,Node.js,Openssl,Cryptography,Aes,我能够使用node.js Crypto module使用“aes-256-cbc”算法的密码和解密类对消息进行加密和解密,如下所示: var crypto = require('crypto'); var cipherKey = crypto.randomBytes(32); // aes-256 => key length is 256 bits => 32 bytes var cipherIV = crypto.randomBytes(16); // aes block siz

我能够使用node.js Crypto module使用“aes-256-cbc”算法的密码和解密类对消息进行加密和解密,如下所示:

var crypto = require('crypto');

var cipherKey = crypto.randomBytes(32); // aes-256 => key length is 256 bits => 32 bytes
var cipherIV = crypto.randomBytes(16); // aes block size = initialization vector size = 128 bits => 16 bytes
var cipher = crypto.createCipheriv('aes-256-cbc', cipherKey, cipherIV);

var message = 'Hello world';
var encrypted = cipher.update(message, 'utf8', 'hex') + cipher.final('hex');
console.log('Encrypted \'' + message + '\' as \'' + encrypted + '\' with key \''+ cipherKey.toString('hex') + '\' and IV \'' + cipherIV.toString('hex') + '\'');
// Outputs: Encrypted 'Hello world' as '2b8559ce4227c3c3c200ea126cb50957' with key '50f7a656cfa3c4f90796a972b2f6eedf41b589da705fdec95b9d25c180c16cf0' and IV '6b28c13d63af14cf05059a2a2caf370c'

var decipher = crypto.createDecipheriv('aes-256-cbc', cipherKey, cipherIV);
var decrypted = decipher.update(encrypted, 'hex', 'utf8') + decipher.final('utf8');
console.log('Decrypted \'' + encrypted + '\' as \'' + decrypted + '\' with key \''+ cipherKey.toString('hex') + '\' and IV \'' + cipherIV.toString('hex') + '\'');
// Outputs: Decrypted '2b8559ce4227c3c3c200ea126cb50957' as 'Hello world' with key '50f7a656cfa3c4f90796a972b2f6eedf41b589da705fdec95b9d25c180c16cf0' and IV '6b28c13d63af14cf05059a2a2caf370c'
然而,当我尝试使用错误的密钥解密消息时,可能是天真地证明攻击者无法解密消息,除非知道密钥,否则,我得到
错误:错误:06065064:数字信封例程:EVP_decryptfail_ex:decrypheriv.final(internal/crypto/cipher.js:164:28)

我希望得到的是无法理解的文本
bad decrypt
是其他SO问题中的特色,无论是关于加密和解密之间的openssl版本不匹配,还是在相同的情况下初始化向量太短,但我相信我的情况是不同的。AES是否知道加密文本是使用不同的密钥生成的

在Windows 10上的节点v12.13.0上测试,也在运行v10.16.0时测试

编辑: 正如回答中所建议的,问题在于默认填充,为了查看无法理解的输出,需要禁用密码和解密的自动填充,并手动填充:

var requirePadding = 16 - Buffer.byteLength(message, 'utf8');
var paddedMessage = Buffer.alloc(requirePadding, 0).toString('utf8') + message;
cipher.setAutoPadding(false)

CBC模式需要填充,您没有定义填充,但库为您应用了一个作为默认填充。默认值是支持1到256字节的块大小

每个填充都有特定的格式,因此可以从解密文本中唯一地删除它,而不会产生歧义。例如,如果明文缺少两个字符以匹配块大小,AES为16字节,则PKCS7填充添加
0202
(十六进制),指示添加了两个字符,每个字符都有值作为添加的字符数。如果以下
xy
中缺少5个
05050505
等,则为一个字节

xyxyxyxyxyxyxyxyxyxyxyxyxyxyxy01
xyxyxyxyxyxyxyxyxyxyxyxyxyxy0202
xyxyxyxyxyxyxyxyxyxyxyxyxy030303
...
xyxy0E0E0E0E0E0E0E0E0E0E0E0E0E0E
xy0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F
如果最后一个块是一个完整的块,那么一个完全填充了填充的新块

xyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxy 10101010101010101010101010101010
解密后,首先检查填充。如果填充没有中指定的正确格式,则可以说存在填充错误

在这种情况下,解密库时会检查填充并警告您。为了防止填充oracle攻击,您不会收到错误的填充警告。你解密得不好

库知道键的结果是否包含有效的填充,仅此而已。在完整性有帮助的情况下,可能会有多个键使用有效填充,即使可能性很小

在现代密码学中,我们不再使用CBC模式。我们更喜欢认证加密(AE)模式,如AES-GCM或ChaCha20-Poly1305。AE模式在捆绑包中提供机密性、完整性和身份验证

Galois计数器模式(GCM)在内部使用CTR模式,在该模式下没有填充,因此不会受到填充oracle攻击。

已正确地将该问题识别为填充问题。我可以这样总结这个问题:

  • 分组密码只能对长度为密码块大小倍数的数据进行操作。(AES的块大小为128位。)
  • 为了使各种大小的输入符合块大小,库添加了填充。此填充具有特定格式(例如,添加长度为N的填充时,对输入的最后N个字节重复值
    N
  • 解密时,库将检查是否存在正确的填充。由于解密不良的数据是任意噪声,因此不太可能有有效的pad

在执行
更新之前,您可以使用关闭此检查。但是,请注意,这将包括解密输出中的填充。这是一个使用
setAutoPadding

的函数,我不知道
PKCS7Padding/PKCS5Padding
padding如何与键相关,以及如何对键有效或无效?我的理解是,填充应用于输入数据以适应CBC模式下的块大小,而与with密钥无关。哦,我缺少的密钥概念是,填充在加密之前添加,然后包含在加密消息中,在消息解密后再次检查。错误的密钥将导致无法理解的消息,包括格式不正确的填充,因此可能会出现填充警告,从而导致最终的
错误解密
,以防止填充oracle攻击。这就是键与填充的交互方式,就是这样。如果您使用GCM或GCM内部使用的CTR模式,则根本不需要填充。@VaclavHonzik“错误的键将导致无法理解的消息,包括格式错误的填充”是的,但大约每256个填充中就有一个是意外正确的。“因此,最后一个错误的解密可以防止填充oracle攻击。”不!!!,在返回错误时,填充oracle攻击会加剧。如果没有任何错误或其他明显的(时间)效应,填充预言将无法工作。您也不需要使用任何密钥来执行填充oracle攻击。没有效果,就没有甲骨文,因为效果是甲骨文。请注意,上述仅适用于ECB和CBC模式,大多数其他模式(如计数器模式)根本不需要填充。
xyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxy 10101010101010101010101010101010