Martin Drozdik Martin Drozdik -4 years ago 220
C Question

How to read an RSA public key from a its PEM format string using the OpenSSL API?

I could use the

PEM_read_RSA_PUBKEY
function to easily read a PEM file. However, I have a public key that I have built into the executable and I would prefer not to make a temporary file. Reading on this example/tutorial: http://hayageek.com/rsa-encryption-decryption-openssl-c/ I came up with the following solution:

#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/bio.h>

#include <QFile>
#include <QByteArray>

#include <stdexcept>
#include <cassert>
#include <cstring>

RSA* createRSA(const char* key)
{
RSA *rsa = nullptr;
BIO *keybio ;
keybio = BIO_new_mem_buf(key, -1); // !!!
if (!keybio)
{
throw std::runtime_error("Failed to create key BIO");
}
rsa = PEM_read_bio_RSA_PUBKEY(keybio, nullptr, nullptr, nullptr); // !!!
if(!rsa )
{
throw std::runtime_error("Failed to create RSA");
}
BIO_free(keybio); // !!!
return rsa;
}

int main()
{
QFile publicKeyFile(":/public.pem");
publicKeyFile.open(QIODevice::ReadOnly);
auto data = publicKeyFile.readAll();

RSA* rsa = createRSA(data.data());

EVP_PKEY* verificationKey = EVP_PKEY_new();
auto rc = EVP_PKEY_assign_RSA(verificationKey, RSAPublicKey_dup(rsa));
assert(rc == 1);

if(verificationKey)
EVP_PKEY_free(verificationKey);

return 0;
}


However I have a lot of doubts:


  1. The
    BIO_new_mem_buf
    takes a
    const void*
    parameter, can I just pass a
    const char*
    ? I did not figure it out even from the docs.

  2. When calling the
    PEM_read_bio_RSA_PUBKEY
    function, the original example calls it like this:
    rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa,NULL, NULL);

    which I do not understand even after reading the docs. I believe that I should pass
    nullptr
    as the second argument.

  3. Should I call
    RSA_free
    on the returned
    RSA
    pointer?
    valgrind
    does not see a memory leak whether I do it or not.

  4. Should I call
    BIO_free(keybio);
    after I am done with the
    BIO
    ?
    valgrind
    sees a memory leak if I do not, and in the tutorial this call was missing. If I call
    BIO_free(keybio);
    it would imply that
    PEM_read_bio_RSA_PUBKEY
    copied the data from the
    BIO
    rather than just linking to it. But if that were the case, shouldn't I free the
    RSA
    ?



Any advice is warmly appreciated. I do not know what is real anymore.

Answer Source

Answers to each of your questions:

  1. Yes, you can pass a const char*, it is cast.
  2. PEM_read_bio_RSA_PUBKEY creates allocates the RSA structure for you. The argument (if not null) is used to store the pointer to it, which will be the same as the return value. It is used for simplified coding: if (!PEM_read_bio_RSA_PUBKEY(keybio, &rsa, nullptr, nullptr)) { /* error */ }

  3. Yes, you have to release it using RSA_free.

  4. Yes, you have to release it too.

P.S.: OpenSSL documentation is a bit tricky because there are many similar functions that only vary in algorithm, structures or data format. At the end of the man page there is a Description section where they are explained, removing the specifics of each variation. But yes, it is quite difficult to find it out with out a good tutorial or examples.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download