Зашифровать и расшифровать строку с помощью OpenSSL

Я не понимаю, как изменить размер cipher_buf и buffer, которые будут содержать зашифрованную/расшифрованную строку.

И как преобразовать зашифрованное buffer в base64? Мне понадобится библиотека кодировщика base64 или для этого есть API на OpenSSL?

#include <openssl/aes.h>
#include <openssl/evp.h>
#include <Windows.h>

void decryptOrEncrypt()
{
    bool encrypt = true;

    std::string str = "testing testing";

    const int MAX_BUFFER_SIZE = str.length();

    int out_len;
    EVP_CIPHER_CTX * ctx = EVP_CIPHER_CTX_new();

    std::string key = "abcdabcdabcdabcd";
    std::string iv = "abcdabcdabcdabcd";

    size_t len = key.size();
    byte* keyPtr = (byte*)key.data();
    byte* ivPtr = (byte*)iv.data();

    EVP_CipherInit(ctx, EVP_aes_128_cbc(), keyPtr, ivPtr , encrypt);
    int blocksize = EVP_CIPHER_CTX_block_size(ctx);
    std::string cipher_buf;
    cipher_buf.resize(MAX_BUFFER_SIZE + blocksize);

    std::string buffer;
    buffer.resize(MAX_BUFFER_SIZE);

    EVP_CipherUpdate(ctx, reinterpret_cast<uchar *>(cipher_buf.data())
        , &out_len, reinterpret_cast<uchar *>(str.data()), str.length());
    buffer.append(cipher_buf.data(), out_len);

    EVP_CipherFinal(ctx, reinterpret_cast<uchar *>(cipher_buf.data()), &out_len);
    buffer.append(cipher_buf.data(), out_len);
    
    auto s = buffer.size(); 

    //std::string test = base64_encode(buffer.c_str(), buffer.length());
    //std::string test = base64_decode(buffer);
    EVP_CIPHER_CTX_free(ctx);

    return;
}


INT main(INT argc, PCHAR* argv)
{
    decryptOrEncrypt();
}

Проблема с приведенным выше кодом заключается в том, что после последнего buffer.append buffer пуст (проверка данных в отладчике Visual Studio), но его размер auto s = buffer.size() равен 31.

На самом деле я не уверен, что вы ожидаете от этого кода, он ничего не выводит. Можете ли вы уточнить это, а также убедиться, что он соответствует минимально воспроизводимому примеру? Подсказка: это должно сделать вашу ошибку очевидной! Кстати: лучшим типом для буферов является std::vector, который вы создаете с правильным типом, чтобы избавиться от всех ненужных приведений типов в вашем коде.

Ulrich Eckhardt 20.11.2022 19:54

@UlrichEckhardt, разве мой код не является воспроизводимым примером? «Это должно сделать вашу ошибку очевидной», какую ошибку вы имеете в виду? На моей стороне компилируется без ошибок.

Natalia 20.11.2022 20:05

«На самом деле я не уверен, что вы ожидаете от этого кода, он ничего не выводит». Сначала я пытаюсь заставить шифрование/дешифрование работать, чтобы завершить функцию.

Natalia 20.11.2022 20:09
[JS за 1 час] - 9. Асинхронный
[JS за 1 час] - 9. Асинхронный
JavaScript является однопоточным, то есть он может обрабатывать только одну задачу за раз. Для обработки длительных задач, таких как сетевые запросы,...
Топ-10 компаний-разработчиков PHP
Топ-10 компаний-разработчиков PHP
Если вы ищете надежных разработчиков PHP рядом с вами, вот список лучших компаний по разработке PHP.
Скраппинг поиска Apple App Store с помощью Python
Скраппинг поиска Apple App Store с помощью Python
📌Примечание: В этой статье я покажу вам, как скрапировать поиск Apple App Store и получить точно такой же результат, как на Apple iMac, потому что...
Редкие достижения на Github ✨
Редкие достижения на Github ✨
Редкая коллекция доступна в профиле на GitHub ✨
Подъем в javascript
Подъем в javascript
Hoisting - это поведение в JavaScript, при котором переменные и объявления функций автоматически "перемещаются" в верхнюю часть соответствующих...
Улучшение генерации файлов Angular
Улучшение генерации файлов Angular
Angular - это фреймворк. Вы можете создать практически любое приложение без использования сторонних библиотек.
1
3
104
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

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

