C - OpenSSL AES Encryption key issue - c

I'm porting an AES encryption procedure from Javascript to C.
I'm using OpenSSL.
For the encryption I'm using ECB 256 mode.
The encryption goes successfully but the final Base64 does not correspond to the result I get in JS.
By testing with several online tools, the output I was getting from the C version was actually correct but different from the one from JS.
Then I realized the problem thanks to this tool https://gchq.github.io/CyberChef.
The key I'm providing is a MD5 hash like this:
aa8744256a89cba0d77f0686916f8b2e
If I pass the key as an HEX string in the CyberChef tool, the output corresponds to the JS version.
The C version is reading the key as an UTF string.
How can I force it to read the key as an HEX string?
Here's the code:
//generating MD5 of key
char keyHash[33];
unsigned char digest[MD5_DIGEST_LENGTH];
MD5((unsigned char*)keyString, strlen(keyString), (unsigned char*)&digest);
for (int i = 0; i < 16; i++)
sprintf(&keyHash[i * 2], "%02x", (unsigned int)digest[i]);
char out[256];
int outlen = 0;
//initializing context
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
//inizializing encryption with 256 ECB Cipher and MD5 Hash as Key
EVP_EncryptInit(ctx, EVP_aes_256_ecb(), keyHash, 0);
//setting padding
EVP_CIPHER_CTX_set_padding(ctx, EVP_PADDING_PKCS7);
//encrypting
EVP_EncryptUpdate(ctx, out, &outlen, user, strlen(user));
EVP_EncryptFinal(ctx, out, &outlen);
EVP_CIPHER_CTX_free(ctx);
//parsing the result to Base64
BIO *mbio = BIO_new(BIO_s_mem());
BIO *b64bio = BIO_new(BIO_f_base64());
BIO_set_flags(b64bio, BIO_FLAGS_BASE64_NO_NL);
BIO *bio = BIO_push(b64bio, mbio);
BIO_write(bio, out, outlen);
BIO_flush(bio);
char* data = NULL;
size_t datalen = 0;
datalen = BIO_get_mem_data(mbio, &data);
data[datalen] = '\0';
//printing the result
printf("encrypted data: [%s]\n", data);

Related

Verify "PKCS5_PBKDF2_HMAC" is generating required byte of key

I am not getting a correct length of key generated by "PKCS5_PBKDF2_HMAC" oppenssl api.
I am expecting a length of key will be 32 (ie 32*8 = 256 bits).
Am i doing something wrong or missing something here?
unsigned char key[32];
bzero(key,sizeof(key));
const char * password1 = "locpasswordkey";
size_t plen = strlen(password1);
const char * salt = "fixedsaltlengthsforenc";
size_t slen = strlen(salt);
if(PKCS5_PBKDF2_HMAC(password1, (int)plen, (const unsigned char *)salt, (int)slen,65536, EVP_sha256(), (int)sizeof(key), key) == 0)
{
cout << "PKCS5_PBKDF2_HMAC_KEY() failed" << endl;
}
cout << "KeyLen: " << strlen(key) << endl
How we can be sure that "PKCS5_PBKDF2_HMAC" is generating required bits key.
Using this key i am performing decryption, but "EVP_CIPHER_CTX_set_key_length" function reporting below error
error:0607A082:digital envelope routines:EVP_CIPHER_CTX_set_key_length:invalid key length:evp_enc.c:651:
Below is my decryption function.
std::string decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *iv ) {
EVP_CIPHER_CTX *ctx;
int len;
int plaintext_len;
unsigned char* plaintext = new unsigned char[ciphertext_len];
bzero(plaintext,ciphertext_len);
if(!(ctx = EVP_CIPHER_CTX_new())) handleOpenSSLErrors();
if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
handleOpenSSLErrors();
EVP_CIPHER_CTX_set_key_length(ctx, EVP_MAX_KEY_LENGTH);
if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
handleOpenSSLErrors();
plaintext_len = len;
if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) handleOpenSSLErrors();
plaintext_len += len;
plaintext[plaintext_len] = 0;
EVP_CIPHER_CTX_free(ctx);
std::string ret = (char*)plaintext;
delete [] plaintext;
return ret;
}
Any thoughts will be helpful.
Yes, your use of PKCS5_PBKDF2_HMAC is correct, and does not contribute at all to your error.
First of all, to use _set_key_length you need to split your Init so you can set the algorithm, then the key length, THEN the key. See e.g. EVP_CIPHER_CTX_set_key_length accepts bad sizes for blowfish and OPENSSL Blowfish CBC encryption differs from PHP to C++ (which, note, are for Blowfish, which is variable-key; AES in OpenSSL EVP is not).
But if you are using OpenSSL 1.0.1 or higher, which you probably are, EVP_MAX_KEY_LENGTH is 64, and 64 bytes is NOT a valid key size for AES ever much less for EVP_aes_256_cbc as you have here which is a fixed-size instantiation, and moreover 64 also is NOT the size of your actual key, so even if it did somehow accept your call it would use garbage data for the key and produce totally wrong and useless results.
Don't do that.

