RSA-CBC расшифровывает его с помощью C++

Я застрял с проблемой расшифровки зашифрованной строки AES-CBC. У меня есть код JS, который расшифровывает эту строку, но мне нужно сделать это на С++. Ключ — это строка хэша SHA512, а сообщение — строка Base64. JS-код для расшифровки:

CryptoJS.algo.AES.keySize = 32,
 CryptoJS.algo.EvpKDF.cfg.iterations = 10000,
  CryptoJS.algo.EvpKDF.cfg.keySize = 32;
var r = CryptoJS.AES.decrypt(message, key.toString());

Мой код C++ не работает

std::string generateIV(std::string key)
{
    std::string iv(CryptoPP::AES::BLOCKSIZE, 0);
    CryptoPP::SHA1().CalculateDigest((byte*)iv.data(), (byte*)key.data(), key.size());
    return iv;
}
std::string decrypt(std::string &message, std::string &key) {

    std::string decrypted;
    std::string iv(generateIV(key));

    // Create the AES decryption object
    CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption aesDecryption;
    aesDecryption.SetKeyWithIV((byte*)key.data(), key.size(), (byte*)iv.data(), iv.size());

    // Decrypt the message
    CryptoPP::StringSource ss(message, true,
        new CryptoPP::StreamTransformationFilter(aesDecryption,
            new CryptoPP::StringSink(decrypted)
        )
    );
    return decrypted;
}

Может быть, мне следует использовать OpenSSL?

CryptoJS использует специфичную для OpenSSL функцию получения ключа на основе пароля, если вы укажете парольную фразу: «Если вы используете парольную фразу, она сгенерирует 256-битный ключ». Для такого рода шизла и глупой обратной совместимости (командная строка OpenSSL не так безопасна) я предлагаю вам использовать библиотеку OpenSSL, см., например. здесь. Да, АОКрипто дурак, что назвал функцию просто encrypt со странными перегрузками.

Maarten Bodewes 24.01.2023 00:02

Уже поздно, поэтому я использовал глупость слишком много раз. Но мне тоже приходилось слишком много раз отвечать на вопрос.

Maarten Bodewes 24.01.2023 00:22
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
79
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Зашифрованный текст, сгенерированный опубликованным кодом CryptoJS, не может быть расшифрован какой-либо библиотекой, совместимой с AES. Это из-за линии

CryptoJS.algo.AES.keySize = 32

который определяет размер ключа 32 слова = 32 * 4 = 128 байтов для получения ключа. Это недопустимый размер ключа AES, и полученное количество раундов вообще не определено для AES (38 раундов для 128 байт, см. здесь ; AES определяет только 10, 12 и 14 раундов в зависимости от размера ключа). Таким образом, зашифрованный текст не соответствует стандарту AES. Его можно расшифровать с помощью CryptoJS, но не с помощью какой-либо библиотеки, совместимой с AES, см. также этот выпуск CryptoJS #293. Чтобы сгенерированный зашифрованный текст был совместим с AES, необходимо использовать один из разрешенных размеров ключа AES, например. размер ключа 8 слов = 32 байта:

CryptoJS.algo.AES.keySize = 8

Кроме того, обратите внимание на эту строку

CryptoJS.algo.EvpKDF.cfg.iterations = 10000

приводит к несовместимости с интерфейсом командной строки OpenSSL, который по умолчанию использует число итераций, равное 1, при получении ключа (что является одной из причин слабости этого вывода ключа, см. здесь).

Кстати, линия

CryptoJS.algo.EvpKDF.cfg.keySize = 32

полностью игнорируется обработкой и также может быть опущен.


Если используется действительный размер ключа AES, например. 8 слов = 32 байта:

CryptoJS.algo.AES.keySize = 8, // 8 words = 32 bytes
 CryptoJS.algo.EvpKDF.cfg.iterations = 10000,
  CryptoJS.algo.EvpKDF.cfg.keySize = 32;
var r = CryptoJS.AES.decrypt(message, key.toString());

зашифрованный текст может быть расшифрован программно. Как уже упоминалось в комментариях, CryptoJS использует собственную функцию получения ключа OpenSSL EVP_BytesToKey(), если материал ключа передается в виде строки. Это генерирует 8-байтовую соль во время шифрования и использует соль и пароль для получения ключа и IV. Они используются для шифрования в режиме CBC с заполнением PKCS#7 по умолчанию. OpenSSL форматирует результат шифрования как конкатенацию кодировки ASCII Salted__, за которой следует 8-байтовая соль и, наконец, фактический зашифрованный текст, обычно закодированный Base64.

Для расшифровки соль и зашифрованный текст должны быть разделены. Затем на основе соли и пароля определяются ключ и IV, с помощью которых окончательно расшифровывается зашифрованный текст.
Таким образом, для расшифровки необходима реализация для EVP_BytesToKey(). Такую реализацию можно найти для Crypto++ здесь в документации Crypto++, а код, с помощью которого можно расшифровать зашифрованный текст кода CryptoJS (после исправления проблемы с размером ключа), например:

#include "aes.h"
#include "modes.h"
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
#include "md5.h"
#include "base64.h"
#include "secblock.h"

