【问题标题】:Invalid characters in decrypted output using AES encryption with OpenSSL libcrypto EVP API and FUSE filesystem使用带有 OpenSSL libcrypto EVP API 和 FUSE 文件系统的 AES 加密的解密输出中的无效字符
【发布时间】:2014-04-14 04:34:26
【问题描述】:

我正在开发一个使用 AES 加密和 OpenSSL 的加密/解密程序。我在两个本地目录之间建立了一个 FUSE 文件系统,一个是镜像目录,另一个是挂载点,在我的 linux 虚拟机上。我正在修改 fuse_operations 结构中的读取、写入和创建函数,以便当我从挂载点目录打开和读取加密文件时,明文应该显示在应用程序的窗口中。

我有一个名为 do_crypt 的函数,它接受一个包含文本的文件、一个解密/加密文本应该去的输出文件、一个用于解密、加密或传递的 int 以及用于完成的密码短语行动。代码如下:

#define BLOCKSIZE 1024
#define FAILURE 0
#define SUCCESS 1

extern int do_crypt(FILE* in, FILE* out, int action, char* key_str){
/* Local Vars */

/* Buffers */
unsigned char inbuf[BLOCKSIZE];
int inlen;
/* Allow enough space in output buffer for additional cipher block */
unsigned char outbuf[BLOCKSIZE + EVP_MAX_BLOCK_LENGTH];
int outlen;
int writelen;

/* OpenSSL libcrypto vars */
EVP_CIPHER_CTX ctx;
unsigned char key[32];
unsigned char iv[32];
int nrounds = 5;

/* tmp vars */
int i;

/* Setup Encryption Key and Cipher Engine if in cipher mode */
if(action >= 0){
if(!key_str){
    /* Error */
    fprintf(stderr, "Key_str must not be NULL\n");
    return 0;
}
/* Build Key from String */
i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), NULL,
           (unsigned char*)key_str, strlen(key_str), nrounds, key, iv);
if (i != 32) {
    /* Error */
    fprintf(stderr, "Key size is %d bits - should be 256 bits\n", i*8);
    return 0;
}
/* Init Engine */
EVP_CIPHER_CTX_init(&ctx);
EVP_CipherInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key, iv, action);
}    

/* Loop through Input File*/
for(;;){
/* Read Block */
inlen = fread(inbuf, sizeof(*inbuf), BLOCKSIZE, in);
if(inlen <= 0){
    /* EOF -> Break Loop */
    break;
}

/* If in cipher mode, perform cipher transform on block */
if(action >= 0){
    if(!EVP_CipherUpdate(&ctx, outbuf, &outlen, inbuf, inlen))
    {
        /* Error */
        EVP_CIPHER_CTX_cleanup(&ctx);
        return 0;
    }
}
/* If in pass-through mode. copy block as is */
else{
    memcpy(outbuf, inbuf, inlen);
    outlen = inlen;
}

/* Write Block */
writelen = fwrite(outbuf, sizeof(*outbuf), outlen, out);
if(writelen != outlen){
    /* Error */
    perror("fwrite error");
    EVP_CIPHER_CTX_cleanup(&ctx);
    return 0;
}
}

/* If in cipher mode, handle necessary padding */
if(action >= 0){
/* Handle remaining cipher block + padding */
if(!EVP_CipherFinal_ex(&ctx, outbuf, &outlen))
    {
    /* Error */
    EVP_CIPHER_CTX_cleanup(&ctx);
    return 0;
    }
/* Write remainign cipher block + padding*/
fwrite(outbuf, sizeof(*inbuf), outlen, out);
EVP_CIPHER_CTX_cleanup(&ctx);
}

/* Success */
return 1;
}

该功能在不使用保险丝系统的情况下也能正常工作。一切都已正确加密/解密,但是当我使用读取功能解密 fuse 文件系统中的文件时,我在输出中得到了一些无效字符。我尝试了 3 个文本编辑器来查看文件:sublime 像任何其他文件一样读取输出,没有问题; geany 不会打开文件,因为它说它无法识别编码并且存在无效字符; gedit 显示解密后的输出,但也显示一些无效字符,表示为“/00/00/00”。我不知道这些字符是什么。我做了一些研究,我相信它们是空字符,但我不知道为什么当我使用 fuse 函数并运行解密时它们会出现,但不使用时它们不会出现在输出中。