How can I add IV (initialization vector) to AES-256 ECB Encryption to create AES-256 CBC mode?

I have the following code working for AES-256 ECB encryption using a simple byte-oriented AES-256 library I found here.
Main:
#define DUMP(s, i, buf, sz) {printf(s); \
for (i = 0; i < (sz);i++) \
printf("%02x ", buf[i]); \
printf("\n");}
int main (int argc, char *argv[])
{
aes256_context ctx;
uint8_t key[32] = "39P8TXDMBCYF4C1NI1CDFJ1WL6P5TTKZ";
uint8_t buf[16] = "KUC7EWG6M2D1WW8F";
uint8_t i;
DUMP("txt: ", i, buf, sizeof(buf));
DUMP("key: ", i, key, sizeof(key));
printf("---\n");
aes256_init(&ctx, key);
aes256_encrypt_ecb(&ctx, buf);
DUMP("enc: ", i, buf, sizeof(buf));
aes256_init(&ctx, key);
aes256_decrypt_ecb(&ctx, buf);
DUMP("dec: ", i, buf, sizeof(buf));
aes256_done(&ctx);
return 0;
}
Encryption function:
void aes256_encrypt_ecb(aes256_context *ctx, uint8_t *buf)
{
uint8_t i, rcon;
aes_addRoundKey_cpy(buf, ctx->enckey, ctx->key);
for(i = 1, rcon = 1; i < 14; ++i)
{
aes_subBytes(buf);
aes_shiftRows(buf);
aes_mixColumns(buf);
if( i & 1 ) aes_addRoundKey( buf, &ctx->key[16]);
else aes_expandEncKey(ctx->key, &rcon), aes_addRoundKey(buf, ctx->key);
}
aes_subBytes(buf);
aes_shiftRows(buf);
aes_expandEncKey(ctx->key, &rcon);
aes_addRoundKey(buf, ctx->key);
} /* aes256_encrypt */
I want to add an IV to this program to create AES-256 CBC mode. From what I understand, IV implementation is as follows:
XOR the first block with the IV.
XOR all following blocks with the cipher text of the previous block.
My question is what does the logic look like? How do I implement that into my code?
The logic and explanations can be found in a few places. For example in: ECB vs CBC or Block cipher mode of operation.
CBC = Cipher Block Chaining is a way of connecting blocks together.
What is does is instead of just processing each block separately, every block will be XOR’ed with the encrypted previous block. This effectively means that every block depends on the output of the previous block.
Each block is XORed with the ciphertext of the previous block as it is explained by diagrams in the quoted articles.
In practice once one block is encrypted by ECB encription:
Cipher((state_t*)buf, ctx->RoundKey);
as in
void AES_ECB_encrypt(struct AES_ctx *ctx,const uint8_t* buf)
{
// The next function call encrypts the PlainText with the Key using AES algorithm.
Cipher((state_t*)buf, ctx->RoundKey);
}
CBC is achieved by XOR with IV on the block, ECB on the same block and moving along the blocks in the buffer.
Example of XOR with IV:
static void XorWithIv(uint8_t* buf, uint8_t* Iv)
{
uint8_t i;
for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size
{
buf[i] ^= Iv[i];
}
}
Example of CBC with the use of XOR with IV and ECB :
void AES_CBC_encrypt_buffer(struct AES_ctx *ctx,uint8_t* buf, uint32_t length)
{
uintptr_t i;
uint8_t *Iv = ctx->Iv;
for (i = 0; i < length; i += AES_BLOCKLEN)
{
XorWithIv(buf, Iv);
Cipher((state_t*)buf, ctx->RoundKey);
Iv = buf;
buf += AES_BLOCKLEN;
//printf("Step %d - %d", i/16, i);
}
/* store Iv in ctx for next call */
memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
}
The above implementation comes from tiny-AES and you may want to study it and adapt it to your needs. I hope it helps.

