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

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

Related

OpenSSL C API - getting keylength of public key in DER encoding

I would like to determine the key length of a public key encoded in DER format while using the OpenSSL API in C.
It is quite straight-forward to do this with a key encoded in PEM format:
FILE *fp = fopen(argv[1], "rb");
RSA *rsa_key = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
int keylength = RSA_size(rsa_key);
(assuming that the key to be loaded has been passed as an argument)
Unfortunately there is no equivalent DER_read_RSA_PUBKEY function.
I have been trying it with i2d_RSA_PUBKEY_fp and d2i_RSA_PUBKEY_fp but to no avail - only segmentation faults.
The difference between _RSA_PUBKEY and _RSAPublicKey seems to be that the former uses a SubjectPublicKeyInfo (certificate public key) structure and the latter uses a PKCS#1 RSAPublicKey structure.
The d2i_* function family is available for converting DER to openssl internal datastructures.
So, you should be able to use:
FILE *fp = fopen(argv[1], "rb");
RSA *rsa_key = d2i_RSAPublicKey_fp(fp, NULL);
int keylength = RSA_size(rsa_key);
to achieve what you want.

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

Extract RSA public key from a x509 char array with openssl

Here is a certificate in x509 format that stores the public key and the modulo:
const unsigned char *certificateDataBytes = {/*data*/};
Using OpenSSL and C, how can I convert it into an RSA object? I've tried several methods but I can't get it to work in RSA_public_encrypt
I think you mean public key into RSA * structure.
Since, you have certificate in bytes, if it is in DER encoded bytes, then you need to first convert it into X509 * structure.
X509 * cert;
EVP_PKEY * pubkey;
//length is the length of the certificateDataBytes in terms of bytes.
cert = d2i_x509 (NULL, certificateDataBytes, length);
pubkey = X509_get_pubkey (cert);
Please note that if certificate has RSA public key, then you can get RSA public key as follows:
RSA * rsa
rsa = EVP_PKEY_get1_RSA(pubkey);
//Now rsa contains RSA public key. Use it.
//After use, free the pubkey
EVP_PKEY_free (pubkey);
I hope this must solve your purpose.
If certificate encoding is different, use different function. Once, you get X509 *, rest step is same.

Openssl/libcrypto AES 128 encoding using the KEY

I am encrypting a certain string using AES-128-ECB and then save the result in a file e.g test.enc
Here is my method that does the encryption:
int do_crypt(char *outfile) {
unsigned char outbuf[1024];
int outlen, tmplen;
unsigned char key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
char intext[] = "Some Text";
EVP_CIPHER_CTX ctx;
FILE *out;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, key, NULL);
if(!EVP_EncryptUpdate(&ctx, outbuf, &outlen, intext, strlen(intext))) {
/* Error */
return 0;
}
/* Buffer passed to EVP_EncryptFinal() must be after data just
* encrypted to avoid overwriting it.
*/
if(!EVP_EncryptFinal_ex(&ctx, outbuf + outlen, &tmplen))
{
/* Error */
return 0;
}
outlen += tmplen;
EVP_CIPHER_CTX_cleanup(&ctx);
/* Need binary mode for fopen because encrypted data is
* binary data. Also cannot use strlen() on it because
* it wont be null terminated and may contain embedded
* nulls.
*/
out = fopen(outfile, "wb");
fwrite(outbuf, 1, outlen, out);
fclose(out);
return 1;
}
As you can see the key is the actual password and in order to decode the encrypted file following command line should be executed:
openssl aes-128-ecb -in test.enc -K 000102030405060708090A0B0C0D0E0F -d
"000102030405060708090A0B0C0D0E0F" is a password hex representation I use in the code above 0123456789191112131415. As I understand the password can be encrypted as well using MD5 algorithm.
The question is how to encrypt data using actual KEY derived from password and not the password itself using libcrypto?
Take a look at EVP_BytesToKey.
My comments in an old app tell me that BytesToKey is out of date and you should perhaps consider looking at PKCS5_v2_PBE_keyivgen or similar. But essentially, a highly simplified way of doing it is you derive your Key as a hash from your password and a suitable salt value:
EVP_DigestInit_ex(...)
EVP_DigestUpdate(...Password...)
EVP_DigestUpdate(...Salt...)
EVP_DigestFinal_ex(...)
then you use the newly generated Key to derive your IV by:
EVP_DigestInit_ex(...)
EVP_DigestUpdate(...Key...)
EVP_DigestUpdate(...Password...)
EVP_DigestUpdate(...Salt...)
EVP_DigestFinal_ex(...)
A browse of the OpenSSL source code is most useful for looking up stuff like this.
Disclaimer: I'm not a C programmer (the app in question was Delphi using OpenSSL DLLs) nor a security expert so take these suggestions as a starting point, read the proper docs and use proper functions where possible!!...

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