openssl create rsa key pair in C and export it - c

I want to create a RSA key pair in C/C++ and export it to a string to work with it.
I managed to create the key
rsa = RSA_generate_key(bits, exp, NULL, NULL);
if(RSA_check_key(rsa)!=1){
std::cout << "Error while checking key" << std::endl << std::flush;
}
pkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pkey, rsa);
From this point I can write the private and public key to a file with PEM_write_PUBKEY() and PEM_write_PrivateKey(). But what I want to do is to get the private and public key from pkey directly into a variable, preferable in the PEM format. I already looked around but couldn't find the right function. Any hints?

If you really need the PEM representation, then PEM_write_bio_RSA_PUBKEY() and PEM_write_bio_RSAPrivateKey() (along with their read counterparts) are the functions you want; give them memory BIOs to have them write into a memory buffer.
You can create a memory BIO by invoking e.g.
BIO * b = BIO_new(BIO_s_mem());
and get the memory buffer by invoking
void * buf;
BIO_get_mem_ptr(b, &buf);
You can also create a memory BIO around existing memory by invoking e.g.
BIO * b = BIO_new_mem_buf(buf, 9001)
but the resulting BIO will be read-only.

Related

how to store public key as c array

I've been looking for an answer for this for a while so i though i might aswell post what I've found.
I'm using openssl to sign and verify license using private key to sign and public key to verify.
since I'm sending the public key to the client for verification, I can't give it to him and ask for path to key, because he can change the public key and encrypt the message using his own private/public keys.
the solution: store the public key inside the C program as an array.
now i need to change my code of reading the public key from:
EVP_PKEY *public_key = NULL;
public_key_fd = fopen( public_key_path, "r" );
if ( !public_key_fd )
// something went wrong
PEM_read_PUBKEY( public_key_fd, &public_key, NULL, NULL );
fclose( public_key_path );
to something that uses a string instead of a file.
first we need to understand what does the look like, using cat:
cat public_key.pem
we get
---- BEGIN PUBLIC KEY ----
AAAAB3NzaC1yc2EAAAABJQAAAQB/nAmOjTmezNUDKYvEeIRf2YnwM9/uUG1d0BYs
c8/tRtx+RGi7N2lUbp728MXGwdnL9od4cItzky/zVdLZE2cycOa18xBK9cOWmcKS
0A8FYBxEQWJ/q9YVUgZbFKfYGaGQxsER+A0w/fX8ALuk78ktP31K69LcQgxIsl7r
NzxsoOQKJ/CIxOGMMxczYTiEoLvQhapFQMs3FL96didKr/QbrfB1WT6s3838SEaX
fgZvLef1YB2xmfhbT9OXFE3FXvh2UPBfN+ffE7iiayQf/2XR+8j4N4bW30DiPtOQ
LGUrH1y5X/rpNZNlWW2+jGIxqZtgWg7lTy3mXy5x836Sj/6L
---- END PUBLIC KEY ----
I've read this char by char to understand where are the '\n' and other invisible characters and what I've found is that each line ends with '\n'
so what we get as an array for C will be:
char *key_string = "---- BEGIN PUBLIC KEY ----\nAAAAB3NzaC1yc2EAAAABJQAAAQB/nAmOjTmezNUDKYvEeIRf2YnwM9/uUG1d0BYs\nc8/tRtx+RGi7N2lUbp728MXGwdnL9od4cItzky/zVdLZE2cycOa18xBK9cOWmcKS\n0A8FYBxEQWJ/q9YVUgZbFKfYGaGQxsER+A0w/fX8ALuk78ktP31K69LcQgxIsl7r\nNzxsoOQKJ/CIxOGMMxczYTiEoLvQhapFQMs3FL96didKr/QbrfB1WT6s3838SEaX\nfgZvLef1YB2xmfhbT9OXFE3FXvh2UPBfN+ffE7iiayQf/2XR+8j4N4bW30DiPtOQ\nLGUrH1y5X/rpNZNlWW2+jGIxqZtgWg7lTy3mXy5x836Sj/6L\n---- END PUBLIC KEY ----\n";
and The code needed to read a key from a char array is:
EVP_PKEY *public_key = NULL;
BIO *bio;
bio = BIO_num_mem_buf( key_string, strlen( key_string ) );
PEM_read_bio_PUBKEY( bio, &public_key, NULL, NULL );
explaination: PEM_read_PrivateKey() is a wrapper around PEM_ASN1_read() (which reads an arbitrary ASN.1 object from a
PEM-encoded blob) and d2i_PrivateKey() (which knows how to read a private key blob specifically).
PEM_ASN1_read() simply creates a BIO from the FILE* that you give it, and calls PEM_ASN1_read_bio(). If
you want, you can instead create a BIO from your string using something like BIO_new_mem_buf() and call
PEM_ASN1_read_bio() yourself. (A BIO is an openssl object that's like a more general-purpose FILE*.)
BTW, if your keys are stored in a database, there's probably no need for them to be PEM-encoded; you can save a
bit of space and time by storing them in DER format and calling d2i_PrivateKey() directly. (PEM format is
more or less just base64-encoded DER.) There's a FAQ entry on this:
http://www.openssl.org/support/faq.html#PROG3

Openssl how to find out what the bit size of the public key in an X509 certificate is

If I have an X509* that openssl has provided me, what's the best way to figure out the bit-ness of the RSA public key in the certificate? I can't quite figure this out. I'm pretty sure that if I'm in the SSL certificate verification callback, I can get the X509 ptr with
X509 * cert = X509_STORE_CTX_get_current_cert(the_x509_store_ctx);
and I would surmise I get the public key like this
EVP_PKEY *public_key = X509_get_pubkey(cert);
and then I need to check whether it's RSA, presumably?
if (public_key && (EVP_PKEY_RSA == public_key->type))
and once I know that I got a public key back and that it's RSA, I'd like to do this:
int key_length = BN_num_bits(public_key->pkey.rsa->n);
but I've found that while this works quite nicely on openssl 0.9.8, on 1.0.1h it segfaults on Windows. The BIGNUM 'n' doesn't seem to be valid - the data ptr in it has a garbage pointer.
Any idea what's wrong?
As already suggested, to get the RSA modulus size in bytes (so not "bit size"...) use:
EVP_PKEY * public_key = X509_get_pubkey(cert);
RSA *rsa_key = EVP_PKEY_get1_RSA(public_key);
int key_length = RSA_size(rsa_key);
...
RSA_free(rsa_key);

How to read/write the public key in an RSA structure

Using C, I can generate and write an RSA public key to a PEM file using PEM_write_RSAPublicKey(),
But I want to write multiple public keys to a single file (like known_hosts or authorized_hosts), rather than have a separate PEM file for each.
I know how to encode/decode base64,
But how do I get the public key from the RSA structure (which I can base64 encode) and later read the same key from file and put it back into the RSA structure for encrypting/decrypting?
PEM_write_RSAPublicKey() will append to the file if opened with "a+" but there is no indication of which keys are for which client. I don't want PEM formatting, only to write each public key to a single line in the file.
I found that the public key is in DER format, which I can get from the RSA struct using:
len = i2d_RSAPublicKey(rsap, 0);
buf1 = buf2 = (unsigned char *)malloc(len + 1);
rc = i2d_RSAPublicKey(rsap, (unsigned char **)&buf2);
Then I base64-encode buf1 and write it to my file.
After reading it from file, putting it back into the RSA struct is just the reverse:
rsap = d2i_RSAPublicKey(NULL, (const unsigned char **)&buf1, (long)len);

Why would RSA_size crash?

I'm trying to use the OpenSSL crypto lib and it's crashing in a call that's a staple in every example I've seen. Here's how it's set up:
BIO* bp = BIO_new_mem_buf(_publicKey, -1); // Create a new memory buffer BIO.
RSA* pubKey = PEM_read_bio_RSA_PUBKEY(bp, 0, 0, 0); // And read the RSA key from it.
unsigned char encryptedRandKey[RSA_size(pubKey)];
BIO_free(bp);
_publicKey is a null-terminated character string containing a PEM-formatted RSA key (with the -----BEGIN XXX----- and so forth). It crashes with bad access in RSA_size.
It doesn't matter if I remove the BIO_free.
Any ideas? Thanks!
You need to check the return value of PEM_read_bio_RSA_PUBKEY() to make sure it is non-null. Most likely the contents of _publicKey are not quite a correctly formatted key and as a consequence pubKey is NULL.
Try PEM_read_bio_RSAPublicKey instead of PEM_read_bio_RSA_PUBKEY.

How do I decrypt a private key file and sign some text using openssl calls in C?

I have 2 separate programs (spliced together below). The first generates the key pair and saves to files (works fine). The second opens the private key, decrypting with a pass phrase and then I need it to sign a string of text. The code below fails on the PEM_read_PrivateKey() (last) call (can't see why). Can anyone point me at what I am doing wrong and then what openssl calls I should make to use the private key to sign some text?
int main (int argc, char *argv[])
{
char *priv_pem = "priv.pem";
char *pub_pem = "pub.pem";
char *pass = "Password";
FILE *fp;
int bits = 4096;
unsigned long exp = RSA_F4;
RSA *rsa;
EVP_PKEY *pkey;
// GENERATE KEY
rsa=RSA_generate_key(bits,exp,NULL,NULL);
if (RSA_check_key(rsa)!=1)
Exit(1,"Error whilst checking key","");
pkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pkey, rsa);
// WRITE ENCRYPTED PRIVATE KEY
if (!(fp = fopen(priv_pem, "w")))
Exit(2,"Error opening PEM file",priv_pem);
if (!PEM_write_PrivateKey(fp,pkey,EVP_aes_256_cbc(),NULL,0,NULL,pass))
Exit(3,"Error writing PEM file",priv_pem);
fclose(fp);
// WRITE PUBLIC KEY
if (!(fp = fopen(pub_pem, "w")))
Exit(4,"Error opening PEM file",pub_pem);
if (!PEM_write_PUBKEY(fp, pkey))
Exit(5,"Error writing PEM file",pub_pem);
fclose(fp);
// ------- End of key generation program -------
// ------- Start of text signing program -------
// READ IN ENCRYPTED PRIVATE KEY
if (!(fp = fopen(priv_pem, "r")))
Exit(6,"Error reading encrypted private key file",priv_pem);
if (!PEM_read_PrivateKey(fp,&pkey,NULL,pass))
Exit(7,"Error decrypting private key file",priv_pem);
fclose(fp);
// Sign some text using the private key....
// FREE
RSA_free(rsa);
return 0;
}
Have you initialised pkey to NULL before you pass &pkey to PEM_read_PrivateKey()? If not, it will attempt to re-use the EVP_PKEY structure that pkey points to - and if pkey is uninitialised, it will be looking at a random spot in memory.
You can use ERR_print_errors_fp(stderr); to dump the OpenSSL error stack to stderr when an error occurs - this is often helpful in finding the problem.
Thanks #caf for your help.
By trial and error I fixed PEM_read_PrivateKey() error by adding the following to the start:
if (EVP_get_cipherbyname("aes-256-cbc") == NULL)
OpenSSL_add_all_algorithms();
However, I'm still looking for the best (practice) way of generating the keys and then using the private key for signing. From my limited understanding, I am looking for openssl methods that adhere to RSA's "PKCS #1 v2.0: RSA Cryptography Standard"

Resources