Я не понимаю, как изменить размер 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.
@UlrichEckhardt, разве мой код не является воспроизводимым примером? «Это должно сделать вашу ошибку очевидной», какую ошибку вы имеете в виду? На моей стороне компилируется без ошибок.
«На самом деле я не уверен, что вы ожидаете от этого кода, он ничего не выводит». Сначала я пытаюсь заставить шифрование/дешифрование работать, чтобы завершить функцию.
Ваша основная проблема с вашим примером заключается в том, что вы неправильно понимаете размер строки буфера.
После этой строки:
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 и когда я должен его использовать?
Он просто показывает два способа использования преобразования Base64 с использованием openssl. Вам решать, какой из них вам больше нравится. Есть два способа: использовать API BIO (когда определено USE_BIO) или использовать API EVP (когда USE_BIO не определено).
Я получаю сообщение об ошибке неверная расшифровка, шифрование с помощью JavaScript: pastebin.com/iSRfNH2Q и расшифровка с помощью вашей функции
Проблема была в ключе!
На самом деле я не уверен, что вы ожидаете от этого кода, он ничего не выводит. Можете ли вы уточнить это, а также убедиться, что он соответствует минимально воспроизводимому примеру? Подсказка: это должно сделать вашу ошибку очевидной! Кстати: лучшим типом для буферов является std::vector, который вы создаете с правильным типом, чтобы избавиться от всех ненужных приведений типов в вашем коде.