使用openssl evp api加密文件的问题(aes256cbc)

时间:2012-04-30 22:56:27

标签: c linux encryption openssl aes

在这种情况下,我试图读取一个文件(.txt),并使用openssl的EVP api使用AES256CBC对其进行加密/解密。(read(plain.txt) - > create(encrypt.txt) - >创建(decrypt.txt))

# include <stdio.h>
# include <stdlib.h>
# include <openssl/evp.h>
# include <openssl/aes.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
# include <string.h>
# include <assert.h>
# include <error.h>
# include "debug.h"
# define SIZE 32

char buf[SIZE];

int aes_init(unsigned char* pwd, unsigned int pwd_len, unsigned char * salt, EVP_CIPHER_CTX *e_ctx, EVP_CIPHER_CTX *d_ctx)
{
    int i, rounds =5;                   /* rounds */
    unsigned char key[32], iv[32];

    i = EVP_BytesToKey(EVP_aes_256_cbc(),EVP_sha1(),salt,pwd,pwd_len,rounds,key,iv);
    if(i != 32)
    {
      printf("\n Error,Incorrect key size generated:%d:\n",i);
      return -1;
    }

    EVP_CIPHER_CTX_init(e_ctx);
    EVP_EncryptInit_ex(e_ctx, EVP_aes_256_cbc(), NULL, key, iv);
    EVP_CIPHER_CTX_init(d_ctx);
    EVP_DecryptInit_ex(d_ctx, EVP_aes_256_cbc(), NULL, key, iv);

    return 0;
}

unsigned char* aes_encrypt(EVP_CIPHER_CTX *e,unsigned char * plain_text, unsigned int * len )    /* this function encryptes the  file:fd is passed as parameter */
{
    int ci_len = (*len) + AES_BLOCK_SIZE;
    int flen =0;
    unsigned char * cipher_text = malloc(ci_len);

    EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL);              /* allows reusing of e for multiple cipher cycles */
    EVP_EncryptUpdate(e, cipher_text, &ci_len, plain_text, *len);       /* Update cipher text */
    EVP_EncryptFinal_ex(e, cipher_text+ci_len, &flen);          /* updates the remaining bytes */
    *len = ci_len + flen;

    return cipher_text; 
}

unsigned char* aes_decrypt(EVP_CIPHER_CTX *e, unsigned char * c_text, unsigned int * len)
{
    int pi_len = (*len);
    int flen = 0;
    unsigned char * plain_text = malloc(pi_len);

    EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
    EVP_DecryptUpdate(e, plain_text, &pi_len, c_text, *len);
    EVP_DecryptFinal_ex(e, plain_text+pi_len, &flen);

    (*len) = pi_len + flen;
    return plain_text;
}