После этой строки:

   buffer.resize(MAX_BUFFER_SIZE);

Размер строки буфера равен MAX_BUFFER_SIZE со всеми символами, установленными на ноль.

После этой строки:

buffer.append(cipher_buf.data(), out_len);

Строка вашего буфера равна MAX_BUFFER_SIZE + out_len. Данные из cipher_buf находятся в буфере, но начинаются со смещения MAX_BUFFER_SIZE.

Итак, чтобы быстро решить вашу проблему, вам нужно всего лишь удалить строку:

   buffer.resize(MAX_BUFFER_SIZE);

Остальную часть вашего вопроса о преобразовании в base64 можно решить разными способами. Библиотека OpenSll поддерживает как минимум два API для преобразования в/из base64. Слой BIO API или EVP API.

Вот пример вашего кода с полным шифрованием в строку base64/дешифрование из строки base64. Мне не нравится использовать std::string в качестве буфера, поэтому вместо этого я использовал std::vector. Также добавлена ​​базовая проверка ошибок.

#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/err.h>

#include <iostream>
#include <format>
#include <string>
#include <vector>
#include <memory>

//#define USE_BIO

using namespace std::string_literals;

template<typename T, typename D>
std::unique_ptr<T, D> make_handle(T* handle, D deleter)
{
    return std::unique_ptr<T, D>{handle, deleter};
}

void CheckResult(int const result)
{
    if (!result)
    {
        auto const errorText = std::format("{}", ERR_error_string(ERR_get_error(), nullptr));
        throw std::exception(errorText.c_str());
    }
}

std::string GetBase64String(std::vector<unsigned char> const& data)
{
#ifdef USE_BIO
    auto const out = make_handle(BIO_push(BIO_new(BIO_f_base64()), BIO_new(BIO_s_mem())), BIO_free_all);
    BIO_set_flags(out.get(), BIO_FLAGS_BASE64_NO_NL);
    BIO_set_close(out.get(), BIO_CLOSE);

    CheckResult(BIO_write(out.get(), data.data(), static_cast<int>(data.size())));
    CheckResult(BIO_flush(out.get()));

    // ensure null terminated
    char* p;
    auto const length = BIO_get_mem_data(out.get(), &p);
    CheckResult(length != -1);
    return std::string(p, length);
#else
    const auto expected_length = 4 * ((data.size() + 2) / 3);
    std::string rv;
    rv.resize(expected_length + 1); // account for the null character
    auto const out_length = EVP_EncodeBlock(reinterpret_cast<unsigned char*>(rv.data()), data.data(), static_cast<int>(data.size()));
    CheckResult(out_length != -1);
    rv.resize(out_length);
    return rv;
#endif
}

#ifndef USE_BIO
size_t GetBase64StringPassingSize(std::string const& base64Data)
{
    // returns the number of '=' at the end of the base64 string as this is the
    // padding size to the base64 block size
    auto const pos = base64Data.find_last_not_of('=');
    if (pos == std::string::npos)
    {
        return 0;
    }
    return base64Data.size() - pos - 1;
}
#endif

std::vector<unsigned char> GetDataFromBase64String(std::string const& base64Data)
{
#ifdef USE_BIO
    auto const out = make_handle(BIO_push(BIO_new(BIO_f_base64()), BIO_new_mem_buf(base64Data.data(), static_cast<int>(base64Data.size()))), BIO_free_all);
    BIO_set_flags(out.get(), BIO_FLAGS_BASE64_NO_NL);
    BIO_set_close(out.get(), BIO_CLOSE);

    std::vector<unsigned char> rv;
    rv.resize(base64Data.size());

    auto const out_length = BIO_read(out.get(), rv.data(), static_cast<int>(rv.size()));
    CheckResult(out_length != -1);
    rv.resize(out_length);

    return rv;
#else
    const auto excepted_length = 3 * base64Data.size() / 4;
    const auto padding_length = GetBase64StringPassingSize(base64Data);
    std::vector<unsigned char> rv;
    rv.resize(excepted_length);
    auto const out_length = EVP_DecodeBlock(rv.data(), reinterpret_cast<unsigned char const*>(base64Data.data()), static_cast<int>(base64Data.size()));
    CheckResult(out_length != -1);
    rv.resize(out_length - padding_length);
    return rv;
#endif
}

