【发布时间】: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
-
哈哈我明白你的意思了!