Crypto.decipher.final для алгоритма 'aes-256-cb c' с неверным ключом завершается неудачно с плохой расшифровкой - PullRequest
2 голосов
/ 05 февраля 2020

Я могу использовать node.js криптографический модуль для шифрования и дешифрования сообщения с использованием классов Cipher и Decipher с алгоритмом «aes-256-cb c», например:

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'

Однако Когда я пытаюсь расшифровать сообщение, используя неправильный ключ, чтобы, возможно, наивно, продемонстрировать, что злоумышленник не сможет расшифровать сообщение, пока ключ не известен, я получаю Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt at Decipheriv.final (internal/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, что зашифрованный текст был сгенерирован с другим ключом?

Протестировано на узле v12.13.0 на Windows 10, а также в repl.it под управлением v10.16.0.

РЕДАКТИРОВАТЬ: Как указывалось в ответах, проблема заключалась в заполнении по умолчанию, чтобы увидеть неразборчивый вывод, необходимо отключить автоматическое заполнение как для шифров, так и для дешифровщиков и для заполнения вручную:

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

Полный пример здесь

Ответы [ 2 ]

2 голосов
/ 05 февраля 2020

Другой ответ правильно определил проблему как проблему заполнения. Я мог бы обобщить проблему следующим образом:

  • Блочные шифры могут работать только с данными, длина которых кратна размеру блока шифра. (AES имеет размер блока 128 битов.)
  • Для того чтобы входные данные различного размера соответствовали размеру блока, библиотека добавляет заполнение. Это заполнение имеет особый формат (например, при добавлении заполнения длины N, повторите значение N для последних N байтов ввода.)
  • При расшифровке библиотека проверяет, существует ли правильное заполнение. Поскольку ваши плохо дешифрованные данные представляют собой произвольный шум, очень маловероятно, что у вас есть действующий пэд.

Вы можете отключить эту проверку с помощью decipher.setAutoPadding(false), прежде чем сделать update. Тем не менее, обратите внимание, что это будет включать заполнение в ваш расшифрованный вывод. Вот модифицированный экземпляр repl.it , который использует setAutoPadding.

2 голосов
/ 05 февраля 2020

Режим CB C требует заполнения, вы не определили его, но библиотека применила его для вас по умолчанию. По умолчанию PKCS7Padding , который поддерживает от 1 до 256 байтов размера блока.

Каждый отступ имеет определенный формат c, так что его можно однозначно удалить из расшифрованного текста без двусмысленности. Например, если в незашифрованном виде отсутствуют два символа для соответствия размеру блока, 16 байт в AES, то дополнение PKCS7 добавляет 0202 (в шестнадцатеричном формате), указывая, что добавлено 2 символа, и каждый из них имеет значение в качестве количества добавленных символов. Если 5 отсутствует 0505050505, et c. Ниже xy - это байт.

xyxyxyxyxyxyxyxyxyxyxyxyxyxyxy01
xyxyxyxyxyxyxyxyxyxyxyxyxyxy0202
xyxyxyxyxyxyxyxyxyxyxyxyxy030303
...
xyxy0E0E0E0E0E0E0E0E0E0E0E0E0E0E
xy0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F

и, если последний блок является полным блоком, новый блок, полностью заполненный заполнением

xyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxy 10101010101010101010101010101010

После дешифрования, во-первых заполнение проверено. если заполнение имеет неправильный формат, указанный в rf c 2315 , то можно сказать, что произошла ошибка заполнения.

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

Библиотека знает, что ключ дает корректное заполнение или нет, и ничего более. Может быть более одного ключа, который приводит к действительному заполнению, даже с незначительной вероятностью, когда целостность полезна.

В современной криптографии мы больше не используем режим CB C. Мы предпочитаем режимы аутентифицированного шифрования (AE), такие как AES-GCM или ChaCha20-Poly1305. Режимы AE обеспечивают конфиденциальность, целостность и аутентификацию в пакете.

Режим счетчика Галуа (GCM), внутренне использует режим CTR, в котором нет заполнения, поэтому они свободны от дополнений oracle атак.

...