EVP_KEY get raw private key in C - c

Good day,
I have been trying to do a simple exercise where I could generate the public and the private key using RSA with Openssl and print them both. My code looks something like this:
size_t private_key_len = KEY_LENGTH;
EVP_PKEY *pkey = EVP_RSA_gen(KEY_LENGTH);
if (pkey == NULL)
{
fprintf(stderr, "error: rsa gen\n");
ERR_print_errors_fp(stderr);
return NULL;
}
unsigned char *private_key = calloc((KEY_LENGTH + 1),sizeof(unsigned char));
EVP_PKEY_get_raw_private_key(pkey, private_key, &private_key_len);
printf("%s\n",private_key);
So normally it should print the private key, given that KEY_LENGTH is 1024, however it just prints nothing (The zeros initialized by calloc). I have tried with malloc too, the result is similar the only difference being that it prints 0xBE.
So basically the array private_key is never filled, and I have no idea why.
What am I missing to make this work?
Thanks in advance!

Quoting the man page with emphasis changed:
EVP_PKEY_get_raw_private_key() fills the buffer provided by priv with raw private key data. The size of the priv buffer should be in *len on entry to the function, and on exit *len is updated with the number of bytes actually written. If the buffer priv is NULL then *len is populated with the number of bytes required to hold the key. The calling application is responsible for ensuring that the buffer is large enough to receive the private key data. This function only works for algorithms that support raw private keys. Currently this is: EVP_PKEY_HMAC, EVP_PKEY_POLY1305, EVP_PKEY_SIPHASH, EVP_PKEY_X25519, EVP_PKEY_ED25519, EVP_PKEY_X448 or EVP_PKEY_ED448.
Notice that RSA is not one of the supported algorithms.
You can "print" an RSA key either by converting each of its components (n,e,d,p,q,dp,dp,qinv) to printable form, which EVP_PKEY_print_private does for you, or getting the encoding of the whole key in PEM which is 'printable' in the sense of being printable and typable characters, but not the sense of being easily understood (or copied or created) by people, with PEM_write_PrivateKey or PEM_write_RSAPrivateKey.
Also, the value you pass to EVP_RSA_gen is in bits, but the size of displayed components of an RSA key (other than e, which is small) will be in hex or decimal digits or (mostly) base64 characters.

Related

Why does dynamically allocated array does not update with the new data coming?

I am trying to receive a message from the socket server which sends a large file of around 7MB. Thus in the following code, I try to concatenate all data into one array s from buffer. But as I try the following, I see that the length of s does not change at all, although the total bytes received continue to increase.
char buffer[300];
char* s = calloc(1, sizeof(char));
size_t n = 1;
while ((b_recv = recv(socket_fd,
buffer,
sizeof(buffer), 0)) > 0) {
char *temp = realloc(s, b_recv + n);
s = temp;
memcpy(s + n -1, buffer, b_recv);
n += b_recv;
s[n-1] = '\0';
printf("%s -- %zu",s, strlen(s));
}
free(s);
Is this not the correct way to update receive data of varying sizes? Also when I try to print s, it gives some random question mark characters. What is the mistake that I am making?
Why does dynamically allocated array does not update with the new data coming?
You have not presented any reason to believe that the behavior is as the question characterizes it. You are receiving binary data and storing it in memory, which is fine, but you cannot expect sensible results from treating such data as if it were a C string. Not even when you replace the last byte with a string terminator.
Binary data can and generally does contain bytes with value 0. C strings use such bytes as terminators marking the end of the string data, so, for example, strlen will measure only the number of bytes before the first zero byte, regardless of how many additional bytes have been stored after it. Moreover, even if you do not receive any zero bytes at all, your particular code inserts them, clobbering some of the real bytes received.
You may attempt to print such data to the console as if it were text, but if in fact it does not consist of text encoded according to the runtime character encoding then there is no reason to expect the resulting display to convey useful information. Instead, examine it in memory via a debugger, or write the raw bytes to a file and examine the result with a hex editor, or write them (still raw) through a filter that converts to hexadecimal or some other text representation, or similar. And you have as many bytes to examine as you have copied to the allocated space. You're keeping track of that already, so you don't need strlen() to tell you how many that is.

Encrypt/decrypt single octet with no padding using RSA_public_encrypt