int main(int argc,char **argv)
{
    if(argc != 2)
    {
      perror("\n Error:\nCorrect Usage: Enter Password to be used");
      exit(-1);
    }

    EVP_CIPHER_CTX en,de;                   /* The EVP structure which keeps track of all crypt operations see evp.h for details */
    int in, out, fd, dec,i =0;                  /* fd for input and output files and random dev*/
    unsigned int pwd_len = strlen((const char *)argv[1]);           /* Length of the pwd supplied by the user */
    unsigned char *pwd =(unsigned char*) argv[1];           /* Pointer to the pwd supplied by the user */
    unsigned int rd= 0;
    unsigned char salt[8];
    unsigned char * encry = NULL, *decry = NULL;
    i =0;
    if((in = open("plain.txt",O_RDONLY)) == -1)          /* Opening a plain text file for encryption */
    {
      perror("\n Error,Opening file for reading::");
      exit(-1);
    }

    if((fd = open("/dev/random", O_RDONLY)) == -1)
    {
      perror("\n Error,Opening /dev/random::");
      exit(-1);
    }
    else
    {
      if(read(fd,salt,8) == -1)
      {
        perror("\n Error,reading from /dev/random::");
        exit(-1);
      }
    }

    if(aes_init(pwd,pwd_len,(unsigned char*) salt,&en,&de))     /* Generating Key and IV and initializing the EVP struct */
    {
      perror("\n Error, Cant initialize key and IV:");
      return -1;
    }

    if((out = open("encrypt.txt",O_RDWR|O_CREAT,0400 | 0200)) == -1)
    {
      dbug_p("ENC%d",out);
      perror("\n Error,Opening the file to be written::");
      exit(-1);
    }

    rd =0;
    while((rd = read(in,buf,SIZE)) >0)
    {
      dbug_p("\nREAD::%s::%d*\n",buf,rd);
      encry =   aes_encrypt(&en,(unsigned char*) buf, &rd);
      if((write(out,encry,rd)) != rd)
      {
        perror("\n Error,Required encrypted bytes not written::");
        exit(-1);
      }     
      free(encry);
    }

    rd =0;
    if((dec = open("dec22.txt",O_RDWR|O_CREAT,0400 | 0200)) == -1)
    {   
      dbug_p("dec%d",dec);
      perror("\n Error,Opening the decrypting o/p file::");
      exit(-1);
    }

    if((lseek(out,0,SEEK_SET)) != 0) perror("\n Error:setting lseek::");
    for(i=0;i<SIZE;i++) buf[i] =0;
    while((rd = read(out,dbuf,SIZE)) >0)
    {
      decry = aes_decrypt(&de,(unsigned char*) dbuf, &rd);
      if((write(dec,decry,rd)) != rd)
      {
        perror("\n Error,Required decrypted bytes not written::");
        exit(-1);
      }
      free(decry);
    }

    close(in);
    close(fd);
    EVP_CIPHER_CTX_cleanup(&en);
    EVP_CIPHER_CTX_cleanup(&de);
    return 0;
}

我的问题是,当我解密加密文件时,我得到一个未正确解密的文件(例如正确的字符串垃圾正确的字符串垃圾 ...)< / p>

abhi@ubuntu:~/mpro/EFF$ cat plain.txt 
Today is tuesday
tomorrow is wednesday
then thursday and friday and saturday
finally sunday

解密文件

cat dec22.txt 
Today is tuesdayw)Q������O-%�A�8���R��.�O���and saturday
finally sunday

这可能是什么原因。它是否正在阅读其他内容,或者我在某处犯了任何愚蠢的错误。

编辑 :如果我只加密一个数组(尝试使用36char long),它会正确加密和解​​密,而不会打印任何垃圾。

我想我缺少(不处理)一些* nix文件结构细节。?? 或者有没有更好的方法对文件进行加密。?

非常感谢

2 个答案:

答案 0 :(得分:4)

我认为您的分析是错误的。这个循环有问题:

while((rd = read(in,buf,SIZE)) >0)
{
    dbug_p("\nREAD::%s::\n",buf);
    encry = aes_encrypt(&en,(unsigned char*) buf, &rd);
    dbug_p("\n EN::%s::\n",encry);
    decry = aes_decrypt(&de,(unsigned char*) encry,&rd);
    dbug_p("\n DE::%s::\n",decry);

            free(encry);
        free(decry);
}

首先,因为您使用期望零终止符的%s进行打印。但是,加密/解密数据不是零终止。相反,您应该使用rd之类的循环打印for (i = 0; i < rd; i++) printf("%02x ");字符 - 这就是您对问题的分析可能存在缺陷的原因。

其次,我假设在您真正的问题中,您一次只读取SIZE个字节并分别将它们发送到aes_decrypt()。这将失败,因为EVP_DecryptFinal_ex()被过早调用(在读取所有加密块之前)。你有两个选择。您可以在每次循环迭代中通过EVP_DecryptUpdate()发送读取字节,并在完成循环后调用EVP_DecryptFinal()(并在循环之前相应地调用init),或者首先将整个文件读入缓冲区,然后一次性通过aes_decrypt()发送。

或者换句话说,您需要稍后将aes_encrypt()生成的整个数据块发送到aes_decrypt()。您不能以不同的块发送它们,除非您将功能拆分并在单独的块上使用EVP“更新”功能。

答案 1 :(得分:3)

