C++ AES Decrypt и Nodejs AES Decrypt производят разные выходные данные

Я повторно реализовал эту расшифровку AES C++ в nodejs.

«Буфер» содержит зашифрованное содержимое. «decryptKey» содержит ключ для расшифровки «буфера». "expectedOutput" содержит ожидаемый результат.

Чтобы обойти исключение плохого дешифрования, вызванное узлом, мне пришлось отключить автозаполнение в моем объекте шифрования.

Для упрощения я добавил код 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
*/

Вот код С++

#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');

person Lothre1    schedule 03.07.2018    source источник
comment
aes_crypt_ecb - В этот момент должны раздаться громадные предупредительные сирены! НЕ используйте ECB ... если вы не используете ECB для этой тестовой программы, а другой режим просто добавит дополнительные сложности, которые не имеют отношения к вашей проблеме (в этом случае - респект).   -  person Martin Bonner supports Monica    schedule 03.07.2018


Ответы (2)


устарело crypto.createDecypher() < strong>получает ключ из аргумента пароль:

Реализация crypto.createDecipher() выводит ключи с помощью функции OpenSSL EVP_BytesToKey с алгоритмом дайджеста, установленным на MD5, одну итерацию и без соли.

Вместо этого вы хотите использовать необработанный ключ. Для этого вы должны вместо этого использовать crypto.createDecipheriv():

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

(В режиме ECB IV может быть пустым)

Разумеется, длина ключа должна соответствовать запрошенному алгоритму (128, 192 или 256 бит). Ваш ключ 128 бит, поэтому будет работать только aes-128-ecb.

person rustyx    schedule 04.07.2018
comment
Раньше я пытался использовать createDecipheriv, но я передавал 16-байтовые ключи, заполненные 0x00. После использования пустого буфера, как было предложено, и автоматического заполнения он волшебным образом устранил проблему. - person Lothre1; 04.07.2018

Причина, по которой вам нужно передать false в setAutoPadding, заключается в том, что ваш зашифрованный текст не дополнен. Вы не включили свой код шифрования, который генерирует зашифрованный текст, но я могу сказать по его длине, что он не дополнен, потому что открытый текст длиной в один блок при шифровании с использованием заполнения PKCS # 7 будет создавать зашифрованный текст это два размера блока, а не один блок, который у вас есть.

Причина, по которой это расшифровывается с помощью aes_crypt_ecb, заключается в том, что похоже, что aes_crypt_ecb просто выполняет шифрование/дешифрование отдельных блоков, поэтому не выполняет заполнение или распаковку. Однако код Javscript, если передать true функции setAutoPadding, будет ожидать заполнения (вероятно, PKCS#7). Что, конечно же, не удастся, потому что ваше шифрование не использует заполнение.

В идеале вы должны изменить свой код шифрования, чтобы он использовал правильное заполнение при шифровании, так как это намного безопаснее, чем не использовать заполнение. В противном случае вам нужно будет убедиться, что ваш вызов setAutoPadding передает false при расшифровке, поэтому Javascript знает, что нет заполнения.

person Sean Burton    schedule 03.07.2018
comment
Я только что обновил свой пост. Дело в том, что какой бы алгоритм не использовался, ожидаемый результат не достигается. - person Lothre1; 03.07.2018
comment
На стороне С++ вы используете один и тот же буфер как для ввода, так и для вывода функции aes_crypt_ecb. Это может не быть проблемой, но кажется необычным. Получаете ли вы другой результат, если используете отдельный буфер для вывода? В С++ вы снова говорите, что ключ имеет длину всего 128 бит, в то время как ваш массив ключей на самом деле имеет длину 256 бит. - person Sean Burton; 04.07.2018
comment
Да, вывод такой же, даже если я разделю выходной буфер. - person Lothre1; 04.07.2018
comment
@ Lothre1 Я отредактировал свой ответ, указав, что теперь я считаю проблемой. - person Sean Burton; 04.07.2018
comment
Посмотрев код C++, я заметил, что библиотека нарезает мой ключ шифрования до 128 бит. Я добавил ссылку на онлайн-инструмент расшифровки AES, который может выдавать тот же результат, что и моя программа на С++. Может быть, это может помочь. - person Lothre1; 04.07.2018
comment
Этот онлайн-инструмент не использует отступы или, по крайней мере, не проверяет их правильность. Я добавил несколько случайных символов в конец зашифрованного текста и попытался расшифровать, и это удалось. Если бы он ожидал заполнения, это не удалось бы, поскольку заполнение было бы неправильным. - person Sean Burton; 04.07.2018