I am trying to encrypt the decimal number 1 with no padding using RSA_public_encrypt. Of course the result of this encryption should be 1 again. But in fact it is
Cipher:
5228673350895653896383201058815462877426144378065091716669226352446563314310753759122681282019606231774744798481087922688401463790373072832646006963123849664906627085625735903412727878929921552918090336305565457632762612646244666075746315455854479912774071351362988587769873374454538153517081634942043196173525640004822126101409481060928041484882764412543090155480476597339865942466034635200613687987398189458867055031285752787781897557950334515480742629110423562374837915117562936777536259795189526199285672603820591631423849227226304977053415509662563290672486408474162339095049681422645956742727171481170704
What am I doing wrong?
I am aware that there are high level routines in OpenSSL and also that using RSA without padding is insecure. This is just an experiment.
I am also aware that OpenSSL uses the Big Endian format. So I also tried to change the order of the input bytes to the encryption - then 1 was encrypted to 1. But implementing the decryption, taking into account the reverse byte order in both encryption and decryption, some numbers, like 2, were encrypted-decrypted correctly and others were not...very strange!
Here is my code for the encryption:
#include <stdio.h>
#include "rsa.h"
#include "pem.h"
#define LENGTH 1000
int main(void)
{
//Variables for message and cipher
//BIGNUM format
BIGNUM message_bignum, cipher_bignum;
BIGNUM *ptr_message_bignum=&message_bignum;
BIGNUM *ptr_cipher_bignum=&cipher_bignum;
//Formatted as decimal string
unsigned char message_decimal[]="1";
//binary format for input to RSA encryption
unsigned char message[LENGTH]={0},cipher[LENGTH]={0};
//Initialise RSA structure
RSA *rsa=RSA_new();
//Get public key
BIO *publickey_handle=BIO_new_file("rsa_publickey.txt","rb");
if(publickey_handle==NULL)
{
fprintf(stderr,"Could not open key file!\n");
return -1;
}
PEM_read_bio_RSA_PUBKEY(publickey_handle,&rsa,NULL,NULL);
if(BIO_free(publickey_handle)==0)
{
fprintf(stderr,"Error closing key file!\n");
return -1;
}
//Convert message to BIGNUM format
BN_init(ptr_message_bignum);
BN_dec2bn(&ptr_message_bignum,(const char *)message_decimal);
//Convert message as BIGNUM to binary format
BN_bn2bin(ptr_message_bignum,message);
//Encrypt message in binary format
if(RSA_public_encrypt(RSA_size(rsa),(const unsigned char*)message,cipher,rsa,RSA_NO_PADDING)==-1)
{
fprintf(stderr,"Error during encryption!\n");
return -1;
}
//Convert cipher to BIGNUM format
BN_init(ptr_cipher_bignum);
BN_bin2bn((const unsigned char*)cipher,RSA_size(rsa),ptr_cipher_bignum);
//Convert cipher from BIGNUM format to decimal format and print it to stdout
printf("Cipher:\n%s",BN_bn2dec(ptr_cipher_bignum));
printf("\nDone!\n");
return 0;
}
As documented:
BN_bn2bin() converts the absolute value of a into big-endian form and stores it at to. to must point to BN_num_bytes(a) bytes of memory.
But it doesn't. It first contains a single 00 because of message[LENGTH]={0}. Then it contains, well, whatever is in LENGTH amount of bytes.
In RSA_public_encrypt you use RSA_size(rsa) as first parameter, flen. flen however should be the result of BN_num_bytes(&ptr_message_bignum), i.e. 1 byte. The output location cipher on the other hand must be able to store RSA_size(rsa) bytes.
Possibly the confusion comes from the fact that BN_bn2bin will store the value as the shortest Big Endian encoding (in bytes). For the value 1 that will of course be one byte, not RSA_size(rsa) bytes.
Edit: I found the RSA_padding_add_none method that could be able to resize.

Appending an Int to a char * in C