while((rd = read(in,buf,SIZE)) >0)
{
    dbug_p("\nREAD::%s::%d*\n",buf,rd);
    encry = aes_encrypt(&en,(unsigned char*) buf, &rd);

unsigned char* aes_encrypt(EVP_CIPHER_CTX *e,unsigned char * plain_text, unsigned int * len )    /* this function encryptes the  file:fd is passed as parameter */
  {
int ci_len = (*len) + AES_BLOCK_SIZE;
int flen =0;
unsigned char * cipher_text = malloc(ci_len);

EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL);              /* allows reusing of e for multiple cipher cycles */
EVP_EncryptUpdate(e, cipher_text, &ci_len, plain_text, *len);       /* Update cipher text */
EVP_EncryptFinal_ex(e, cipher_text+ci_len, &flen);          /* updates the remaining bytes */

您多次致电EVP_EncryptFinal_ex。它应该在最后被调用一次。 你如何进行解密也是如此。

这是一个关于如何进行加密的手册页的简单示例。 有一个类似的解密功能,它应该工作。

int do_crypt(char *outfile)
       {
       unsigned char outbuf[1024];
       int outlen, tmplen;
       /* Bogus key and IV: we'd normally set these from
        * another source.
        */
       unsigned char key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
       unsigned char iv[] = {1,2,3,4,5,6,7,8};
       char intext[] = "Some Crypto Text";
       EVP_CIPHER_CTX ctx;
       FILE *out;
       EVP_CIPHER_CTX_init(&ctx);
       EVP_EncryptInit_ex(&ctx, EVP_bf_cbc(), NULL, key, iv);

       if(!EVP_EncryptUpdate(&ctx, outbuf, &outlen, intext, strlen(intext)))
               {
               /* Error */
               return 0;
               }
       /* Buffer passed to EVP_EncryptFinal() must be after data just
        * encrypted to avoid overwriting it.
        */
       if(!EVP_EncryptFinal_ex(&ctx, outbuf + outlen, &tmplen))
               {
               /* Error */
               return 0;
               }
       outlen += tmplen;
       EVP_CIPHER_CTX_cleanup(&ctx);
       /* Need binary mode for fopen because encrypted data is
        * binary data. Also cannot use strlen() on it because
        * it wont be null terminated and may contain embedded
        * nulls.
        */
       out = fopen(outfile, "wb");
       fwrite(outbuf, 1, outlen, out);
       fclose(out);
       return 1;
       }

以下示例是根据您的情况读取文件。了解如何使用Update(多次调用)和Final(最后一次)例程。

int do_crypt(FILE *in, FILE *out, int do_encrypt)
        {
        /* Allow enough space in output buffer for additional block */
        inbuf[1024], outbuf[1024 + EVP_MAX_BLOCK_LENGTH];
        int inlen, outlen;
        /* Bogus key and IV: we'd normally set these from
         * another source.
         */
        unsigned char key[] = "0123456789";
        unsigned char iv[] = "12345678";
        /* Don't set key or IV because we will modify the parameters */
        EVP_CIPHER_CTX_init(&ctx);
        EVP_CipherInit_ex(&ctx, EVP_rc2(), NULL, NULL, NULL, do_encrypt);
        EVP_CIPHER_CTX_set_key_length(&ctx, 10);
        /* We finished modifying parameters so now we can set key and IV */
        EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, do_encrypt);
        for(;;)
                {
                inlen = fread(inbuf, 1, 1024, in);
                if(inlen <= 0) break;
                if(!EVP_CipherUpdate(&ctx, outbuf, &outlen, inbuf, inlen))
                        {
                        /* Error */
                        EVP_CIPHER_CTX_cleanup(&ctx);
                        return 0;
                        }
                fwrite(outbuf, 1, outlen, out);
                }
        if(!EVP_CipherFinal_ex(&ctx, outbuf, &outlen))
                {
                /* Error */
                EVP_CIPHER_CTX_cleanup(&ctx);
                return 0;
                }
        fwrite(outbuf, 1, outlen, out);
        EVP_CIPHER_CTX_cleanup(&ctx);
        return 1;
        }