【问题标题】:C++ AES Decrypt and Nodejs AES Decrypt producing different outputsC++ AES Decrypt 和 Nodejs AES Decrypt 产生不同的输出
【发布时间】:2018-12-11 15:15:16
【问题描述】:

我在 nodejs 中重新实现了这个 AES c++ 解密。

“缓冲区”包含加密内容。 “decryptKey”包含解密“缓冲区”的密钥。 “expectedOutput”包含预期的输出。

为了绕过节点抛出的错误解密异常,我必须在我的 crypt 对象中禁用 autoPadding。

为了简化,我添加了 c++ 代码,并对三种不同的算法(AES-128-ECB、AES-192-ECB、AES-256-ECB)添加了测试。没有一个解密结果与 C++ 输出匹配。

我错过了什么?

var crypto = require('crypto');

var buffer = new Buffer([
    0x5e,0x51,0xa3,0x53,0x9d,0xe7,0xe5,0xd3,
    0xee,0x30,0xbb,0xf8,0x0c,0x72,0x9f,0x80
]);


var decryptKey = new Buffer([
    0x36, 0x46, 0xb4, 0xf6, 
    0x8e, 0x6d, 0xdc, 0xf4, 
    0xb0, 0x31, 0x7e, 0x81, 
    0x6b, 0x5d, 0x96, 0x55
])

/*
After looking to my C++ code I noticed that despite of providing a 32 length key the 128 argument ensures that only the first 16 bytes are used
var decryptKey = new Buffer([
    0x36, 0x46, 0xb4, 0xf6, 
    0x8e, 0x6d, 0xdc, 0xf4, 
    0xb0, 0x31, 0x7e, 0x81, 
    0x6b, 0x5d, 0x96, 0x55, // 16
    0x15, 0x9c, 0x78, 0x54, 
    0x8c, 0xca, 0x3e, 0x39, 
    0x2d, 0x49, 0x75, 0x5d, 
    0xa1, 0x1a, 0xc3, 0xe3  // 32
])*/

var expectedOutput = new Buffer([
    0xc8,0x6c,0x8f,0x2b,0xe8,0x21,0xc4,0x2e,
    0xfb,0x4a,0x8e,0x8b,0xc3,0x94,0x19,0xc2
]);

// aes_context aes_ctx;
function decrypt(data, password, algorithm, padding){
  if (padding === void 0) padding = true;
  algorithm = algorithm || 'aes-128-ecb';

  //aes_setkey_dec( &aes_ctx, digest, 128 );
  var crypt = crypto.createDecipher(algorithm,password);
  crypt.setAutoPadding(padding);
  // aes_crypt_ecb( &aes_ctx, AES_DECRYPT, buffer, buffer );
  var res = crypt.update(data, null, 'hex')
  res += crypt.final('hex');

  return new Buffer(res,'hex');
}

// aes_setkey_dec( &aes_ctx, digest, 128 );
var algoList = [
    'aes-128-ecb',
    'aes-192-ecb',
    'aes-256-ecb'
];

for (var i = 0; i<= 1; i++){
    console.log('\n ******* AUTO PADDING: ' + (padding ? 'ON': 'OFF') + ' ********* ');
    var padding = i === 0;
    for (let algo of algoList){
        try {
            var output = decrypt(buffer, decryptKey, algo, padding);
            console.log(algo + ' => ' + output.toString('hex') + ' < ' + (Buffer.compare(expectedOutput, output) === 0 ? 'ok' : 'ko'))
        } catch (err){
            console.log('Failed to perform ' + algo + ' with autopadding ' + (padding ? ' on ': ' off ') + ' due to ' + err.message);
        }
    }
}

/*
 ******* AUTO PADDING: OFF *********
Failed to perform aes-128-ecb with autopadding  on  due to error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
Failed to perform aes-192-ecb with autopadding  on  due to error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
Failed to perform aes-256-ecb with autopadding  on  due to error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt

 ******* AUTO PADDING: ON *********
aes-128-ecb => d9817f142f9bca262b67f6a6be570345 < ko
aes-192-ecb => 9181784373bb6060c04c9ba75de26322 < ko
aes-256-ecb => c5945203368de477e5f0dbeedeb2189f < ko
*/

这是 c++ 代码

#include "aes.h"
#include "sha2.h"

int main(int argc, char *argv[]) {


    unsigned char data[16] = { 
        0x5e,0x51,0xa3,0x53,0x9d,0xe7,0xe5,0xd3,0xee,0x30,0xbb,0xf8,0x0c,0x72,0x9f,0x80 
    };

    unsigned char key[32] = {
        0x36, 0x46, 0xb4, 0xf6,
        0x8e, 0x6d, 0xdc, 0xf4,
        0xb0, 0x31, 0x7e, 0x81,
        0x6b, 0x5d, 0x96, 0x55, // 16
        0x15, 0x9c, 0x78, 0x54,
        0x8c, 0xca, 0x3e, 0x39,
        0x2d, 0x49, 0x75, 0x5d,
        0xa1, 0x1a, 0xc3, 0xe3  // 32
    };

    aes_context aes_ctx;
    aes_setkey_dec(&aes_ctx, key, 128);
    aes_crypt_ecb(&aes_ctx, AES_DECRYPT, data, data);

    for (int i = 0; i< sizeof(data); ++i)
        std::cout << std::hex << (int)data[i];

    /* Output => c86c8f2be821c42efb4a8e8bc39419c2*/
}

