Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

c

openssl

pem

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.

like image 782
Martin Drozdik Avatar asked Jan 07 '17 22:01

Martin Drozdik


1 Answers

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.

like image 79
cbuchart Avatar answered Oct 13 '22 06:10

cbuchart