当前位置: 首页 > 知识库问答 >
问题:

加密。破译具有无效密钥的“aes-256-cbc”算法的最终结果因解密错误而失败

高祺
2023-03-14

我能够使用节点。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_DecryptFinal_ex:Decrypheriv的解密错误。最终版本(内部/crypto/cipher.js:164:28)

var differentCipherKey = crypto.randomBytes(32);
var decipherDifferentKey = crypto.createDecipheriv('aes-256-cbc', differentCipherKey, cipherIV);
decrypted = decipherDifferentKey.update(encrypted, 'hex', 'utf8') + decipherDifferentKey.final('utf8');

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

在Windows 10和运行v10.16.0的repl.it节点v12.13.0上测试。

编辑:如答案中所示,问题是默认填充,为了看到不可理解的输出,需要禁用密码和破译器上的自动填充,并手动填充:

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

这里有完整的例子

共有2个答案

姜志行
2023-03-14

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

每个填充都有特定的格式,这样它就可以从解密文本中唯一地删除,而不会有歧义。例如,如果明文缺少两个字符来匹配块大小,在AES中为16字节,那么PKCS7填充将添加0202(十六进制),指示添加了2个字符,每个字符都有值作为添加的字符数。如果5缺少0505050505等。在下面的xy是一个字节。

xyxyxyxyxyxyxyxyxyxyxyxyxyxyxy01
xyxyxyxyxyxyxyxyxyxyxyxyxyxy0202
xyxyxyxyxyxyxyxyxyxyxyxyxy030303
...
xyxy0E0E0E0E0E0E0E0E0E0E0E0E0E0E
xy0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F

如果最后一个块是一个完整的块,那么一个完全填充了填充的新块

xyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxy 10101010101010101010101010101010

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

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

库知道键的结果是否有有效的填充,仅此而已。可能有不止一个键会导致有效的填充,即使完整性有帮助的概率可以忽略不计。

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

Galois计数器模式(GCM)在内部使用CTR模式,其中没有填充,因此它们不受填充oracle攻击。

乐正迪
2023-03-14

另一个答案正确地将该问题确定为填充问题。我可以这样总结这个问题:

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

您可以使用解密关闭此检查。在执行更新之前设置自动添加(false)。但是,请注意,这将包括解密输出中的填充。这是一个修改过的repl。它是使用setAutoPadding的实例。

 类似资料:
  • 我做了一个简单的文件加密/解密器。它将模式和要操作的文件作为参数。加密时,它生成随机字符串并使用该字符串加密文件。解密文件时,它会提示用户输入密码,并在解密时使用该密码。 我的问题是,解密时得到的不是明文,而是胡言乱语,尽管我小心翼翼地将相同的密钥写入输入。 非常感谢James K Polk提供的加密/解密代码!

  • 我希望有一个用C编写的程序,可以在没有openssl这样的大型库的帮助下,用AES-CBC对字符串进行编码/解码。 目标: 使用密码短语对字符串进行编码/解码: 因此,应用程序需要接受3个输入参数。。。 输入字符串(待编码)/或已编码字符串(待解码) 用于编码/解码字符串的密码 编码或解码指示器 我对C语言不熟悉(我可以用C#编码)。 我已经找到了https://github.com/kokke/

  • 我在这个网站上用AES-256加密一个虚拟字符串: https://www.devglan.com/online-tools/aes-encryption-decryption 具有以下参数: null 当我尝试用OpenSSL从命令行解密它时: 我得到这个错误:

  • 我在Erlang中加密AES 256位CBC然后用c代码解密它时遇到了一个问题。而加密/解密在Erlang和C中有效,但不能从一个到另一个。 和 c 代码 我得到的错误是 当我在Erlang中解密时,它可以正常工作,当我在C中加密时,它也可以使用相同的Key和IV。它是否是密码模式不匹配。虽然在我看来是正确的。任何指针都会非常有用。谢谢。 我算出,同样的数据我用C加密和解密,然后进行十六进制转储。