I'm trying to encrypt something with RSA.
But my rsa libary doesn't seam to be able to use x509 keys.
So i tried to convert it to a DER key using openssl.
but i don't really understand how it works. i spotted two classes that seemed ok but i can't figure out how to use them.
the function are :
-i2d_X509
-X509
I did find a piece of code, but i can't understand it :
int len;
unsigned char *buf, *p;
len = i2d_X509(x, NULL);
buf = OPENSSL_malloc(len);
if (buf == NULL)
/* error */
p = buf;
i2d_X509(x, &p);
If you could help me out it would be great.
i2d_X509 means convert X509 object from internal representation (which is X509 structure) to DER encoded representation (which is copied over a buffer or in file).
So, in this code in line
len = i2d_X509(x, NULL);
you are determining the length of buffer or number of bytes required to represent the given certificate in DER from.
Then, you are allocating that much memory and final statement
len = i2d_X509(x, &p);
copies the X509 * certificate into this buffer in DER format.
This buffer you can persist in the file and save it as a certificate file say .cer, .crt and can open with any certificate tool.
Coming back to your problem, you can use this buffer into your program which accepts DER certificate.
But you mentioned key, did you?
If you need RSA public key, then you can do the following.
You may need to extract the key first by using X509_get_pubkey which will give key in EVP_PKEY structure.
EVP_PKEY * pkey;
pkey = X509_get_pubkey(x);
RSA * rsa;
rsa = EVP_PKEY_get1_RSA(pkey);
Now, output this RSA structure into DER.
int len;
unsigned char *buf, *p;
len = i2d_RSAPublicKey(rsa, buffer, buffer_length);
Allocate buffer to sufficient large length say 4000 depending on the key.
I think this would help you.
Related
I'm trying to implement AES decryption into one of my C++ program. The idea would be to use the following openSSL command line to generate the ciphered text (but to use the C++ API to decipher) :
openssl enc -aes-256-cbc -in plaintext.txt -base64 -md sha512 -pbkdf2 -pass pass:<passwd>
As the official doc is a bit too complicated I based my implementation on this tutorial to implement the decryption : https://eclipsesource.com/blogs/2017/01/17/tutorial-aes-encryption-and-decryption-with-openssl/
It does works well, but uses a deprecated key-derivation algorithm which I wanna replace with PBKDF2.
As far as I understand I should then use PKCS5_PBKDF2_HMAC() rather than the EVP_BytesToKey() suggested in the tutorial. My problem is that EVP_BytesToKey was able to derivate both key and IV from salt and password, where PKCS5_PBKDF2_HMAC only seems to derivate one at a time.
I couldn't find any more information/tutorial on how to get both key and IV, and tried several implementations, but couldn't find how the openSSL CLI generates the IV.
I'd really like to avoid to write the IV in either the CLI or the payload, the implementation of the tutorial was really convenient for that.
Could someone help me ?
Thanks, best regards
I realize the question is about a month old by now but I came across it in my search of information on doing something similar. Given the lack of answers here I went to the source for answers.
TL;DR (direct answer)
PKCS5_PBKDF2_HMAC() generates both key and IV at the same time. Although it's concatenated to one string. It's up you to split the string into the needed parts.
const EVP_CIPHER *cipher = EVP_aes_256_cbc();
int iklen = EVP_CIPHER_key_length(cipher);
int ivlen = EVP_CIPHER_iv_length(cipher);
PKCS5_PBKDF2_HMAC(pass, -1, salt, 8, iter, EVP_sha512(), iklen + ivlen, keyivpair);
memcpy(key, keyivpair, iklen);
memcpy(iv, keyivpair + iklen, ivlen);
Detailed description
Before going into specifics I feel that I should mention that I'm using C and not C++. I do however hope that the information provided is helpful even for C++.
Before anything else the string needs to be decoded from base64 in the application. After that we can move along to the key and IV generation.
The openssl tool indicates that a salt is being used by starting the encrypted string with the string 'Salted__' followed by 8 bytes of salt (at least for aes-256-cbc). In addition to the salt we also need to know the length of both the key and the IV. Luckily there are API calls for this.
const EVP_CIPHER *cipher = EVP_aes_256_cbc();
int iklen = EVP_CIPHER_key_length(cipher);
int ivlen = EVP_CIPHER_iv_length(cipher);
We also need to know the number of iterations (the default in openssl 1.1.1 when using -pbkdf2 is 10000), as well as the message digest function which in this case will be EVP_sha512() (as specified by option -md sha512).
When we have all of the above it's time to call PKCS5_PBKDF2_HMAC().
PKCS5_PBKDF2_HMAC(pass, -1, salt, 8, iter, EVP_sha512(), iklen + ivlen, keyivpair);
Short info on the arguments
pass is of type (const char *)
password length (int), if set to -1 the length will be determined by strlen(pass)
salt is of type (const unsigned char *)
salt length (int)
iteration count (int)
message digest (const EVP_MD *), in this case returned by EVP_sha512()
total length of key + iv (int)
keyivpair (unsigned char *), this is where the key and IV is stored
Now we need to split the key and IV apart and store them i separate variables.
unsigned char key[EVP_MAX_KEY_LENGTH];
unsigned char iv[EVP_MAX_IV_LENGTH];
memcpy(key, keyivpair, iklen);
memcpy(iv, keyivpair + iklen, ivlen);
And now we have a key and IV which can be used to decrypt data encrypted by the openssl tool.
PoC
To further clarify I wrote the following proof of concept (written on and for Linux).
/*
* PoC written by zoke
* Compiled with gcc decrypt-poc.c -o decrypt-poc -lcrypto -ggdb3 -Wall -Wextra
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
void bail() {
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]) {
if(argc < 3)
bail();
unsigned char key[EVP_MAX_KEY_LENGTH];
unsigned char iv[EVP_MAX_IV_LENGTH];
unsigned char salt[8]; // openssl tool uses 8 bytes for salt
unsigned char decodeddata[256];
unsigned char ciphertext[256];
unsigned char plaintext[256];
const char *pass = argv[1]; // use first argument as password (PoC only)
unsigned char *encodeddata = (unsigned char *)argv[2]; // use second argument
int decodeddata_len, ciphertext_len, plaintext_len, len;
// Decode base64 string provided as second option
EVP_ENCODE_CTX *ctx;
if(!(ctx = EVP_ENCODE_CTX_new()))
bail();
EVP_DecodeInit(ctx);
EVP_DecodeUpdate(ctx, decodeddata, &len, encodeddata, strlen((const char*)encodeddata));
decodeddata_len = len;
if(!EVP_DecodeFinal(ctx, decodeddata, &len))
bail();
EVP_ENCODE_CTX_free(ctx);
// openssl tool format seems to be 'Salted__' + salt + encrypted data
// take it apart
memcpy(salt, decodeddata + 8, 8); // 8 bytes starting at 8th byte
memcpy(ciphertext, decodeddata + 16, decodeddata_len - 16); // all but the 16 first bytes
ciphertext_len = decodeddata_len - 16;
// Get some needed information
const EVP_CIPHER *cipher = EVP_aes_256_cbc();
int iklen = EVP_CIPHER_key_length(cipher);
int ivlen = EVP_CIPHER_iv_length(cipher);
int iter = 10000; // default in openssl 1.1.1
unsigned char keyivpair[iklen + ivlen];
// Generate the actual key IV pair
if(!PKCS5_PBKDF2_HMAC(pass, -1, salt, 8, iter, EVP_sha512(), iklen + ivlen, keyivpair))
bail();
memcpy(key, keyivpair, iklen);
memcpy(iv, keyivpair + iklen, ivlen);
// Decrypt data
EVP_CIPHER_CTX *cipherctx;
if(!(cipherctx = EVP_CIPHER_CTX_new()))
bail();
if(!EVP_DecryptInit_ex(cipherctx, cipher, NULL, key, iv))
bail();
if(!EVP_DecryptUpdate(cipherctx, plaintext, &len, ciphertext, ciphertext_len))
bail();
plaintext_len = len;
if(!EVP_DecryptFinal_ex(cipherctx, plaintext + len, &len))
bail();
plaintext_len += len;
EVP_CIPHER_CTX_free(cipherctx);
plaintext[plaintext_len] = '\0'; // add null termination
printf("%s", plaintext);
exit(EXIT_SUCCESS);
}
Application tested by running
$ openssl aes-256-cbc -e -a -md sha512 -pbkdf2 -pass pass:test321 <<< "Some secret data"
U2FsdGVkX19ZNjDQXX/aACg7d4OopxqvpjclkaSuybeAxOhVRIONXoCmCQaG/Vg9
$ ./decrypt-poc test321 U2FsdGVkX19ZNjDQXX/aACg7d4OopxqvpjclkaSuybeAxOhVRIONXoCmCQaG/Vg9
Some secret data
The Key/IV generation used by the command line tool is in apps/enc.c and was very helpful when figuring this out.
I am new to socket programming, so be kind :)
I am writing a client-server application in C++ and using OpenSSL. Till now I have generated the public-private keys for the client and server and have exchanged it over the network. Now is the part where I want to encrypt my client's message using the server's public key. But my public_encrypt function returns gibberish. I know the methods which I am using are deprecated and there are better methods but the purpose is to get the hands dirty only.
Below is the function that invokes the encryption API. (Ignore the if part, it's for sending the clients public key)
#define RSA_SIZE 256
void sendMessage(int clientFD, uint16_t type, char *data, serverState *server){
uint16_t length = strlen(data);
unsigned char message[MESSAGE_SIZE];
if (server->state == 0)
{
memcpy(message, (char *)&length, sizeof(length));
memcpy(message + 2, (char *)&type, sizeof(type));
memcpy(message + 4, data, length);
send(clientFD, message, 4 + length, 0);
server->state = 1;
}
else
{
unsigned char encrypted[RSA_SIZE] = {0};
length = public_encrypt(reinterpret_cast<unsigned char *>(data), length, server->key, encrypted);
assert(length != -1);
printf("%s\n", encrypted);
memcpy(message, (char *)&length, sizeof(length));
memcpy(message + 2, (char *)&type, sizeof(type));
memcpy(message + 4, encrypted, length);
send(clientFD, message, 4 + length, 0);
}}
This is the code for the encryption
int padding = RSA_PKCS1_OAEP_PADDING;
RSA *createRSA(unsigned char *key, int pub){
RSA *rsa = NULL;
BIO *keybio;
keybio = BIO_new_mem_buf(key, -1);
if (keybio == NULL)
{
printf("Failed to create key BIO");
return 0;
}
if (pub)
{
rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL);
}
else
{
rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);
}
if (rsa == NULL)
{
printf("Failed to create RSA");
}
return rsa;}
int public_encrypt(unsigned char *data, int data_len, unsigned char *key, unsigned char *encrypted){
printf("Data:%s\n:", data);
printf("Data Length:%d\n:", data_len);
printf("Server's Key:\n%s\n:", key);
RSA *rsa = createRSA(key, 1);
int result = RSA_public_encrypt(data_len, data, encrypted, rsa, padding);
return result;}
Please check out the link https://i.stack.imgur.com/WJn7e.png to see my output.
PS: Sorry for such a long post.
The output of RSA is a random value between 0 and the modulus of the RSA private key, encoded as an unsigned big endian octet string (octet string is just another name for byte array, a char[] in C / C++). It contains bytes with any value, and it is therefore certainly not ASCII. If you want ASCII you have to base 64 encode the ciphertext.
However, quite often ciphertext is "stringified" for no good reason at all, so only do this if this is necessary within your protocol / system. Python strings are made somewhat readable for you by the Python runtime. I'm not sure if that's a good thing or not - it's certainly not a good idea to copy that string as it is only Python proprietary.
C is not as forgiving, if you treat the binary array as text you'll run into trouble, as it can contain any character, including control characters and the NUL character (00), which can play merry hell with functions such as strlen and many others that expect a textual string instead of an array of bytes (both are usually based on char in C/C++).
I'm trying to decrypt a file in C++. This file is encrypted with the following command:
openssl enc -nosalt -aes-128-cbc -pass pass:test -in "test.txt" -out "test_enc.txt" -p
The console shows the key=098F6BCD4621D373CADE4E832627B4F6 and iv=0A9172716AE6428409885B8B829CCB05.
In C++ I have included the #include openssl/aes.h line and try to decrypt with the following code:
const char *indata = string.toAscii().constData();
unsigned char outdata[strlen(indata)];
unsigned char ckey[] = "098F6BCD4621D373CADE4E832627B4F6";
unsigned char ivec[] = "0A9172716AE6428409885B8B829CCB05";
/* data structure that contains the key itself */
AES_KEY key;
/* set the encryption key */
AES_set_decrypt_key(ckey, 256, &key);
AES_cbc_encrypt((unsigned char*) indata, outdata, strlen(indata), &key, ivec, AES_DECRYPT);
QString result = QString((const char*) outdata);
return result;
The variable outdata contains different value than before encryption with OpenSSL.
You specify -aes-128-cbc as an option on OpenSSL so the key and initialization vector will be 128 bits long. openssl prints these out as hex strings, as they would be obfuscated on the console if printed binary.
Therefor you should initialize your ckey[] and ivec[] as the binary value of the hex strings like this:
unsigned char ckey[] = "\x09\x8F\x6B\xCD\x46\x21\xD3\x73\xCA\xDE\x4E\x83\x26\x27\xB4\xF6";
unsigned char ivec[] = "\x0A\x91\x72\x71\x6A\xE6\x42\x84\x09\x88\x5B\x8B\x82\x9C\xCB\x05";
and also, use key length 128 instead of 256 in:
AES_set_decrypt_key(ckey, 128, &key);
OpenSSL creates the key using the password you offer, and also the vector you specified is related to decryption and encryption. Make sure you have the same key and vector while decrypting the text.
I am using this simple function for decrypting a AES Encrypted string
unsigned char *aes_decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext, int *len)
{
int p_len = *len, f_len = 0;
unsigned char *plaintext = (unsigned char*)malloc(p_len + 128);
memset(plaintext,0,p_len);
EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
EVP_DecryptUpdate(e, plaintext, &p_len, ciphertext, *len);
EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len);
*len = p_len + f_len;
return plaintext;
}
The problem is that len is returning a value that does not match the entire decoded string. What could be the problem ?
When you say "string", I assume you mean a zero-terminated textual string. The encryption process is dependent on a cipher block size, and oftentimes padding. What's actually being encoded and decoded is up to the application... it's all binary data to the cipher. If you're textual string is smaller than what's returned from the decrypt process, your application needs to determine the useful part. So for example if you KNOW your string inside the results is zero-terminated, you can get the length doing a simple strlen. That's risky of course if you can't guarantee the input... probably better off searching the results for a null up to the decoded length...
If you are using cipher in ECB, CBC or some other chaining modes, you must pad plain text to the length, which is multiple of cipher block length. You can see a PKCS#5 standard for example. High-level functions like in OpenSSL can perform padding transparently for programmer. So, encrypted text can be larger than plain text up to additional cipher block size.
SOLVED: I was dumb. First argument of encrypt should have been key.size() and first argument of decrypt should have been RSA_size(myKey).
ORIGINAL QUESTION
Hey guys, I'm having some trouble figuring out how to do this.
Basically I just want a client and server to be able to send each other encrypted messages.
This is going to be incredibly insecure because I'm trying to figure this all out so I might as well start at the ground floor.
So far I've got all the keys working but encryption/decryption is giving me hell.
I'll start by saying I am using C++ but most of these functions require C strings so whatever I'm doing may be causing problems.
Note that on the client side I receive the following error in regards to decryption.
error:04065072:rsa routines:RSA_EAY_PRIVATE_DECRYPT:padding check failed
I don't really understand how padding works so I don't know how to fix it.
Anywho here are the relevant variables on each side followed by the code.
Client:
RSA *myKey; // Loaded with private key
// The below will hold the decrypted message
unsigned char* decrypted = (unsigned char*) malloc(RSA_size(myKey));
/* The below holds the encrypted string received over the network.
Originally held in a C-string but C strings never work for me and scare me
so I put it in a C++ string */
string encrypted;
// The reinterpret_cast line was to get rid of an error message.
// Maybe the cause of one of my problems?
if(RSA_private_decrypt(sizeof(encrypted.c_str()), reinterpret_cast<const unsigned char*>(encrypted.c_str()), decrypted, myKey, RSA_PKCS1_OAEP_PADDING)==-1)
{
cout << "Private decryption failed" << endl;
ERR_error_string(ERR_peek_last_error(), errBuf);
printf("Error: %s\n", errBuf);
free(decrypted);
exit(1);
}
Server:
RSA *pkey; // Holds the client's public key
string key; // Holds a session key I want to encrypt and send
//The below will hold the encrypted message
unsigned char *encrypted = (unsigned char*)malloc(RSA_size(pkey));
// The reinterpret_cast line was to get rid of an error message.
// Maybe the cause of one of my problems?
if(RSA_public_encrypt(sizeof(key.c_str()), reinterpret_cast<const unsigned char*>(key.c_str()), encrypted, pkey, RSA_PKCS1_OAEP_PADDING)==-1)
{
cout << "Public encryption failed" << endl;
ERR_error_string(ERR_peek_last_error(), errBuf);
printf("Error: %s\n", errBuf);
free(encrypted);
exit(1);
}
Let me once again state, in case I didn't before, that I know my code sucks but I'm just trying to establish a framework for understanding this.
I'm sorry if this offends you veteran coders.
Thanks in advance for any help you guys can provide!
Maybe not the only problem but: The first argument to RAS_xxxcrypt functions is the number of bytes of the buffers. sizeof(key.c_str()) does not yield the number of bytes in key, it yields the size of the type of key.c_str()'s result type, i.e. sizeof(const char*). You probably want to pass the number of chars in the string instead, which can be obtained with the size() member function.