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.
Related
I'm trying to write AES encryption/decryption program in C using openssl. However, when I tried to decrypt the message, I got error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length.
void aes_encrypt(unsigned char* in, int inl, unsigned char *out, int* len, unsigned char * key){
unsigned char iv[16] = "encryptionIntVec";
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key, iv);
*len = 0;
int outl = 0;
EVP_EncryptUpdate(&ctx, out+*len, &outl, in+*len, inl);
*len+=outl;
int test = inl>>4;
if(inl != test<<4){
EVP_EncryptFinal_ex(&ctx,out+*len,&outl);
*len+=outl;
}
EVP_CIPHER_CTX_cleanup(&ctx);
}
void aes_decrypt(unsigned char* in, int inl, unsigned char *out, unsigned char *key){
unsigned char iv[16] = "encryptionIntVec";
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
int result;
result = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key, iv);
if(result > 0) {
printf("passed\n");
} else {
printf("failed\n");
}
int len = 0;
int outl = 0;
result = EVP_DecryptUpdate(&ctx, out+len, &outl, in+len, inl);
if(result > 0) {
printf("passed\n");
} else {
printf("failed\n");
}
len += outl;
result = EVP_DecryptFinal_ex(&ctx, out+len, &outl);
if(result > 0) {
printf("passed\n");
} else {
printf("failed\n");
ERR_print_errors_fp(stdout);
}
len+=outl;
out[len]=0;
EVP_CIPHER_CTX_cleanup(&ctx);
}
int main()
{
unsigned char content[400];
unsigned char key[] = "0123456789abcdef0123456789abcdef";
/******************Block 1*****************************/
unsigned char en[400],de[400],base64[400], base64_out[400];
int len;
memset(content, 0,400);
memset(en, 0, 400);
memset(de, 0, 400);
memset(base64, 0,400);
memset(base64_out, 0, 400);
strcpy((char *)content, "loc: 123.2132412, -39.123142");
printf("%d %s\n", strlen((const char*)content), content);
//encrypt content
aes_encrypt(content,strlen((const char*)content), en, &len, (unsigned char*)key);
//base64 encode ciphertext
int encode_str_size = EVP_EncodeBlock(base64, en, len);
printf("%d %s\n", encode_str_size, base64);
//base64 decode
int length = EVP_DecodeBlock(base64_out, base64, strlen((const char*)base64));
while(base64[--encode_str_size] == '=') length--;
//decrypt
aes_decrypt(base64_out, length, de, (unsigned char*)key);
printf("%d %s\n", strlen((const char*)de), de);
/***********************Block 2*******************************/
unsigned char msg_out[400];
unsigned char msg[400] = "6hKe8RGg+4p1N1R6Y9aaTovxLtuH115JoWUO8plrAJE=";
unsigned char result[400];
int l = EVP_DecodeBlock(msg_out, msg, strlen((const char*)msg));
if(strcmp((const char*)msg, (const char*)base64)==0) {
printf("match\n");
}
if(strcmp((const char*)en, (const char*)msg_out)==0) {
printf("match\n");
}
while(msg[--encode_str_size] == '=') l--;
aes_decrypt(msg_out, l, result, (unsigned char*)key);
printf("%d %s\n", strlen((const char*)result), result);
return 0;
}
In block 1 of main function, I encrypted, base64 encoded, base64 decoded, decrypted, then got the exactly the same String as it was, and produced NO error.
However, in the block 2, I directly used the base64 encoded string which was produced from block 1, decoded and decypted, but an error occurred at result = EVP_DecryptFinal_ex(&ctx, out+len, &outl); that was error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length.
If I printed the decrypted string anyways, it is loc: 123.2132412, -39.123142^D^D^D^D, there are four '^D' appended at the end. I compared strings, both match was printed, which means the ciphertexts that was decoded from base64 with the one(en) in block 1 were the same. (NOTE: same key, same IV)
Any ideas why it failed?
I am using version OpenSSL 3.0.0-dev and therefore had to modify the code for the reasons described in David C. Rankin's comment. The problem occurs even after these changes.
Debugging reveals that the length of the Base64 decoded data in block 2 is determined incorrectly with l = 0x21 = 33. The correct value is l = 0x20 = 32.
This is because in block 2, when determining the length of the Base64 decoded data without the 0x00 padding bytes (added by EVP_DecodeBlock()) with
while (msg[--encode_str_size] == '=') l--;
the value for encode_str_size already changed in block 1 is used. The fix is to apply the current value for the length of the Base64 encoded data, e.g.
encode_str_size = strlen((const char*)msg);
Then decryption for block 2 works on my machine.
Note that EVP_DecodeBlock() always returns the Base64 decoded data with a length equal to an integer multiple of 3, padded with 0x00 values if necessary.
The actual length of the data can therefore also be determined with msg_out and l alone. l specifies the length including the 0x00 padding bytes, so that only the number of the 0x00 padding bytes has to be subtracted from l.
Thus, msg and encode_str_size are actually not needed (although both can be used, since the number of = padding bytes is equal to the number of 0x00 padding bytes).
As already mentioned in the comment by Joël Hecht, the EVP_EncryptFinal_ex() call is necessary, which among other things performs the padding (PKCS7).
In the currently posted code, this only occurs if the length of the plaintext does not equal an integer multiple of 16 bytes (the AES block size) which is satisfied for the test data.
However, for a plaintext whose length is a multiple of 16 bytes, EVP_EncryptFinal_ex() is not called, so padding is not performed, resulting in a decryption failure in EVP_DecryptFinal_ex().
Apart from the Base64 encoding, the OpenSSL documentation here provides a complete example for EVP_aes_256_cbc(), which could be used for comparison.
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);
I have made an implementation using the OpenSSL library to encrypt a password.
I am able to successfully encrypt and decrypt the password using the library in the code.
However, if I try to decrypt the cipher generated by the Library on a linux system on the terminal the decryption fails.
Encryption:
openssl enc -aes-256-cbc -base64 -salt -k <passphrase> -in plain.txt -out
encrypt.txt
Decryption:
openssl enc -aes-256-cbc -base64 -salt -d -k <passphrase> -in encrypt.txt -out plain.txt
Please help.
I have removed salts so as to make the process simpler.
I have generated sample base64 ciphers on the terminal and tried decrypting with Library, this fails.
I have tried generating sample base64 ciphers with Library and tried decrypting it in the terminal, this fails, too!
size_t init_key_iv(const std::string& pass, const unsigned char* salt, unsigned char* key, unsigned char* iv ) {
size_t derived_key_size = 0;
const unsigned char * pass_key = reinterpret_cast<const unsigned char*>( pass.c_str() );
const size_t pass_key_len = pass.size();
if(salt && key && iv && pass_key && pass_key_len > 0) {
memset(key, 0, sizeof(key));
memset( iv, 0, sizeof(iv));
derived_key_size = EVP_BytesToKey(cipher_type, msg_digest_type, salt, pass_key, pass_key_len, 5, key, iv);
}
return derived_key_size;
}
void encrypt(const unsigned char* msg, unsigned char** encrypted_message, const size_t msg_len, const unsigned char *key, unsigned char *iv) {
AES_KEY enc_key;
AES_set_encrypt_key(key, 256, &enc_key);
AES_cbc_encrypt(msg, *encrypted_message, msg_len, &enc_key, iv, AES_ENCRYPT);
}
void decrypt(const unsigned char* cipher, unsigned char** decrypted_msg, const size_t cipher_len, const unsigned char *key, unsigned char *iv ) {
AES_KEY enc_key;
AES_set_decrypt_key(key, 256, &enc_key);
AES_cbc_encrypt(cipher, *decrypted_msg, cipher_len, &enc_key, iv, AES_DECRYPT);
}
int decode(const char* b64_msg, unsigned char** decode_msg, const size_t decode_msg_len) {
size_t bytes_decoded = 0;
bytes_decoded = EVP_DecodeBlock(*decode_msg, (unsigned char *)b64_msg, strlen(b64_msg));
return bytes_decoded;
}
int encode(const unsigned char* msg, const size_t msg_len, char** b64_msg) {
size_t bytes_encoded = 0;
if(msg && msg_len > 0 && b64_msg) {
bytes_encoded = EVP_EncodeBlock((unsigned char *) *b64_msg, msg, msg_len);
}
return bytes_encoded;
}
const int derived_key_size = init_key_iv(password, salt, key, iv_enc);
encrypt((unsigned char *)msg, &encrypted_message, strlen(msg), key, iv_enc);
const size_t bytes_encoded( encode((const unsigned char*)encrypted_message, strlen(reinterpret_cast<char*>(encrypted_message)), &base64_enc_str) );
const size_t bytes_decoded( CBase64::decode(cipher_base64, &cipher, cipher_len) );
decrypt(cipher, &decrypted_message, cipher_len, key, iv_dec);
Expectation is; the base64 ciphers generated by the Library should be decrypted in the openssl terminal and vice versa.
Your code is not complete, and there are several things not shown which could be wrong, but the things shown that are definitely or probably wrong are:
you call EVP_BytesToKey with count=5, when commandline enc uses count=1. In addition you don't show what cipher_type and msg_digest_type are and they could be wrong; in particular, the default digest used for BytesToKey in commandline enc varies depending on the version of OpenSSL, and you didn't say what version(s) you are or will or might be using. Although specifying -md $hash overrides that default, which is a more robust and clearer solution.
you don't show where the plaintext comes from, and in particular whether and how you've padded it. Commandline enc by default uses PKCS5/7 padding, and has an option to use no padding, but in that case the plaintext length must always be an exact multiple of 16 -- have you guaranteed that?
you use strlen(ciphertext) as the length of the (raw = before base64) ciphertext; this is usually wrong. The ciphertext is effectively random bits, and can easily contain a byte with value 0, giving a strlen() that is too small, but if that doesn't happen it will not necessarily be followed or terminated by a 0 byte, giving a strlen() that is too large.
you do not include in the base64 encoding the file header (aka 'magic') required by commandline enc when using salt. The code shown does not add the line breaks required by commandline enc file format, but that could be done elsewhere, and only matters if the values you encrypt (and want to decrypt) are or (ever) can be more than 31 bytes.
Also, you call memset (key, 0, sizeof(key)) and the same for iv when they are pointers; this clears only the size of the pointer, 4 or 8 bytes on modern systems, not the object pointed to. But since these objects are promptly overwritten by BytesToKey, this mistake doesn't matter.
Anyway, here is a minimal-ish code that is complete and produces an output that is decryptable by commandline enc -aes-256-cbc -d -a -k $password with -md sha256 for versions below 1.1.0 where that is not the default. For convenience I have limited to 80 bytes input, but it should be obvious how to increase that if desired.
/* SO56447374 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/aes.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
void err (const char *label){
fprintf (stderr, "%s:\n", label); ERR_print_errors_fp (stderr); exit (1);
}
int main (int argc, char**argv)
{
ERR_load_crypto_strings();
//OPENSSL_add_all_algorithms_noconf();
const char * pass = argv[1];
unsigned char salt [8], key [32], iv [16],
plain [80], buffer [16+96], *cipher = buffer+16;
int inlen = fread (plain, 1, 80-1, stdin), pad = 16-inlen%16U;
AES_KEY aeskey;
RAND_bytes (salt, 8);
if( !EVP_BytesToKey (EVP_aes_256_cbc(), EVP_sha256(), salt,
(unsigned char*)pass, strlen(pass), 1, key, iv) ) err("BytesToKey");
AES_set_encrypt_key (key, 256, &aeskey);
memset (plain+inlen, pad, pad); // PKCS5/7
AES_cbc_encrypt (plain, cipher, inlen+pad, &aeskey, iv, AES_ENCRYPT);
memcpy (buffer+0, "Salted__", 8); memcpy (buffer+8, salt, 8);
BIO *bio1 = BIO_new (BIO_f_base64()); // does b64 with linebreaks (by default)
BIO_push (bio1, BIO_new_fp (stdout, BIO_NOCLOSE));
BIO_write (bio1, buffer, 16+inlen+pad);
BIO_flush (bio1);
BIO_free_all (bio1);
return 0;
}
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.
I am using the EVP library found here: https://www.openssl.org/docs/manmaster/crypto/EVP_EncryptInit.html
Here are my two encryption and decryption functions:
I am trying to encrypt a string using AES 128 CBC.
The string is usually of the format word1 word2 word3
char* encrypt(char *s, char *key) {
unsigned char iv[16] = {[0 ... 15 ] = 0};
unsigned char outbuf[1024] = {[0 ... 1023] = 0};
int outlen1, outlen2;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);
if (EVP_EncryptUpdate(&ctx, outbuf, &outlen1, s, strlen(s)) == 1) {
if (EVP_EncryptFinal_ex(&ctx, outbuf + outlen1, &outlen2) == 1) {
EVP_CIPHER_CTX_cleanup(&ctx);
return strdup(outbuf);
}
}
EVP_CIPHER_CTX_cleanup(&ctx);
return NULL;
}
char* decrypt(char *s, char *key) {
unsigned char iv[16] = {[0 ... 15 ] = 0};
unsigned char outbuf[1024] = {[0 ... 1023] = 0};
int outlen1, outlen2;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);
if (EVP_DecryptUpdate(&ctx, outbuf, &outlen1, s, strlen(s)) == 1) {
printf("After decrypt update\n");
if (EVP_DecryptFinal_ex(&ctx, outbuf + outlen1, &outlen2) == 1) {
printf("After decrypt final\n");
EVP_CIPHER_CTX_cleanup(&ctx);
return strdup(outbuf);
}
}
EVP_CIPHER_CTX_cleanup(&ctx);
return NULL;
}
The problem is the decryption final function works on some strings but not on others.
If the string before it is encrypted is something like cat dog cow, the decryption works.
But if it is like bat dog cow, the decryption fails in particular at the EVP_DecryptFinal_ex() function.
For some strings, the decryption always fails at the EVP_DecryptFinal_ex() function. It does not return 1.
Any idea what the problem could be? Padding maybe? I just can't seem to figure it out.
You probably miss that the encrypted string may contain zero-bytes, so the strlen(s) in DecryptUpdate has a too low value. You have to remember from encrypt how long the encrypted data is and use that value for decrypting.