OpenSSL Variable Length Result for RSA Encryption [C programming]

I am trying to encrypt some text using OpenSSL's RSA encryption functions. My main issue is that the length of the encrypted RSA text varies between 0 and 256.
My RSA encryption function is:
/* Encrypt data using RSA */
char* rsa_encrypt(char* pub_key_filename, const unsigned char *data)
{
int padding = RSA_PKCS1_PADDING;
FILE *fp_pub;
fp_pub = fopen(pub_key_filename, "rb");
if (fp_pub == NULL)
{
printf("There was an error opening the public key file. Exiting!\n");
exit(EXIT_FAILURE);
}
RSA *pub_key = PEM_read_RSA_PUBKEY(fp_pub, NULL, NULL, NULL);
char *encrypted = malloc(2048);
int i;
for (i = 0; i < (2048); i++)
{
encrypted[i] = '\0';
}
int result = RSA_public_encrypt(strlen(data), data, encrypted, pub_key, padding);
if (result == -1)
{
printf("There was an error during RSA encryption.\n");
return "ERROR_RSA_ENCRYPTION";
}
fclose(fp_pub);
return encrypted;
}
The following code involves trying to encrypt some text:
const unsigned char *key = (unsigned char *)"abcdefghijklmnopqrstuvwxyzabcdef";
unsigned char *encrypted_aes_key = rsa_encrypt("public.pem", key);
I know that RSA with no padding is primitive RSA encryption and the resulting length is between 0 and n (RSA bit size) as seen here but my code is using RSA_PKCS1_PADDING so I am not sure why I am still getting variable length output.
The length of the encrypted data is returned in result from:
int result = RSA_public_encrypt(strlen(data), data, encrypted, pub_key, padding);
The encrypted data returned in encrypted is binary data. You can't do a strlen on it. The data is not 0 terminated and might contain some random 0 in it.

X509* certificate serialization and deserialization in C

I have a certificate in 509* format and I want to serialize it into a char buffer and then later desserialize it in other to recover the certificate 509* again.
I am doing it like this to serialize:
int size_cert = 0;
unsigned char* data;
BIO* bio = BIO_new(BIO_s_mem());
PEM_write_bio_X509(bio,certificate);
size_cert = BIO_get_mem_data(bio, &data);
BIO_free(bio);
where data should have the certificate data!
To reconstruct the X509* certificate back from the data buffer I am doing this:
BIO* bio;
X509* cert;
bio = BIO_new(BIO_s_mem());
BIO_puts(bio, data);
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
Where cert should get the certificate. This is not working properly, can some one give me a good example for this?
I have done it with the below code,
1 Load certificate to BIO using BIO_read_filename
2 convert it to x509 using PEM_read_bio_X509_AUX
3 convert it to unsigned char* using i2d_X509
4 reconstruct the x509 from unsigned char* using d2i_X509
int main()
{
X509 *x509,*x509ser;
BIO *certBio = BIO_new(BIO_s_file());
char * path = "E:\\share\TempCert.pem"; // certificate path
int len;
unsigned char *buf;
buf = NULL;
BIO_read_filename(certBio, path); // reading certificate to bio
x509 = PEM_read_bio_X509_AUX(certBio, NULL, 0, NULL); //converting to x509
len = i2d_X509(x509, &buf); // converting to unsigned char*
x509ser = d2i_X509(NULL, &buf, len); // converting back to x509 from unsigned char*
BIO_free_all(certBio);
return 0;
}

