1

嗨,我正在 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);

}

谢谢你。

4

1 回答 1

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 确实如此)。然后它将包含盐的标头写入输出文件,然后是密文。这称为基于密码的加密 (PBE),有时称为基于密码的密钥派生函数 (PBKDF)。

根据您实际想要做的事情,有两种方法。

解密您拥有的文件

将第一行从(不包括行终止符)读取为字符,而不是 base64,并通过sym.key以下方式用盐喂它: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 文件,则无需先将其读入内存,然后将 b64BIO 推送到 memBIO 上;您可以在读取文件的 fileBIO 上推送 b64BIO。

您无需flen*2outbuff. 解密后的明文永远不会比密文长。(salted-PBE形式其实flen-16就是上面那个。)是加密,可以扩展数据,然后只有一个块(对于AES,16字节)。

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

如果你想支持不适合可用内存的大文件,或者可能不支持,迭代读取块,将每个块输入DecryptUpdate,写出结果(这将滞后大约一个块),并在 EOF 调用DecryptFinal和输出任何最后部分阻塞。

于 2015-12-22T19:59:45.277 回答