static int OPENSSL_PKCS5_SALT_LEN = 8;

int OPENSSL_EVP_BytesToKey(CryptoPP::HashTransformation& hash, const unsigned char* salt, const unsigned char* data, int dlen, unsigned int count, unsigned char* key, unsigned int ksize, unsigned char* iv, unsigned int vsize);

int main(int, char**) {

    // Pass data and parameter
    std::string passphrase = "my passphrase";
    std::string encryptedB64 = "U2FsdGVkX18AuE7abdK11z8Cgn3Nc+2cELB1sWIPhAJXBZGhnw45P4l58o33IEiJ8fV4oEid2L8wKXpAntPrAQ= = "; // CryptoJS ciphertext for a 32 bytes keysize 
    std::string encrypted;
    int iterationCount = 10000;
    int keySize = 32;

    // Base64 decode
    CryptoPP::StringSource ssB64decodeCt(encryptedB64, true,
        new CryptoPP::Base64Decoder(
            new CryptoPP::StringSink(encrypted)
        )
    );

    // Separate
    std::string salt(encrypted.substr(8, 8));
    std::string ciphertext(encrypted.substr(16));

    // Derive key
    CryptoPP::SecByteBlock key(keySize), iv(16);
    CryptoPP::Weak::MD5 md5;
    OPENSSL_EVP_BytesToKey(md5, (const unsigned char*)salt.data(), (const unsigned char*)passphrase.data(), passphrase.size(), iterationCount, key.data(), key.size(), iv.data(), iv.size());

    // Decryption
    std::string decryptedText;
    CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decryption(key.data(), key.size(), iv.data());
    CryptoPP::StringSource ssDecryptCt(
        ciphertext,
        true,
        new CryptoPP::StreamTransformationFilter(
            decryption,
            new CryptoPP::StringSink(decryptedText),
            CryptoPP::BlockPaddingSchemeDef::BlockPaddingScheme::PKCS_PADDING
        )
    );

    // Output
    std::cout << decryptedText << std::endl; // The quick brown fox jumps over the lazy dog

    return 0;
}

// from: https://www.cryptopp.com/wiki/OPENSSL_EVP_BytesToKey
int OPENSSL_EVP_BytesToKey(CryptoPP::HashTransformation& hash, const unsigned char* salt, const unsigned char* data, int dlen, unsigned int count, unsigned char* key, unsigned int ksize, unsigned char* iv, unsigned int vsize)
{
    if (data == NULL) return (0);

    unsigned int nkey = ksize;
    unsigned int niv = vsize;
    unsigned int nhash = hash.DigestSize();
    CryptoPP::SecByteBlock digest(nhash);

    unsigned int addmd = 0, i;

    for (;;)
    {
        hash.Restart();

        if (addmd++)
            hash.Update(digest.data(), digest.size());

        hash.Update(data, dlen);

        if (salt != NULL)
            hash.Update(salt, OPENSSL_PKCS5_SALT_LEN);

        hash.TruncatedFinal(digest.data(), digest.size());

        for (i = 1; i < count; i++)
        {
            hash.Restart();
            hash.Update(digest.data(), digest.size());
            hash.TruncatedFinal(digest.data(), digest.size());
        }

        i = 0;
        if (nkey)
        {
            for (;;)
            {
                if (nkey == 0) break;
                if (i == nhash) break;
                if (key != NULL)
                    *(key++) = digest[i];
                nkey--;
                i++;
            }
        }
        if (niv && (i != nhash))
        {
            for (;;)
            {
                if (niv == 0) break;
                if (i == nhash) break;
                if (iv != NULL)
                    *(iv++) = digest[i];
                niv--;
                i++;
            }
        }
        if ((nkey == 0) && (niv == 0)) break;
    }

    return ksize;
}

Спасибо за ваш ответ! Я скомпилировал код, и программа показывает мне диалоговое окно «Вызов abort()» :(

DreamCoder 25.01.2023 01:49

@DreamCoder - код работает, по крайней мере, на моей машине. Возможно, есть ошибка, которая не вызывает исключения в моей среде, но делает это в вашей. Или, может быть, вам просто нужно адаптировать его для вашей среды. Итак, попытайтесь выяснить, что вызывает проблему в вашей среде, например. отладка, комментирование областей кода и т. д., пока вы не найдете точное местоположение исходного кода, которое вызывает исключение.

Topaco 25.01.2023 08:27

Хорошо, я понял. Я прав, если мне нужно расшифровать зашифрованный текст, сгенерированный кодом CryptoJS, я должен скопировать полную реализацию кода CryptoJS AES на C++? Возможна ли эта задача?

DreamCoder 25.01.2023 15:13

@DreamCoder - вы не опубликовали код CryptoJS для шифрования, поэтому на него можно ответить только в общих чертах: вам необходимо реализовать дешифрование в коде C++, которое является точным аналогом шифрования в коде CryptoJS. Так что, если код CryptoJS соответствует стандартам и не делает сумасшедших вещей (например, не применяет 128-байтовый ключ), это, как правило, возможно.

Topaco 25.01.2023 16:12

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