std::string Encrypt(std::string const& str, std::string const& key, std::string const& iv)
{
    auto const MAX_BUFFER_SIZE = str.length();

    auto const ctx = make_handle(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
    CheckResult(static_cast<bool>(ctx));

    CheckResult(EVP_CipherInit(ctx.get(), EVP_aes_128_cbc(), reinterpret_cast<unsigned char const*>(key.data()), reinterpret_cast<unsigned char const*>(iv.data()), true));
    const int block_size = EVP_CIPHER_CTX_block_size(ctx.get());

    std::vector<unsigned char> cipherBuffer;
    cipherBuffer.resize(MAX_BUFFER_SIZE + block_size);

    int out_length;
    CheckResult(EVP_CipherUpdate(ctx.get(), cipherBuffer.data(), &out_length, reinterpret_cast<unsigned char const*>(str.data()), static_cast<int>(str.length())));
    cipherBuffer.resize(out_length + MAX_BUFFER_SIZE + block_size);
    auto totalLength = out_length;

    CheckResult(EVP_CipherFinal(ctx.get(), cipherBuffer.data() + totalLength, &out_length));
    totalLength += out_length;
    cipherBuffer.resize(totalLength);

    auto encodedBase64String = GetBase64String(cipherBuffer);
    std::cout << "Encrypted base64 string: " << encodedBase64String << "\n";
    return encodedBase64String;
}

std::string Decrypt(std::string const& base64Encrypted, std::string const& key, std::string const& iv)
{

    auto const cipherBuffer = GetDataFromBase64String(base64Encrypted);
    auto const MAX_BUFFER_SIZE = cipherBuffer.size();

    auto const ctx = make_handle(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
    CheckResult(static_cast<bool>(ctx));

    CheckResult(EVP_CipherInit(ctx.get(), EVP_aes_128_cbc(), reinterpret_cast<unsigned char const*>(key.data()), reinterpret_cast<unsigned char const*>(iv.data()), false));
    const int block_size = EVP_CIPHER_CTX_block_size(ctx.get());

    std::string rv;
    rv.resize(MAX_BUFFER_SIZE + block_size);

    int out_length;
    CheckResult(EVP_CipherUpdate(ctx.get(), reinterpret_cast<unsigned char*>(rv.data()), &out_length, cipherBuffer.data(), static_cast<int>(cipherBuffer.size())));
    rv.resize(out_length + MAX_BUFFER_SIZE + block_size);
    auto totalLength = out_length;

    CheckResult(EVP_CipherFinal(ctx.get(), reinterpret_cast<unsigned char*>(rv.data()) + totalLength, &out_length));
    totalLength += out_length;
    rv.resize(totalLength);

    std::cout << "Decrypted string: " << rv << "\n";
    return rv;
}

bool RunEncryptDecryptTest()
{
    try
    {
        auto const key = "abcdabcdabcdabcd"s;
        auto const iv = "abcdabcdabcdabcd"s;
        auto const data = "testing testing"s;
        auto const encryptedData = Encrypt(data, key, iv);
        // encryptedData == C3Lx6DlB5m7bGn3ajCeo7g==
        auto const rv = Decrypt(encryptedData, key, iv);
        return data == rv;
    }
    catch(std::exception const& e)
    {
        std::cout << "Failed with: " << e.what() << "\n";
    }

    return false;
}

Что делает #define USE_BIO и когда я должен его использовать?

Natalia 01.12.2022 05:40

Он просто показывает два способа использования преобразования Base64 с использованием openssl. Вам решать, какой из них вам больше нравится. Есть два способа: использовать API BIO (когда определено USE_BIO) или использовать API EVP (когда USE_BIO не определено).

Shane Powell 01.12.2022 06:15

Я получаю сообщение об ошибке неверная расшифровка, шифрование с помощью JavaScript: pastebin.com/iSRfNH2Q и расшифровка с помощью вашей функции

Natalia 01.12.2022 06:18

Проблема была в ключе!

Natalia 01.12.2022 06:21

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