OpenSSL EVP API:如何使用对称密钥文件解密加密文件

时间:2015-12-18 14:10:59

标签: openssl symmetric-key evp-cipher

嗨,我正在Linux上使用C语言。

我有一个与对称密钥解密相关的查询。

我使用以下命令生成了对称密钥。

openssl rand base64 512> sym.key

使用此密钥( sym.key )我已使用以下命令加密文件。

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

它已生成加密文件 temp.enc

现在,我必须使用与 EVP Decrypt API 相同的密钥( sym.key ),并且必须解密此加密文件。

任何人都可以建议我采取更好的方法。

这是代码

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);

}

谢谢。

1 个答案:

答案 0 :(得分:3)

  

使用此密钥(sym.key)我已使用以下命令加密文件。   
openssl enc -aes-256-cbc -in temp.txt -out temp.enc -kfile sym.key

不完全。 openssl enc -kfile读取文件的第一行,只读取文件的第一行,并将其用作密码,这与密钥不同。 enc有三个输入密码的选项,之后它通过密钥派生过程使用随机盐运行密码以生成实际密钥和IV (对于使用IV的密码,以及AES-CBC确实如此。然后它将包含salt的头,然后是密文写入输出文件。这称为基于密码的加密(PBE),有时也称为基于密码的密钥派生功能(PBKDF)。

根据您的实际需要,有两种方法。

要解密您拥有的文件

sym.key(不包括行终结符)读取第一行作为字符,而不是base64,并使用temp.encEVP_BytesToKey之类的盐来提供它:

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!)

创建您显然想要的文件

要使用enc创建使用直接密钥加密的文件,必须使用十六进制命令行上的-K选项(大写K)传递它。但是,在C程序中处理十六进制是一件很麻烦的事情,所以我会在加密方面处理它,比如:

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

然后在你的C程序中读取sym.key(至少在Windows上作为二进制文件)并按原样使用它,现在你的IV为16 0。

或者,让rand写入十六进制并按原样使用

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

然后在你的C程序中读取sym.key(作为文本行)并将其从64个十六进制转换为unsigned char key [32]

其他要点

如果您确实需要解码base64文件,则不需要先将其读入内存,然后在memBIO上推送b64BIO;你可以在读取文件的fileBIO上推送b64BIO。

您无需为flen*2分配outbuff。解密的明文永远不会比密文长。 (对于salted-PBE表单实际上flen-16如上所述。) encryption 可以扩展数据,然后只有一个块(对于AES,16个字节)。

在使用之前检查malloc未返回NULL是一种好习惯。这在现代系统中很少发生,但是如果从这个更大的程序中调用这样的简单代码并且程序的某些其他部分有一个耗尽内存的错误,或者可能是拒绝服务攻击利用的漏洞,则会发生这种情况。

如果要支持不适合可用内存的大文件,或者可能不支持,请迭代读取块,将每个文件提供到DecryptUpdate,写出结果(将滞后一个块),并在EOF调用DecryptFinal并输出任何最后的部分块。