Openssl base64 decoded string does not always decrypt

I am trying to encrypt some plain text with a public key using RSA_public_encrypt(), this data would then be sent to a remote server for validation. I believe I have the encryption/decryption working, since the output of RSA_public_encrypt can be passed to RSA_private_decrypt and it works. The problem I am having now is I need to base64 encode the data in order to send it over HTTP.
As a test (before sending it to the server) I am encoding to base64 the output of RSA_public_encrypt(), then decoding it and passing it back into RSA_private_decrypt(). This appears to work some of the time, and fails with an error like this:
error:0407A079:rsa routines:RSA_padding_check_PKCS1_OAEP:oaep decoding error
When I use memcmp to compare the original data (pre-base64) to the output of my base64 decode function I receive a -1 despite the contents appearing to match (in Visual Studio by viewing the contents of the memory as hex). I have also checked the base64 encoded version with various online tools and they appear to decode to the expected value.
I have double checked that the input/output from the base64/unbase64 functions are null terminated which appears to make little difference.
I've been going round in circles with this problem for a couple of days but I believe it must be something with the base64 encode/decode process because when that is not involved everything works. If anyone has any advice on how this could be happening it would be appreciated.
Openssl version: 1.0.1c
Platform: Windows/MSVC
Base64 Code:
char *base64(const unsigned char *input, int length)
{
BIO *bmem, *b64;
BUF_MEM *bptr;
char *buff = NULL;
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_write(b64, input, length);
BIO_flush(b64);
BIO_get_mem_ptr(b64, &bptr);
buff = (char *)malloc(bptr->length+1);
memcpy(buff, bptr->data, bptr->length);
buff[bptr->length] = '\0';
BIO_free_all(b64);
return buff;
}
Unbase64 Code:
char *unbase64(unsigned char *input, int length)
{
BIO *b64, *bmem;
char *buffer = (char *)malloc(length+1);
memset(buffer, 0, length+1);
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new_mem_buf(input, length);
bmem = BIO_push(b64, bmem);
BIO_read(bmem, buffer, length);
buffer[length] = '\0';
BIO_free_all(bmem);
return buffer;
}
Encrypted "hello world" example:
š:Œ¼JŒ"ÌïëŸÔè#¢Oo‚À– œê\çrú¿±a/8ƒòÌ¢Q\T¹]nío
Base64 version (using the above code):
G5qdOgWMvEqMIswZ7+uf1OgPI6JPb4LAlgmc6lzncvq/sWEvOIPyzByiUVwMjYFUuV0Vbu1v
Thanks for any help!
You are passing in the correct size in the unbase64 function? It should be the size of the base64 buffer returned, not the size of the destination buffer i.e. using an example main function:
int main(void)
{
unsigned char bufron[2000];
int i;
char *chab;
unsigned char *chac;
for (i = 0; i < 2000; i++) {
bufron[i] = i % 255;
}
chab = base64(bufron, 2000);
printf("%s\n", chab);
chac = unbase64(chab, strlen(chab));
for (i = 0; i < 2000; i++) {
if (bufron[i] != chac[i]) {
printf("Failed at %d\n", i);
return (1);
}
}
}

Resources