【问题标题】:How to avoid SIGABRT when generating RSA Signature at EVP_SignFinal在 EVP_SignFinal 生成 RSA 签名时如何避免 SIGABRT
【发布时间】:2016-12-08 11:00:00
【问题描述】:

我正在尝试使用 libopenssl for c++ 生成 RSA 签名: 但是当我运行我的代码时,我得到了一个 SIGABRT。我对 libopenssl 内部的东西进行了一些深入的调试,以查看 Segfault 的来源。稍后我会谈到这一点。

首先我想明确一点,RSA PrivateKey 是从 .pem 文件成功加载的。所以我很确定这不是问题的根源。

所以我的问题是:如何避免 SIGABRT 以及它的原因是什么?

我这样做是为了我的理学学士学位。论文所以我真的很感谢你的帮助:)


签名生成函数:

DocumentSignature* RSASignatureGenerator::generateSignature(ContentHash* ch, CryptographicKey* pK) throw(PDVSException) {
    OpenSSL_add_all_algorithms();
    OpenSSL_add_all_ciphers();
    OpenSSL_add_all_digests();

    if(pK == nullptr)
        throw MissingPrivateKeyException();

    if(pK->getKeyType() != CryptographicKey::KeyType::RSA_PRIVATE || !dynamic_cast<RSAPrivateKey*>(pK))
        throw KeyTypeMissmatchException(pK->getPem()->getPath().string(), "Generate RSA Signature");

    //get msg to encrypt
    const char* msg = ch->getStringHash().c_str();

    //get openssl rsa key
    RSA* rsaPK = dynamic_cast<RSAPrivateKey*>(pK)->createOpenSSLRSAKeyObject();

    //create openssl signing context
    EVP_MD_CTX* rsaSignCtx = EVP_MD_CTX_create();
    EVP_PKEY* priKey  = EVP_PKEY_new();
    EVP_PKEY_assign_RSA(priKey, rsaPK);

    //init ctxt
    if (EVP_SignInit(rsaSignCtx, EVP_sha256()) <=0)
        throw RSASignatureGenerationException();

    //add data to sign
    if (EVP_SignUpdate(rsaSignCtx, msg, std::strlen(msg)) <= 0) {
        throw RSASignatureGenerationException();
    }

    //create result byte signature struct
    DocumentSignature::ByteSignature* byteSig = new DocumentSignature::ByteSignature();
    //set size to max possible
    byteSig->size = EVP_MAX_MD_SIZE;
    //alloc buffer memory
    byteSig->data = (unsigned char*)malloc(byteSig->size);

    //do signing
    if (EVP_SignFinal(rsaSignCtx, byteSig->data, (unsigned int*) &byteSig->size, priKey) <= 0)
        throw RSASignatureGenerationException();


    DocumentSignature* res = new DocumentSignature(ch);
    res->setByteSignature(byteSig);

    EVP_MD_CTX_destroy(rsaSignCtx);
    //TODO open SSL Memory leaks -> where to free open ssl stuff?!

    return res;
}

RSA* rsaPK = dynamic_cast(pK)->createOpenSSLRSAKeyObject();

virtual RSA* createOpenSSLRSAKeyObject() throw (PDVSException) override {
        RSA* rsa = NULL;
        const char* c_string = _pem->getContent().c_str();
        BIO * keybio = BIO_new_mem_buf((void*)c_string, -1);

        if (keybio==NULL)
            throw OpenSSLRSAPrivateKeyObjectCreationException(_pem->getPath());

        rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);

        if(rsa == nullptr)
            throw OpenSSLRSAPrivateKeyObjectCreationException(_pem->getPath());

        //BIO_free(keybio);

        return rsa;
    }

文件 openssl/crypto/mem.c 中的 SigAbrt 来源