So I am looking to append the length of a cipher text onto the end of the char array that I am storing the cipher in. I am not a native to C and below is a test snippet of what I have devised that I think works.
...
int cipherTextLength = 0;
unsigned char *cipherText = NULL;
...
EVP_EncryptFinal_ex(&encryptCtx, cipherText + cipherTextLength, &finalBlockLength);
cipherTextLength += finalBlockLength;
EVP_CIPHER_CTX_cleanup(&encryptCtx);
// Append the length of the cipher text onto the end of the cipher text
// Note, the length stored will never be anywhere near 4294967295
char cipherLengthChar[1];
sprintf(cipherLengthChar, "%d", cipherTextLength);
strcat(cipherText, cipherLengthChar);
printf("ENC - cipherTextLength: %d\n", cipherTextLength);
...
The problem is I don't think using strcat when dealing with binary data is going to be trouble free. Could anyone suggest a better way to do this?
Thanks!
EDIT
Ok, so I'll add a little context as to why I was looking to append the length. In my encrypt function, the function EVP_EncryptUpdate requires the length of the plainText being encrypted. As this is much more easy to obtain, this part isn't a problem. However, similarly, using EVP_DecryptFinal_ex in my decrypt function requires the length of the ciperText being decrypted, so I need to store it somewhere.
In the application where I am implementing this, all I am doing is changing some poor hashing to proper encryption. To add further hassle, the way the application is I first need to decrypt information read in from XML, do something with it, then encrypt it and rewrite it to XML again, so I need to have this cipher length stored in the cipher somehow. I also don't have scope to redesign this.
Instead of what you are doing now, it may be smarter to encode the ciphertext size to a location before the ciphertext itself. Once you start decrypting, it is not very useful to find the size at the end. You need to know the end to get the size to find the end, not very helpful.
Furthermore, the ciphertext is binary, so you don't need to convert anything to string. You would like to convert it to a fixed number of bytes (otherwise you don't know the size of the size :P ). So create a bigger buffer (4 bytes more than you require for the ciphertext), and start encrypting to offset 4 forwards. Then copy the size of the ciphertext in at the start of the buffer.
If you don't know how to encode an integer, take a look at - for instance - this question/ answer. Note, this will only encode 32 bits for a maximum size of the ciphertext of 2^32, about 4 GiB. Furthermore, the link pointed to use Big Endian encoding. You should use either Big Endian (preferred for crypto code) or Little Endian encoding - but don't mix the two.
Neither the ciphertext nor the encoded size should be used as a character string. If you need a character string, my suggestion is to base 64 encode the buffer up to the end of the ciphertext.
I hope you are having enough big arrays both for cipherText and cipheLegthChar to store the required text. Hence instead of
unsigned char *cipherText = NULL;
You can have
unsigned char cipherText[MAX_TEXT];
similarly for
cipherLenghthChar[MAX_INT];
Or you can have them dynamically allocated.
where MAX_TEXT and MAX_INT max buffer size to store text and integer. Also after first call of EVP_EncryptFinal_ex NULL terminate cipherText so that you strcat works.
The problem is I don't think using strcat when dealing with binary data is going to be trouble free.
Correct! That's not your only problem though:
// Note, the length stored will never be anywhere near 4294967295
char cipherLengthChar[1];
sprintf(cipherLengthChar, "%d", cipherTextLength);
Even if cipherTextLength is 0 here, you've gone out of bounds, since sprintf will add a null terminator, making a total of two chars -- but cipherLengthChar only has room for one.
If you consider, e.g. 4294967295, as a string, that's 10 chars + '\0' = 11 chars.
It would appear that finalBlockLength is the length of the data put into cipherText. However, the EVP_EncryptFinal_ex() call will probably fail in one way or another, or at least not do what you want, since cipherText == NULL. You then add 0 to that (== 0 aka. still NULL) and submit it as a parameter. Either you need a pointer to a pointer there (if EVP_EncryptFinal_ex is going to allocate space for you), or else you have to make sure there is enough room in cipherText to start with.
With regard to tacking text (or whatever) onto the end, you can just use sprintf directly:
sprintf(cipherText + finalBlockLength, "%d", cipherTextLength);
Presuming that cipherText is non-NULL and has enough extra room in it (see first couple of paragraphs).
However, I'm very dubious that doing that will be useful later on, but since I don't have any further context, I can't say more.

I can't decrypt the string after I encrypt the string with PolarSSL AES-CBC

