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:
- The
BIO_new_mem_buf
takes aconst void*
parameter, can I just pass aconst char*
? I did not figure it out even from the docs. - 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 passnullptr
as the second argument. - Should I call
RSA_free
on the returnedRSA
pointer?valgrind
does not see a memory leak whether I do it or not. - Should I call
BIO_free(keybio);
after I am done with theBIO
?valgrind
sees a memory leak if I do not, and in the tutorial this call was missing. If I callBIO_free(keybio);
it would imply thatPEM_read_bio_RSA_PUBKEY
copied the data from theBIO
rather than just linking to it. But if that were the case, shouldn't I free theRSA
?
Any advice is warmly appreciated. I do not know what is real anymore.
Answers to each of your questions:
const char*
, it is cast.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 */ }
Yes, you have to release it using
RSA_free
.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.