参考:


基于以下答案的解决方案

var crypto = require('crypto')
var buffer = new Buffer([
    0x5e,0x51,0xa3,0x53,0x9d,0xe7,0xe5,0xd3,
    0xee,0x30,0xbb,0xf8,0x0c,0x72,0x9f,0x80
]);


var decryptKey = new Buffer([
    0x36, 0x46, 0xb4, 0xf6, 
    0x8e, 0x6d, 0xdc, 0xf4, 
    0xb0, 0x31, 0x7e, 0x81, 
    0x6b, 0x5d, 0x96, 0x55
])

var expectedOutput = new Buffer([
    0xc8,0x6c,0x8f,0x2b,0xe8,0x21,0xc4,0x2e,
    0xfb,0x4a,0x8e,0x8b,0xc3,0x94,0x19,0xc2
]);

// aes_context aes_ctx;
function decrypt(data, password, algorithm, padding){
  if (padding === void 0) padding = true;
  algorithm = algorithm || 'aes-128-ecb';

  //aes_setkey_dec( &aes_ctx, digest, 128 );
  var crypt = crypto.createDecipheriv(algorithm,password, new Buffer([]));//new Buffer(32).fill(0).byteLength
  crypt.setAutoPadding(padding);
  // aes_crypt_ecb( &aes_ctx, AES_DECRYPT, buffer, buffer );
  var res = crypt.update(data, null,'hex')
  + crypt.final('hex');

  return new Buffer(res,'hex');
}

// aes_setkey_dec( &aes_ctx, digest, 128 );
var output = decrypt(buffer, decryptKey, 'aes-128-ecb', false);
console.log(Buffer.compare(expectedOutput, output) === 0 ? 'ok' : 'ko');

【问题讨论】:

  • aes_crypt_ecb - 此时巨大的警告喇叭应该响起! NOT 是否使用 ECB ... 除非您将 ECB 用于此测试程序,并且另一种模式只会增加与您的问题无关的额外复杂性(在这种情况下 -荣誉)。

标签: c++ node.js encryption aes


【解决方案1】:

已弃用crypto.createDecypher()派生来自密码参数的密钥:

crypto.createDecipher() 的实现使用 OpenSSL 函数 EVP_BytesToKey 派生密钥,摘要算法设置为 MD5,一次迭代,无盐。

您想要的是使用 raw 键。为此,您应该改用crypto.createDecipheriv()

  var crypt = crypto.createDecipheriv(algorithm,password,new Buffer([]));

(在 ECB 模式下,IV 可以为空)

当然,密钥长度必须与请求的算法相匹配(128、192 或 256 位)。你的密钥是 128 位,所以只有 aes-128-ecb 可以工作。

【讨论】:

  • 我之前尝试使用 createDecipheriv,但我传递了一个 16 字节的密钥,其中填充了 0x00。在按照建议使用空缓冲区并自动填充后,它神奇地解决了问题
【解决方案2】:

您需要将false 传递给setAutoPadding 的原因是您的密文没有填充。您没有包含生成密文的加密代码,但我可以从它的长度看出它没有被填充,因为长度为一个块大小的明文,当使用 PKCS#7 填充加密时,将产生一个密文即 两个 个块大小的长度,而不是您拥有的单个块。

使用aes_crypt_ecb 解密的原因是它看起来像aes_crypt_ecb 只是执行单个块加密/解密, 不进行任何填充或取消填充。但是,如果将 true 传递给 setAutoPadding 函数,Javscript 代码将需要填充(可能是 PKCS#7)。这当然会失败,因为您的加密没有使用了填充。

理想情况下,您应该修改您的加密代码,以便在进行加密时使用适当的填充,因为这比不使用填充更安全。否则,您需要确保在进行解密时您对setAutoPadding 的调用通过false,以便Javascript 知道没有填充。

【讨论】:

  • 我刚刚更新了我的帖子。问题是,无论使用哪种算法,都没有达到预期的输出。
  • 在 C++ 端,您使用相同的缓冲区作为aes_crypt_ecb 函数的输入和输出。这可能不是问题,但似乎不寻常。如果您使用单独的缓冲区作为输出,您会得到不同的结果吗?在 C++ 中,您再次告诉它密钥只有 128 位,而您的密钥数组实际上是 256 位长。
  • 是的,即使我将输出缓冲区分开,输出也是一样的。
  • @Lothre1 我已经用我现在认为的问题编辑了我的答案。
  • 查看 c++ 代码后,我注意到该库将我的加密密钥切成 128 位。我添加了一个在线 AES 解密工具的链接,该工具能够产生与我的 c++ 程序相同的输出。也许这会有所帮助。
猜你喜欢
  • 2022-12-02
  • 2022-01-25
  • 2022-12-02
  • 2021-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-18
  • 2019-03-10
相关资源
最近更新 更多