[英]Encrypt and decrypt string using OpenSSL
我对如何调整cipher_buf
和包含加密/解密字符串的buffer
的大小感到困惑。
以及如何将加密buffer
转换为base64
? 我需要一个 base64 编码器库,或者 OpenSSL 上有一个 API?
#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
。
您的示例的主要问题是您误解了缓冲区的字符串大小。
在这一行之后:
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 的问题的 rest 可以通过多种不同的方式完成。 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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.