尝试在OpenSSL中使用EVP函数时出现分段错误

时间:2015-02-28 22:44:20

标签: c++ encryption openssl rsa envelope

我正在尝试使用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);

处遇到了分段错误

我做错了什么?

2 个答案:

答案 0 :(得分:2)

eklsize_t,您将其投放到(int*)

docs for EVP_SealInit说:

  

每个加密密钥的实际大小都写入数组   EKL。

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

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

或者,首先将ekl声明为int,您可以避免演员:

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

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

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

您将缓冲区从空std::stringcipherText)传递到EVP_SealUpdateEVP_SealFinal。这一般不会起作用,如果缓冲区中没有足够的空间,可能会导致内存崩溃或损坏。

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

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

更一般的说明:

  • 在C ++中避免使用C风格的强制转换,并在可能的情况下编写代码,这样您根本不需要进行投射(例如,将整数声明为int而不是size_t,如果这样做&# 39; API期待的内容。)
  • 尽可能使用newdelete进行明确的内存管理,例如使用std::vector<unsigned char>作为缓冲区。

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

答案 1 :(得分:0)

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

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