Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reading Public/Private Key from Memory with OpenSSL

Tags:

c

ssl

openssl

I am using Public/Private Keys in my project to encrypt/decrypt some data.

I am hosting a public key ("public.pem") on a server.

"public.pem" looks like this:

-----BEGIN PUBLIC KEY-----
.....
.....
-----END PUBLIC KEY-----

I wrote a client side that downloads this public key and save it to disk and then calls OpenSSL's PEM_read_RSA_PUBKEY() with a File descriptor to that file. This operation works great and the result is an RSA object that is ready for encryption.

I would like to avoid writing the public key to disk each time (since i have the buffer in memory already).

How can i do the same operation without saving the buffer to disk? I noticed a function called: PEM_read_bio_RSAPublicKey() but i am not sure of it's usage of BIO structure. Am I on the right path?

So the real question would be: How do I read a public/private key to an RSA object straight from memory and not from a file descriptor.

like image 984
user1144031 Avatar asked Aug 09 '12 14:08

user1144031


3 Answers

You are on the right track. You must wrap the PEM key already in memory by means of a BIO buffer via BIO_new_mem_buf(). In other words, something like:

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);

The same approach is valid for an RSA private key (via PEM_read_bio_RSAPrivateKey), but in that case you most certainly need to cater for the pass phrase. Check the man page for details.

like image 191
SquareRootOfTwentyThree Avatar answered Nov 15 '22 05:11

SquareRootOfTwentyThree


Here's complete example, showing embedded key, and how to use C++11 unique pointers to manage OpenSSL resources.

Updated: following on from comments by spectras. No longer using specialisation of 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;
}
like image 39
Darren Smith Avatar answered Nov 15 '22 07:11

Darren Smith


SquareRootOfTwentyThree's method not work for me. Here is my solution.

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);
like image 4
ekoj Avatar answered Nov 15 '22 06:11

ekoj