I wrote a program to encrypt a string with PolarSSL AES-CBC
this is my code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <polarssl/aes.h>
#include <polarssl/havege.h>
int main()
{
char buff[2][64] = {"Who can tell me WHY?", ""};
havege_state hs;
int retval;
unsigned char IV[16];
aes_context enc_ctx;
aes_context dec_ctx;
aes_setkey_enc(&enc_ctx, "password", 256);
aes_setkey_dec(&dec_ctx, "password", 256);
havege_init(&hs);
havege_random(&hs, IV, 16);
//encrypt
aes_crypt_cbc(&enc_ctx, AES_ENCRYPT, 64, IV, buff[0], buff[1]);
havege_random(&hs, IV, 16);
//decrypt
aes_crypt_cbc(&dec_ctx, AES_DECRYPT, 64, IV, buff[1],buff[0]);
printf("After decrypt:%s\n", buff[0]);
return 0;
}
But when I run it, I just got the wrong text after it decrypt.
I don't understand AES algorithm clearly, because my english is very bad, It's too hard for me to read some articles.
-------------------------Added By midCat--------------------------------------------
I followed you advice, and Change me code
now I use the same IV and 256-bit key, this is the new code
int main()
{
char buff[2][64] = {"ABCDEFGHIJKLMN", ""};
havege_state hs;
int retval;
unsigned char IV[16];
unsigned char IV2[16];
unsigned char key[32];
aes_context enc_ctx;
aes_context dec_ctx;
havege_init(&hs);
havege_random(&hs, IV, 16);
havege_random(&hs, key, 32);
strncpy(IV, IV2, 16); //copy IV
aes_setkey_enc(&enc_ctx, key, 256);
aes_setkey_dec(&dec_ctx, key, 256);
//encrypt
aes_crypt_cbc(&enc_ctx, AES_ENCRYPT, 64, IV, buff[0], buff[1]);
printf("Before encrypt:%s\n", buff[0]);
//decrypt
aes_crypt_cbc(&dec_ctx, AES_DECRYPT, 64, IV2, buff[1],buff[0]);
printf("After decrypt:%s\n", buff[0]);
return 0;
}
I compiler it, and run for many times, got the same output:
Before encrypt:ABCDEFGHIJKLMN
After decrypt:ABCDEFGHYC
LMN
How to got IV?
EDIT: As Daniel pointed out in his answer, one big problem is that you are trying to use a random IV to decrypt (which will not give you the results you expect). However, I suggest that you read the rest of this answer as well.
First of all, aes_set_key_enc and aes_set_key_dec do not take passwords as input, they take keys. A key should be a completely random value as long as the key length (a random string of 32 bytes in your case, since you want to use a 256-bit key).
In your case you're calling aes_set_key_enc/aes_set_key_dec with a short password, but telling it that it should expect a 256-bit key, this will lead to these functions using memory outside of your password as part of the key, and will lead to the encryption and decryption keys to be different.
To recap, AES (or any cipher algorithm for that matter) expects random keys, the algorithms themselves have no concept of expanding a password to a key.
If you want to use a password as a key you need some function that expands a non-random password into a pseudo-random key. The only sane way to do this is to use a function that is designed to be cryptographically secure, but also slow, this is to mitigate the risk of brute-force attacks on the password.
Good functions to use are Colin Percivals scrypt, bcrypt or PBKDF2 of
which I would recommend scrypt the most.
However, I want to stress that you probably shouldn't be working at this abstraction level at all. The primitives that you are using (block ciphers, CBC-mode) are at a really low abstraction level, and building a cryptosystem using those primitives can be really dangerous. For example you do not use any kind of authentication on your ciphertext, which might open up your implementation to chosen ciphertext attacks.
I think that the best way to if you want to use cryptography in your application is to try to work at a higher abstraction level, for example using Dan Bernsteins excellent NaCl or Googles KeyCzar.
To recap, your specific problem can be solved by using 256-bit keys, but you should think twice about implementing your own cryptosystem at this abstraction level.
You are using a random IV to decrypt. That is wrong. You need the same IV for decryption as you used for encryption. Simply remove the second call to
havege_random(&hs, IV, 16);
between the encrypt and the decrypt.
Also make sure to use a 32 byte char array as second argument to aes_setkey_enc and aes_setkey_dec because you are reading past the end of the string as it is now. Depending on the compiler it might lead to different keys being used for encryption and decryption (and thus, fail).
(Edit for second half of question)
Change this:
strncpy(IV, IV2, 16); //copy IV
to this:
memcpy(IV2, IV, 16); //copy IV
That's memcpy instead of strncpy because the IV is random data which can contain the string terminator '\0', and the first argument to both of these functions is the destination, not the source. You copied a string from IV2 to IV instead of memory from IV to IV2.
And to answer the question on how to get the IV for decryption: Normally, the IV is just sent along with the ciphertext. Each IV must only once be used once and it must not be predictable, but it is not necessary to keep it secret. A common construction is to choose a random IV for every message and simply prepend the IV to the ciphertext or to store it in a file header, depending on the scenario.
That being said, I agree with the advice to not construct your own crypto protocols using crypto primitives unless you know what you are doing.
I just changed 64 to 14 in
aes_crypt_cbc(&dec_ctx, AES_DECRYPT, 64, IV2, buff[1],buff[0]);
and every thing worked fine!

