2

我正在尝试使用 RSA 及其高级信封函数对 OpenSSL 进行公共加密。但是,我似乎无法理解它们,并且遇到了分段错误。我的项目中的这个精简代码重现了这个问题:

#include <iostream>
#include <string>

#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/rand.h>

int main()
{
    EVP_CIPHER_CTX *rsaCtx;
    rsaCtx = new EVP_CIPHER_CTX;

    unsigned char *ek;
    size_t ekl;
    unsigned char *iv;
    size_t ivl;

    EVP_PKEY *keypair;
    keypair = NULL;

    EVP_CIPHER_CTX_init(rsaCtx);

    EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
    EVP_PKEY_keygen_init(ctx);
    EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048);
    EVP_PKEY_keygen(ctx, &keypair);
    EVP_PKEY_CTX_free(ctx);

    ek = new unsigned char[EVP_PKEY_size(keypair)];
    iv = new unsigned char[EVP_MAX_IV_LENGTH];
    ivl = EVP_MAX_IV_LENGTH;

    std::string cipherText;
    std::string plainText = "A STRING";
    size_t encMsgLen = 0;
    size_t blockLen  = 0;

    EVP_SealInit(rsaCtx, EVP_aes_256_cbc(), &ek, (int*)ekl, iv, &keypair, 1);
    EVP_SealUpdate(rsaCtx, (unsigned char*)cipherText.c_str() + encMsgLen, (int*)&blockLen, (const unsigned char*)plainText.c_str(), (int)plainText.size() + 1);
    encMsgLen += blockLen;
    EVP_SealFinal(rsaCtx, (unsigned char*)cipherText.c_str() + encMsgLen, (int*)&blockLen);
    encMsgLen += blockLen;
    EVP_CIPHER_CTX_cleanup(rsaCtx);

    EVP_PKEY_free(keypair);
    delete[] ek;
    delete[] iv;
    delete rsaCtx;

    std::cout << cipherText;

    return 0;
}

我在线路上遇到分段错误EVP_SealInit(rsaCtx, EVP_aes_256_cbc(), &ek, (int*)ekl, iv, &keypair, 1);

我究竟做错了什么?

4

2 回答 2

3

ekl是 a size_t,而您将其转换为(int*).

文档EVP_SealInit说:

每个加密密钥的实际大小被写入数组 ekl。

您只是传递一个键,因此传递单个整数的地址就足够了,但您应该传递该整数的地址,例如:

EVP_SealInit(rsaCtx, EVP_aes_256_cbc(), &ek, reinterpret_cast<int*>(&ekl), iv, &keypair, 1);

或者,只需首先声明ekl为 an int,您就可以避免强制转换:

int ekl;
//...
EVP_SealInit(rsaCtx, EVP_aes_256_cbc(), &ek, &ekl, iv, &keypair, 1);

我很惊讶您的编译器没有警告您使用未初始化的局部变量。

更新:除了分段错误之外,此代码还有一些问题。

您将缓冲区从空std::string( cipherText) 传递到EVP_SealUpdateand EVP_SealFinal。这通常不起作用,如果缓冲区中没有足够的空间,可能会崩溃或损坏内存。

您应该为输出声明一个大小合适的缓冲区,可能为std::vector<unsigned char> cipherText(bufferSize);,并传递&cipherText[0]以获取指向第一个元素的指针。

中的数据cipherText不是人类可读的字符串,而是二进制数据,std::cout不适合显示它。

一些更一般的注释:

  • 避免在 C++ 中进行 C 风格的强制转换,并尽可能编写代码以便您根本不需要强制转换(例如,声明整数int而不是size_tAPI 所期望的)。
  • 尽可能避免显式的内存管理newdelete例如使用std::vector<unsigned char>缓冲区。

我建议再次查看这些函数的文档,或者网络上的其他一些使用它们的示例。另外,编写一些执行解密步骤的代码,以便您可以测试纯文本是否正确往返。

于 2015-03-01T00:07:05.907 回答
0

对 EVP_SealX 函数使用太小的缓冲区可能会在代码中看似不相关的部分产生令人抓狂的后果。这是我的经验。

加入保护措施以确保密码缓冲区与总明文输入一样大,加上可能的填充开销将降低风险。

于 2018-07-03T23:20:52.030 回答