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;
}
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 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.
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 writing a pam module, that does authentication with one time passwords.
I started writing it with sha512 as algorithm for the hmac and ran into a problem. I also did it with sha1, which works.
The code is at: https://github.com/Ongy/pam_totp
I tested the hmac with sha512, as far as I can tell that is correct, so the error should be in the get_truncate function, which does work with sha1, but not with sha512.
static int get_truncate(const uint8_t * hash, size_t len, char * buffer,
size_t maxlen)
{
uint32_t value;
uint8_t offset;
offset = hash[len-1] & 0x0F;
value = *((uint32_t *) (hash+offset));
value = be32toh(value) & 0x7FFFFFFF;
value %= 100000000;
return snprintf(buffer, maxlen, "%08d", value);
}
which is called in
static int get_totp_sha512(const uint8_t * hashdata, size_t len, uint64_t time,
char * dst, size_t maxlen)
{
uint8_t buffer[64];
uint64_t counter = htobe64(time);
memset(buffer, 0, sizeof(buffer));
calculate_hmac_sha512(hashdata, len, (uint8_t *) &counter,
sizeof(counter), buffer, sizeof(buffer));
return get_truncate(buffer, sizeof(buffer), dst, maxlen);
}
Most of the relevant code should be in src/main.c.
The rfcs relevent are: 6238 and 4226. (The second one describes the general algorithm, the first one also allows other hashes than sha1)
Ok so the thing I had to fix was my testcase, I used the test vectors given in https://www.rfc-editor.org/rfc/rfc6238#appendix-B. What I had to change:
The key is not actually "12345678901234567890" for sha512 but "1234567890123456789012345678901234567890123456789012345678901234".
I am currently writing a linux application in C that reads from a configuration file. This configuration file contains some data that I would like to encrypt so it is not plain text. I have spent hours researching this and have not found a viable solution. Since the application will need to read from the configuration I will need to be able to encrypt it and decrypt it on the fly. So far from research I really like openSSL crypto library. I know from the command line you can do:
openssl enc -aes-256-cbc -salt -in file.txt -out file.enc
If anyone can provide an example of how I can do this in C, it would be most appreciated.
You should have a look at the O'Reilly-Book. There are a couple of examples on how to encrypt things. Unfortunately the most are for Network-Encryption.
I found an example in the book, but didnt test it:
#include <openssl/evp.h>
int main(int argc, char *argv[])
{
EVP_CIPHER_CTX ctx;
char key[EVP_MAX_KEY_LENGTH];
char iv[EVP_MAX_IV_LENGTH];
char *ct, *out;
char final[EVP_MAX_BLOCK_LENGTH];
char str[] = "123456789abcdef";
int i;
if (!seed_prng())
{
printf("Fatal Error! Unable to seed the PRNG!\n");
abort();
}
select_random_key(key, EVP_MAX_KEY_LENGTH);
select_random_iv(iv, EVP_MAX_IV_LENGTH);
EVP_EncryptInit(&ctx, EVP_bf_cbc(), key, iv);
ct = encrypt_example(&ctx, str, strlen(str), &i);
printf("Ciphertext is %d bytes.\n", i);
EVP_DecryptInit(&ctx, EVP_bf_cbc(), key, iv);
out = decrypt_example(&ctx, ct, 8);
printf("Decrypted: >>%s<<\n", out);
out = decrypt_example(&ctx, ct + 8, 8);
printf("Decrypted: >>%s<<\n", out);
if (!EVP_DecryptFinal(&ctx, final, &i))
{
printf("Padding incorrect.\n");
abort();
}
final[i] = 0;
printf("Decrypted: >>%s<<\n", final);
}
char *encrypt_example(EVP_CIPHER_CTX *ctx, char *data, int inl, int *rb)
{
char *ret;
int i, tmp, ol;
ol = 0;
ret = (char *)malloc(inl + EVP_CIPHER_CTX_block_size(ctx));
for (i = 0; i < inl / 100; i++)
{
EVP_EncryptUpdate(ctx, &ret[ol], &tmp, &data[ol], 100);
ol += tmp;
}
if (inl % 100)
{
EVP_EncryptUpdate(ctx, &ret[ol], &tmp, &data[ol], inl%100);
ol += tmp;
}
EVP_EncryptFinal(ctx, &ret[ol], &tmp);
*rb = ol + tmp;
return ret;
}
char *decrypt_example(EVP_CIPHER_CTX *ctx, char *ct, int inl)
{
/* We're going to null-terminate the plaintext under the assumption it's
* non-null terminated ASCII text. The null can be ignored otherwise.
*/
char *pt = (char *)malloc(inl + EVP_CIPHER_CTX_block_size(ctx) + 1);
int ol;
EVP_DecryptUpdate(ctx, pt, &ol, ct, inl);
if (!ol) /* there's no block to decrypt */
{
free(pt);
return NULL;
}
pt[ol] = 0;
return pt;
}
Hope this will help you.
Check this answer and check this article.
You can replace EVP_bf_cbc() with EVP_aes_128_cbc() EVP_aes_192_cbc() or EVP_aes_256_cbc() depending on what you need.
You need the SSL development packages. (libssl-dev in Ubuntu). Depending on how you implement it, you will also need libcrypto-dev.
I'm not a huge O'Reilly fan, but I found this book to be very helpful when starting this type of thing for the first time.
Why not do your own? Read enough bytes from dev/random (your key) and XOR the configuration file with it. To decrypt, XOR again with the same key. This is simple,fast,and secure. No complicated libraries needed.