我已经为读取功能尝试了 2 个不同的版本:一种是使用内存缓冲区来保存内容。

static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
        struct fuse_file_info *fi)
{
  (void) fi;
  int res;

char fpath[PATH_MAX];
xmp_fullpath(fpath, path);//changes path to mirror directory

FILE *f;


FILE *tmpFile;
tmpFile = tmpfile();
f = fopen(fpath, "r");

do_crypt(f, tmpFile, DECRYPT, XMP_DATA->key_phrase);//function that decrypts/encrypts

rewind(tmpFile);
res = pread(fileno(tmpFile), buf, size, offset);//read the encrypted/decrypted output to the application buffer

fclose(f);
fclose(tmpFile);
return res;
}

另一种方法是在进行加密/解密之前创建一个临时文件:

int res;
char fpath[PATH_MAX];
xmp_fullpath(fpath, path);
FILE *memstream;
char *membuf;
size_t memlen;
//off_t eob;
memstream = open_memstream(&membuf, &memlen);//create a dynamically allocated buffer in memory

FILE *f = fopen(fpath, "rb");

do_crypt(f, memstream, DECRYPT, XMP_DATA->key_phrase);

fflush(memstream);
fseek(memstream, offset, SEEK_SET);

res = fread(buf, 1, memlen, memstream);

return res;

我的问题是:

1) 为什么在尝试解密文件时,我的输出中会出现无效字符“/00/00”,这些字符似乎是空字符?

2) 为什么我可以用一个文本编辑器查看文件,而不能用另一个?

再次加密和传递在保险丝系统中完美运行,我可以使用所有三个文本编辑器打开它们。当我不使用保险丝时,do_crypt 工作得很好。但是当我尝试在保险丝读取功能中解密时,我得到了正确的解密文本,但我也得到了那些无效字符。当我使用“cat”终端命令时,输出不包含无效字符。

这里是完整代码库的链接。 https://github.com/latitude98/CU-CS3753-PA4

【问题讨论】:

  • 这可能是sizeof() 的罪魁祸首。请注意,这取决于编辑器/查看器对不可打印字符(如 ASCII 字符 00)的处理方式。
  • 我的意思是“像”而不是“喜欢”——当我累了,给我一点酒,我变成了一个加拿大女孩:P
  • 哈哈我明白你的意思了!

标签: openssl aes


【解决方案1】:

在 VFS 级别(例如通过 fuse)进行数据变异(例如加密/解密)的常见错误是在调用底层 getattr() 系统调用之前未能在 fuse getattr 函数中变异(即解密)文件。

在您的情况下,如果 getattr() 系统调用传递的是加密文件而不是未加密文件,它将返回错误的文件大小(加密和解密文件具有不同的文件大小)。许多程序依赖这个文件大小是正确的,以便知道要读取多少文件,所以如果 getattr() 返回的是加密文件的大小而不是未加密文件的大小,你可能会看到错误就像你描述的那样。

某些程序(例如复杂的文本编辑器)在读取文件时依赖于知道文件大小,因此需要一个有效的 getattr() 才能运行。像“cat”这样的实用程序倾向于使用更基本的“一次读取一个字节,直到我看到 EOF”的方法,因此不太依赖于正确的文件大小。这可以解释为什么有些程序可以正常工作(那些寻找 EOF 的程序),而其他程序却不能(那些试图读取字节的程序)。

除了明显的(即读取)需要调用解密的 VFS/fuse 功能外,还有一些 VFS/fuse 功能。如上所述,getattr 可能是最关键的一个。您可能还想查看 truncate 和 open。根据您实现文件系统的方式,这些可能还需要一些解密意识。

【讨论】:

    猜你喜欢
    • 2021-06-06
    • 2015-01-06
    • 1970-01-01
    • 1970-01-01
    • 2015-02-16
    • 2020-08-20
    • 1970-01-01
    • 2021-03-17
    • 2012-12-04
    相关资源
    最近更新 更多