繁体   English   中英

使用 OpenSSL 加密和解密字符串

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM