I'm confused about how to resize the cipher_buf
and the buffer
which will contain the encrypted/decrypted string.
And how do I convert the encrypted buffer
to base64
? I'll need a base64 encoder lib or there's an API for this on 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();
}
The problem with the code above is that after the latest buffer.append
, buffer
is empty (Checking the data on Visual Studio debugger), but its size auto s = buffer.size()
is 31
.
Your main problem with your example is you misunderstanding of the string size of buffer.
After with this line:
buffer.resize(MAX_BUFFER_SIZE);
buffer string size is MAX_BUFFER_SIZE with all the characters set to zero.
After this line:
buffer.append(cipher_buf.data(), out_len);
Your buffer string is MAX_BUFFER_SIZE + out_len. The data from cipher_buf is in buffer but start at the offset of MAX_BUFFER_SIZE.
So to quickly fix your problem you only need to remove the line:
buffer.resize(MAX_BUFFER_SIZE);
The rest of your question about converting to base64 can be done in many differnt ways. OpenSll library supports at least two api's to convert to/from base64. The BIO layer api or the EVP api.
Here is a example of your code with full encrypt to base64 string / decrypt from base64 string. I don't like using std::string as a buffer so I used a std::vector instead. There is also basic error checking added as well.
#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;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.