how to store public key as c array - c

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

Related

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

openssl create rsa key pair in C and export it

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.

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.

DSA Signing with OpenSSL

I'm tryng to sign using DSA from OpenSSL. I have the files containing public and private keys.
First of all I make an unicast connection and every thing is fine. After that I need a multicast UDP connection and I want to sign the packets. I'm trying to use function PEM_read_DSA_PUBKEY() in order to load my public key from my cert but it doesn't work. It returns always NULL instead of a DSA struct.
Here you have a simplistic version of the code. I compile like this:
gcc -Wall -g -lm prueba.c -o prueba -lcrypto
Any idea? Thank you!
#include <stdio.h>
#include <openssl/dsa.h>
#include <openssl/pem.h>
int main()
{
FILE *DSA_cert_file = fopen("./certs/cert.pem", "r");
if (DSA_cert_file == NULL)
return 1;
printf("Certificate read\n");
DSA *dsa = DSA_new();
if((dsa = PEM_read_DSA_PUBKEY(DSA_cert_file, 0, 0, 0)) == NULL)
return 1;
printf("DSA public key read\n");
return 0;
}
Are you using a password-protected public key?
If yes, you are required to pass a callback function as the third argument to PEM_read_DSA_PUBKEY, so if the provided password matches, it will be able to properly load your key.
Update:
Alternatively, as pointed by Hasturkun, you can pass a null-terminated string as the fourth argument. Quoting the official documentation:
If the cb parameters is set to NULL
and the u parameter is not NULL then
the u parameter is interpreted as a
null terminated string to use as the
passphrase. If both cb and u are NULL
then the default callback routine is
used which will typically prompt for
the passphrase on the current terminal
with echoing turned off.
Does your cert.pem contains a X.509 certificate ? It looks like PEM_read_DSA_PUBKEY expects a PEM-encoded DSA public key without the X.509 container.
Try something like that instead:
X509 *cert;
EVP_PKEY *pk;
DSA *dsa;
cert = PEM_read_X509(DSA_cert_file,NULL,NULL,NULL);
if (!cert) { /* error */ }
pk = X509_get_pubkey(cert);
if (!pk) { /* error */ }
if (pk->type != 116) { /* not a dsa key */ }
dsa = pk->pkey.dsa

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