2012-04-30 328 views
2

我想在这种情况下读取文件(.txt),并使用openssl的EVP api使用AES256CBC对其进行加密/解密。 (读(plain.txt) - >创建(encrypt.txt) - >创建(decrypt.txt))使用openssl对文件进行加密的问题evp api(aes256cbc)

# 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; 
} 

我的问题是,当我解密加密文件我的,我收到了文件,该文件是没有正确解密(如正确的字符串垃圾正确的字符串垃圾 ...)

[email protected]:~/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 

这可能是什么原因造成的。它是否还在读别的东西,或者我在某处做出任何愚蠢的错误。

EDIT:如果我只是加密的阵列它正确地加密和解密(用36char长试过)而不打印任何垃圾。

我想我错过了(不处理)一些* nix文件结构的细节..? 或者有没有更好的方法来对文件进行这种加密?

非常感谢

+0

[OpenSSL 1.1.0c更改了摘要算法](http://stackoverflow.com/q/39637388/608639)在某些内部组件中使用。以前使用MD5,1.1.0切换到SHA256。请注意,这些更改不会影响'EVP_BytesToKey'和像'openssl enc'这样的命令。 – jww

回答

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一个希望将0终结。但是,加密/解密数据不是零终止的。相反,您应该使用类似for (i = 0; i < rd; i++) printf("%02x ");这样的循环打印rd字符 - 这就是为什么您对问题的分析可能存在缺陷。

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

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

+0

再次感谢您回答我的问题。其实我不是在我的实际代码中调用aes_decrypt(),就像那样..我首先从plain.txt中读取一个循环(每次32bytes)并传递给aes_encrypt(),然后将它们写入encrypt.txt文件中,直到EOF然后我打开然后文件lseek它开始,然后再次从这个加密文件读取32bytes /循环迭代,并且这次调用aes_decrypt(),并且在返回时将解密代码写入一个新的txt文件。我已经用完整的代码更新了我的代码,请检查非常感谢 – abhi

+0

得到你在评论中说的话我试着在最后一次调用encrypt final和decrypt最后一次读取迭代。现在我没有得到解密文件中的垃圾,但一些字符从解密文件中丢失..任何想法whay?非常感谢.. – abhi

+1

嗯,不知道我明白你现在的代码是怎么样的,但你应该做的是:init,{读,更新,写},最后写。用于加密和解密。 –

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[] = ""; 
     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; 
     } 
相关问题