使用OpenSSL从内存中读取公钥/私钥

10

我在项目中使用公钥/私钥来加密/解密一些数据。

我在服务器上托管了一个公钥("public.pem")。

"public.pem"看起来像这样:

-----BEGIN PUBLIC KEY-----
.....
.....
-----END PUBLIC KEY-----
我编写了一个客户端,它下载了这个公钥并将其保存到磁盘上,然后使用指向该文件的文件描述符调用 OpenSSL 的 PEM_read_RSA_PUBKEY() 函数。这个操作很好用,结果是一个已准备好加密的 RSA 对象。
我想避免每次都将公钥写入磁盘(因为我已经有内存中的缓冲区了)。
如何在不将缓冲区保存到磁盘的情况下执行相同的操作呢?我注意到有一个名为 PEM_read_bio_RSAPublicKey() 的函数,但我不确定它对 BIO 结构的使用方法。我是否走在正确的道路上?
所以真正的问题是:如何直接从内存中读取公/私钥并将其转换为 RSA 对象,而不是从文件描述符中读取?
3个回答

25

你已经朝着正确的方向前进。你需要通过BIO_new_mem_buf()函数将内存中的PEM密钥包装在BIO缓冲区中。换句话说,像这样:

BIO *bufio;
RSA *rsa

bufio = BIO_new_mem_buf((void*)pem_key_buffer, pem_key_buffer_len);
PEM_read_bio_RSAPublicKey(bufio, &rsa, 0, NULL);

对于RSA私钥(通过PEM_read_bio_RSAPrivateKey),相同的方法是有效的,但在这种情况下,您肯定需要考虑密码短语。请查看手册了解详情。


1
我现在尝试了那个选项。加密没问题,但解密不起作用。公钥需要像以下这样吗?还是当我发送给客户端时需要去掉“BEGIN PUBLIC KEY”? -----BEGIN PUBLIC KEY----- ..... ..... -----END PUBLIC KEY----- - user1144031
1
文档现在位于(OpenSSL 1.1):https://www.openssl.org/docs/man1.1.0/crypto/BIO_s_mem.html https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_RSAPublicKey.html - user2859193

5
这是一个完整的示例,展示了嵌入式密钥和如何使用C++11唯一指针来管理OpenSSL资源。
更新:根据spectras的评论,不再使用default_delete<T>的特化。
/* compile with:

    c++ -Wall -pedantic  -std=c++17 main.cc  -lssl -lcrypto -o main
*/

#include <memory>
#include <iostream>

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

#include <assert.h>
#include <string.h>

/* Custom deletors for use with unique_ptr */

struct EVP_PKEY_deleter {
  void operator()(EVP_PKEY* p) const {
      if (p)
        EVP_PKEY_free(p);
  }
};

struct BIO_deleter {
  void operator()(BIO* p) const {
      if (p)
        BIO_free(p);
  }
};

/* Smart pointers wrapping OpenSSL resources */

using evp_key_ptr = std::unique_ptr<EVP_PKEY, EVP_PKEY_deleter>;
using bio_ptr = std::unique_ptr<BIO, BIO_deleter>;

/* Create key based on memory contents */

evp_key_ptr load_public_key(const char* buf, size_t len)
{
  bio_ptr bp (BIO_new_mem_buf((void*) buf, len));
  if (!bp)
    throw std::runtime_error("BIO_new_mem_buf failed");

  EVP_PKEY * kp = nullptr;

  kp = PEM_read_bio_PUBKEY(bp.get(), &kp, nullptr, nullptr);
  ERR_print_errors_fp(stderr);
  return evp_key_ptr{kp};
}


int main()
{
  const char * RSA_PUBLIC_KEY=R"(
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA80ZqDPPW5eOH6TWdLsEJ
8qf6hoMJfFZ3BL9Fz+YNGeBpF3zxKmm8UuRrBHHVZZB2Gs1MTo06IU3fqDfFsOyh
J6pHeJF3wyUlYZuYbGAyMlZZ/+M5TOvo92f7lt/A40QThCVf1vS5o+V8sFkgnz3N
C7+VvC4dYrv+fwnmnWGxPy1qfp3orB+81S4OPRiaoy+cQBZs10KCQaNBI/Upzl2R
3dMkWKM+6yQViKTHavT4DRRZ1MKp9995qOR3XfhhJdWuDl4moXcU3RcX4kluvS5q
b8oTnVyd2QB1GkUw6OKLWB/5jN1V1WzeYK447x2h4aPmJfsn5gCFJs6deq2RFQBR
SQIDAQAB
-----END PUBLIC KEY-----
)";
  ERR_load_crypto_strings();
  ERR_free_strings();

  auto pubkey = load_public_key(RSA_PUBLIC_KEY, strlen(RSA_PUBLIC_KEY));
  if (pubkey)
    std::cout << "load_public_key success" << std::endl;
}

1
注意:这是非法的。虽然您可以专门化default_delete,但专门化除调用operator delete之外不能产生任何效果。是的,这使得专门化变得有点无意义。它旨在 - default_delete不是一个自定义点。 - spectras
有趣的是...这个规则在最近的C++11标准中是否明确规定了呢?例如:(1) 是否可能存在副作用,以及(2) 是否必须调用operator delete?我可以看到23.11.1.1.3节涉及到default_delete,但没有具体规定。 - Darren Smith
在第20.7节中,它说明:*void operator()(T* ptr) const; Effects: calls delete on ptr*。嗯,它在标准之间移动,这里是最新版本的直接链接:http://eel.is/c++draft/unique.ptr.dltr.dflt#3 - spectras
1
顺便提一下,我在写关于这个主题的博客文章时偶然发现了这个。我建议使用一个没有数据成员的函数对象作为替代方案。 - spectras
谢谢,根据您的建议已更新代码示例。 - Darren Smith

4

SquareRootOfTwentyThree的方法对我没有用。这是我的解决方案。

BIO* bio = BIO_new(BIO_s_mem());
int len = BIO_write(bio, pem_key_buffer, pem_key_buffer_len);
EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
RSA* rsa = EVP_PKEY_get1_RSA(evp_key);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接