void CRYPTO_free(void *str, const char *file, int line)
{
    if (free_impl != NULL && free_impl != &CRYPTO_free) {
        free_impl(str, file, line);
        return;
    }

#ifndef OPENSSL_NO_CRYPTO_MDEBUG
    if (call_malloc_debug) {
        CRYPTO_mem_debug_free(str, 0, file, line);
        free(str);
        CRYPTO_mem_debug_free(str, 1, file, line);
    } else {
        free(str);
    }
#else
    free(str); // <<<<<<< HERE
#endif
}

堆栈跟踪

stacktrace screenshot from debugger (clion - gdb based)

【问题讨论】:

  • SIGABRT 的原因是代码中的某个错误。避免它的唯一方法是找到并修复错误。如果没有minimal reproducible example(显示的代码在最小部分和完整部分都失败了),就不可能有进一步的答案。由于 C++ 的工作方式,错误可能出现在任何地方。仅仅因为程序在一个特定函数内崩溃并不意味着这就是错误所在。
  • 嗯,好的,谢谢..我想我必须找到这个错误..因为当我做一个简约的例子时它可以工作..:(
  • 当然可以。这证明你在某个地方有一个错误。当然,库中的错误总是有可能的。但一个非常不可能的。欢迎使用 C++。
  • 另请参阅 OpenSSL wiki 上的 EVP Signing and Verifying。您应该能够复制/粘贴它,并且事情应该为您“正常工作”。此外,有关 C++ 中的 C 对象管理示例,请参阅 OpenSSL wiki 上的 EVP Symmetric Encryption and Decryption | C++ Programs
  • " 我想我将不得不寻找这个错误......因为当我做一个简约的例子时它可以工作......" - Valgrind 通常可以提供帮助,但是 OpenSSL有如此多的内存泄漏,通常很难在 OpenSSL 上使用该工具。也许您可以使用 Address Sanitizer 构建并等待错误的读/写。

标签: c++ openssl rsa digital-signature


【解决方案1】:

我刚刚发现了这个错误(我真的不确定这是否是一个 libopenssl 错误..)

//set size to max possible
byteSig->size = EVP_MAX_MD_SIZE;
//alloc buffer memory
byteSig->data = (unsigned char*)malloc(byteSig->size);

问题是当我将缓冲区大小设置为 EVP_MAX_MD_SIZE 时!

(在我看来)非常奇怪的事情是,您必须保持未初始化的大小! (甚至没有设置为 0 - 只是 "size_t size;" )。

奇怪的是,你还必须像我一样分配内存。我不明白这一点,因为然后分配了未定义大小的内存..

真正奇怪的是 libopenssl 在内部将大小设置回 0 并自行分配内存..(我通过浏览 libopenssl 源代码检测到这一点)

【讨论】:

  • 会不会是 byteSig-&gt;size 在未初始化时恰好为零,而 malloc(0) 返回 NULL
  • 奇怪的是,没有。当我使用 gdb 调试时,我的机器上未初始化的 size_t 始终为 1713(Ubuntu 64 位 16.04 LTS)。当我将大小初始化为 0 时,同样的错误(如在主要问题中发布的那样)
  • 您应该知道您“必须保持未初始化的大小”。在我看来,这听起来像是某种堆损坏,但不可能有任何代码依赖于“未初始化”的值,因为这不是一个已定义的状态。
  • 我怀疑是这种情况,但通常在 Windows 中,物理内存是由虚拟内存地址指向的,因此它不是连续的。我相信很多 Linux 服务器都使用 oppenssl,并且我听说过攻击者能够在 oppenssl 的生产版本中导致未处理的异常的附加方法,从而导致服务器崩溃或更糟的是允许攻击者利用连续内存来恢复ssl 中经常使用的隐藏密钥,用于加密通过 http 的传输。也许这是某种修复,因为它是操作系统中的一种漏洞。由应用程序修复。
  • 基本上你不应该理解例如数组溢出,但在某些情况下人们可以。我可以看到 openssl 可能希望对分配的位置很挑剔。
猜你喜欢
  • 2023-02-16
  • 2014-09-17
  • 1970-01-01
  • 2014-07-26
  • 2016-02-09
  • 2017-06-22
  • 2012-02-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多