OpenSSL EVP API: How to decrypt the encrypted file using a symmetric key file

我们两清 提交于 2019-12-20 04:52:35

问题


Hi I'm working on C on Linux.

I have a query related to symmetric key decryption.

I have generated an symmetric key using the below command.

openssl rand base64 512 > sym.key

Using this key (sym.key) I have encrypted a file with below command.

openssl enc -aes-256-cbc -in temp.txt -out temp.enc -kfile sym.key

It has generated an encrypted file temp.enc.

Now, I have to use the same key (sym.key) with EVP Decrypt API's and have to decrypt this encrypted file.

Could any one suggest me a better approach for this.

Here is the code

unsigned char* decode (unsigned char *key, int len)
{

   BIO *b64, *bmem;

   char *buffer = (char *)malloc(len);
   memset (buffer, 0, len);

   b64 = BIO_new(BIO_f_base64());
   bmem = BIO_new_mem_buf(key, len);
   bmem = BIO_push(b64, bmem);

   BIO_read(bmem, buffer, len);

   BIO_free_all(bmem);

    return buffer;
}

void decrypt(char *file_name, char *key_file)
{   

    unsigned char *inbuff = NULL, *outbuff = NULL, *ckey = NULL;
    char *buff = NULL;
    unsigned int flen = 0, outlen2 = 0, outlen1 = 0, klen = 0;

    FILE *fp = NULL, *kfp = NULL;
        unsigned char iv[16] = {};

    fp = fopen (file_name, "r");

    if (NULL == fp)
    {
        printf ("Cannot open file : %s\n", file_name);
        exit(1);
    }

    fseek (fp, 0, SEEK_END);
    flen = ftell (fp);
    rewind (fp);

    kfp = fopen (key_file, "r");

    if (NULL == kfp)
    {
        printf ("Cannot open file : %s\n", key_file);
        exit(1);
    }

    fseek (kfp, 0, SEEK_END);
    klen = ftell (kfp);
    rewind (kfp);

    inbuff = (unsigned char *)malloc(flen);
    outbuff = (unsigned char *)malloc(flen * 2);
    ckey = (unsigned char *)malloc(klen);
    buff = (char *)malloc(klen);

    fread (inbuff, sizeof(char), flen, fp);
    fread (buff, sizeof(char), klen, kfp);

    ckey = decode(buff, klen);

    EVP_CIPHER_CTX ctx;

#if 1
    if (! EVP_DecryptInit (&ctx, EVP_aes_256_cbc(), ckey, iv))
    {
        ERR_print_errors_fp(stderr);
        EVP_CIPHER_CTX_cleanup(&ctx);
        printf ("Error in Init\n");
        exit(1);
    }

    if (! EVP_DecryptUpdate (&ctx, outbuff, &outlen1, inbuff, flen))
    {
        ERR_print_errors_fp(stderr);
        EVP_CIPHER_CTX_cleanup(&ctx);
        printf ("Error in Init\n");
        exit(1);
    }

    if (! EVP_DecryptFinal (&ctx, outbuff + outlen1, &outlen2))
    {
        ERR_print_errors_fp(stderr);
        EVP_CIPHER_CTX_cleanup(&ctx);
        printf ("Error in Init\n");
        exit(1);
    }

    EVP_CIPHER_CTX_cleanup(&ctx);
#endif

    free (inbuff);
    free (outbuff);
    free (ckey);
    fclose (fp);
    fclose (kfp);

    printf ("Outbuff:\n %s\n", outbuff);

}

Thank you.


回答1:


Using this key (sym.key) I have encrypted a file with below command.
openssl enc -aes-256-cbc -in temp.txt -out temp.enc -kfile sym.key

Not quite. openssl enc -kfile reads the first line, and only the first line, of the file and uses it as the password, which is not the same thing as the key. enc has three options to enter a password, after which it runs the password with random salt through a key derivation process to produce the actual key and IV (for ciphers that use an IV, and AES-CBC does). It then writes a header containing the salt, followed by the ciphertext, to the output file. This is called Password Based Encryption (PBE) and sometimes a Password Based Key Derivation Function (PBKDF).

Depending on what you actually want to do there are two approaches.

To decrypt the file you have

Read the first line from sym.key (excluding the line terminator) as characters, NOT base64, and feed it with the salt from temp.enc through EVP_BytesToKey something like this:

FILE * pwfile = fopen (key_file, "r"); 
if(!pwfile) error_handling 
char pwline [70]; 
fgets (pwline, sizeof pwline, pwfile);
int pwlen = strlen (pwline); 
if(pwlen==0 || pwline[pwlen-1]!='\n') error_handling
pwline[--pwlen] = '\0';

// Open file_name and read into inbuff for flen as you have now 
// Optionally confirm the first 8 bytes are "Salted__"
// If on Windows must fopen with mode "rb" to get all bytes correctly;
// on Unix it is clearer to specify this but not actually needed
// because on Unix binary files and text files are treated the same

unsigned char key [256/8]; // AES-256 key is 32 bytes
unsigned char iv [128/8]; // AES IV is 16 bytes (regardless of key)
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_md5(), inbuff+8, /* the salt! */
    (unsigned char*)pwline, pwlen, 1, key, iv);

// Now continue as you have with EVP_Encrypt{Init,Update,Final}
// using key,iv except use buffer inbuff+16 for length flen-16 .
// (And do something with the output, which you don't now!)

To create the file you apparently wanted

To use enc to create a file encrypted with a direct key, you must pass it with the -K option (uppercase K) on the commandline in hex. However, it's a nuisance to handle hex in your C program, so I would handle it on the encrypt side something like:

openssl rand 32 >sym.key # AES-256 key must be exactly 32 bytes, not more
openssl enc -aes-256-cbc -in temp.txt -out temp.enc \
  -K $(od -An -tx1 sym.key | sed 's/ //g') -iv 00000000000000000000000000000000

then in your C program read sym.key (as binary on Windows at least) and use it as-is, with an IV of 16 0's as you have now.

Alternatively, have rand write hex and use that as-is

openssl rand -hex 32 >sym.key
openssl enc -aes-256-cbc -in temp.txt -out temp.enc -K $(cat sym.key) -iv (same)

then in your C program read sym.key (as a text line) and convert it from 64 chars of hex to unsigned char key [32].

Other points

If you did need to decode a base64 file, you don't need to read it into memory first then push a b64BIO on a memBIO; you can push a b64BIO on a fileBIO that reads the file.

You don't need to allocate flen*2 for outbuff. Decrypted plaintext will never be longer than the ciphertext. (Which for the salted-PBE form is actually flen-16 as above.) It is encryption that can expand the data, and then only one block (for AES, 16 bytes).

It's good practice to check malloc didn't return NULL before using it. This rarely happens on modern systems, but it can occur if even simple code like this is called from a larger program and some other part of the program has a bug that exhausts memory, or perhaps a vulnerability exploited by a denial-of-service attack.

If you want to support large files that don't fit in the memory available, or might not, iteratively read chunks, feed each into DecryptUpdate, write out the results (which will lag about one block), and at EOF call DecryptFinal and output any last partial block.



来源:https://stackoverflow.com/questions/34357543/openssl-evp-api-how-to-decrypt-the-encrypted-file-using-a-symmetric-key-file

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!