Javascript: расшифровать строку, зашифрованную с помощью PHP

У меня есть код в PHP для шифрования строки.

$password = 'secret';
$iv_length = openssl_cipher_iv_length('AES-256-CBC');
$key = substr(sha1($password, true), 0, $iv_length);
$string = 'Brevity is the soul of wit';
$iv = hex2bin('2ceb02a85f6d4de6c28b2e59fda886d5');
$encrypted_string = openssl_encrypt($string, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
echo base64_encode($encrypted_string);

Возвращает зашифрованный текст в base64 Ky27sjhxUY7BFoscEgyFToxRDAsARzRFgQBIApbPmjQ=. Я передаю его и IV в javascript и пытаюсь расшифровать с помощью этого кода:

const crypto = require("crypto");
const Algorithm = "aes-256-cbc";

function encrypt(plainText, key, iv, outputEncoding = "base64") {
    const cipher = crypto.createCipheriv(Algorithm, key, iv);
    const output = Buffer.concat([cipher.update(plainText), cipher.final()]).toString(outputEncoding);
    return output.replace('+', '-').replace('/', '_').replace('=', '');
}

function decrypt(cipherText, key, iv, outputEncoding = "base64") {
    cipherText = Buffer.from(cipherText.replace('-', '+').replace('_', '/'), "base64");
    const cipher = crypto.createDecipheriv(Algorithm, key, iv, {

    });
    return Buffer.concat([cipher.update(cipherText), cipher.final()]).toString(outputEncoding);
}

const string = "Brevity is the soul of wit";

const password = 'secret';
const iv_length = 16;
let KEY = crypto
    .createHash('sha1')
    .update(password)
    .digest('bin')
    .slice(0, iv_length);

const IV = Buffer.from('2ceb02a85f6d4de6c28b2e59fda886d5', 'hex');

const decrypted = decrypt('GIyzBeAUpxr93RIyjUZZ6W4lm/qHiF1lf/8drSEgfGs=', KEY, IV, "utf8")
console.info("Decrypted:", decrypted);

Но он терпит неудачу с этой ошибкой:

internal/crypto/cipher.js:103
    this[kHandle].initiv(cipher, credential, iv, authTagLength);
                  ^

Error: Invalid key length
    at Decipheriv.createCipherBase (internal/crypto/cipher.js:103:19)
    at Decipheriv.createCipherWithIV (internal/crypto/cipher.js:121:20)
    at new Decipheriv (internal/crypto/cipher.js:264:22)
    at Object.createDecipheriv (crypto.js:130:10)
    at decrypt (/Users/sacram/code/decrypt/test.js:12:24)
    at Object.<anonymous> (/Users/sacram/code/decrypt/test.js:36:19)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)

Можно ли расшифровать его без изменения кода PHP?

Я пытаюсь расшифровать строку с помощью js, которая была зашифрована в php.

В коде NodeJS отсутствует KEY = Buffer.concat([KEY, Buffer.alloc(16, '\0')]), что неявно происходит в коде PHP (заполнение слишком коротких ключей). Обратите внимание, что SHA1 и добавление значений 0x00 в генерацию ключа являются уязвимостями.

Topaco 03.04.2023 14:28

@Topaco, вы хотите опубликовать ответ или это дубликат или существующий вопрос?

Mike Szyndel 03.04.2023 15:41

@Topaco, не могли бы вы опубликовать свой комментарий в качестве ответа, и я приму его как правильный ответ. Большое спасибо.

Andrey A 03.04.2023 20:23

Пожалуйста. Я поставил свой комментарий в качестве ответа.

Topaco 03.04.2023 23:13
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
4
118
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В обоих кодах длина ключа определяется по заданному алгоритму: AES-256-CBC требуется ключ размером 32 байта.

В коде PHP/OpenSSL в качестве ключа используются первые 16 байт хэша SHA-1 пароля. Слишком короткий ключ автоматически дополняется значениями 0x00 до требуемой длины в 32 байта (слишком длинный ключ будет автоматически усечен до требуемой длины в конце), см. описание параметра фразы-пароля в openssl_encrypt() документация.

В коде NodeJS ключ также определяется как первые 16 байт хэша SHA-1 пароля. Однако, в отличие от кода PHP/OpenSSL, ключ не дополняется (и не усекается). Ключ неправильной длины идентифицируется как недопустимый, и выдается соответствующее исключение. Это причина возникновения исключения в коде NodeJS.

Чтобы в коде NodeJS использовался тот же ключ, что и в коде PHP, в код NodeJS необходимо добавить следующее:

KEY = Buffer.concat([KEY, Buffer.alloc(16, '\0')])

Код PHP имеет ряд уязвимостей:

  • Использование быстрого дайджеста для получения ключа: вместо этого следует использовать специальную функцию получения ключа (KDF), такую ​​как Argon2 или, по крайней мере, PBKDF2 (для замедления возможных злоумышленников). Здесь для каждого шифрования применяется случайная соль, которая передается вместе с зашифрованным текстом на расшифровывающую сторону, как правило, конкатенированная (соль не является секретной).
  • Расширение ключа с 16 до 32 байтов со значениями 0x00: вместо этого следует напрямую сгенерировать 32-байтовый ключ (хотя это может быть достигнуто с помощью подходящего дайджеста, такого как SHA256, следует применить KDF, как упоминалось в предыдущем разделе). ; обратите внимание, что KDF позволяют указать размер ключа).
  • Использование статического IV: вместо этого для каждого шифрования должен генерироваться случайный IV и передаваться вместе с зашифрованным текстом на дешифрующую сторону, обычно объединенным (IV не является секретным). Для целей тестирования, конечно, статический IV подходит.

Другие вопросы по теме