How do I send an array of integers over TCP in C?

I'm lead to believe that write() can only send data buffers of byte (i.e. signed char), so how do I send an array of long integers using the C write() function in the sys/socket.h library?
Obviously I can't just cast or convert long to char, as any numbers over 127 would be malformed.
I took a look at the question, how to decompose integer array to a byte array (pixel codings), but couldn't understand it - please could someone dumb it down a little if this is what I'm looking for?
Follow up question:
Why do I get weird results when reading an array of integers from a TCP socket?
the prototype for write is:
ssize_t write(int fd, const void *buf, size_t count);
so while it writes in units of bytes, it can take a pointer of any type. Passing an int* will be no problem at all.
EDIT:
I would however, recomend that you also send the amount of integers you plan to send first so the reciever knows how much to read. Something like this (error checking omitted for brevity):
int x[10] = { ... };
int count = 10;
write(sock, &count, sizeof(count));
write(sock, x, sizeof(x));
NOTE: if the array is from dynamic memory (like you malloced it), you cannot use sizeof on it. In this case count would be equal to: sizeof(int) * element_count
EDIT:
As Brian Mitchell noted, you will likely need to be careful of endian issues as well. This is the case when sending any multibyte value (as in the count I recommended as well as each element of the array). This is done with the: htons/htonl and ntohs/ntohl functions.
Write can do what you want it to, but there's some things to be aware of:
1: You may get a partial write that's not on an int boundary, so you have to be prepared to handle that situation
2: If the code needs to be portable, you should convert your array to a specific endianess, or encode the endianess in the message.
The simplest way to send a single int (assuming 4-byte ints) is :
int tmp = htonl(myInt);
write(socket, &tmp, 4);
where htonl is a function that converts the int to network byte order. (Similarly,. when you read from the socket, the function ntohl can be used to convert back to host byte order.)
For an array of ints, you would first want to send the count of array members as an int (in network byte order), then send the int values.
Yes, you can just cast a pointer to your buffer to a pointer to char, and call write() with that. Casting a pointer to a different type in C doesn't affect the contents of the memory being pointed to -- all it does is indicate the programmer's intention that the contents of memory at that address be interpreted in a different way.
Just make sure that you supply write() with the correct size in bytes of your array -- that would be the number of elements times sizeof (long) in your case.
It would be better to have serialize/de-serialize functionality in your client /server program.
Whenever you want to send data, serialize the data into a byte buffer and send it over TCP with byte count.
When receiving data, de-serialize the data from buffer to your own interpretation .
You can interpret byte buffer in any form as you like. It can contain basic data type, objects etc.
Just make sure to take care of endianess and also alignment stuff.
Declare a character array. In each location of the array, store integer numbers, not characters.
Then you just send that.
For example:
char tcp[100];
tcp[0] = 0;
tcp[1] = 0xA;
tcp[2] = 0xB;
tcp[3] = 0xC;
.
.
// Send the character array
write(sock, tcp, sizeof(tcp));
I think what you need to come up with here is a protocol.
Suppose your integer array is:
100, 99, 98, 97
Instead of writing the ints directly to the buffer, I would "serialize" the array by turning it into a string representation. The string might be:
"100,99,98,97"
That's what would be sent over the wire. On the receiving end, you'd split the string by the commas and build the array back up.
This is more standardised, is human readable, and means people don't have to think about hi/lo byte orders and other silly things.
// Sarcasm
If you were working in .NET or Java, you'd probably encode it in XML, like this:
<ArrayOfInt><Int>100</Int><Int>99</Int><Int>98</Int><Int>97</Int></ArrayOfInt>
:)

Resources