使用libsodium的crypto_secretbox解密后文件末尾出现异常字符求助
问题:解密后文件末尾出现冗余[NUL]字符
我编写了encryptf和decryptf两个函数(后缀加f避免与libsodium原有函数重名),用于通过指定密钥对内容进行加密解密,密钥为随机生成的ANSI字符串。
加密函数实现
int encryptf(const char *input_file, const char *output_file, const unsigned char *key) { if (sodium_init() < 0) { printf("Error initializing\n"); return 1; } // 打开输入输出文件,确保可读写,不存在则创建 FILE *input = fopen(input_file, "r"); FILE *output = fopen(output_file, "ab+"); if (!input || !output) { printf("Error opening files to encrypt\n"); return 1; } // 获取文件大小 fseek(input, 0, SEEK_END); size_t file_size = ftell(input); fseek(input, 0, SEEK_SET); // 分配明文和密文内存 unsigned char *plaintext = (unsigned char *) malloc(file_size); unsigned char *ciphertext = (unsigned char *) malloc(file_size + crypto_secretbox_MACBYTES); // 创建随机nonce(使用libsodium安全随机函数) unsigned char nonce[crypto_secretbox_NONCEBYTES]; randombytes(nonce, sizeof(nonce)); // 读取明文 fread(plaintext, 1, file_size, input); // 此处mac数组未实际使用,可删除 unsigned char mac[crypto_secretbox_NONCEBYTES]; // 加密明文 crypto_secretbox_easy(ciphertext, plaintext, file_size, nonce, key); // 写入nonce和密文到输出文件 fwrite(nonce, 1, sizeof(nonce), output); fwrite(ciphertext, 1, file_size + crypto_secretbox_MACBYTES, output); // 清理资源 fclose(input); fclose(output); free(plaintext); free(ciphertext); return 0; }
解密函数实现
int decryptf(const char *input_file, const char *output_file, const unsigned char *key) { if (sodium_init() < 0) { printf("Error initializing Libsodium\n"); return 1; } FILE *input = fopen(input_file, "rb"); FILE *output = fopen(output_file, "wb"); if (!input || !output) { printf("Error opening files to decrypt\n"); return 1; } // 读取nonce unsigned char nonce[crypto_secretbox_NONCEBYTES]; fread(nonce, 1, sizeof(nonce), input); // 获取去掉nonce后的文件大小 fseek(input, 0, SEEK_END); size_t file_size = ftell(input) - crypto_secretbox_NONCEBYTES; fseek(input, crypto_secretbox_NONCEBYTES, SEEK_SET); // 分配密文和明文内存 unsigned char *ciphertext = (unsigned char *) malloc(file_size); unsigned char *plaintext = (unsigned char *) malloc(file_size); // 读取密文 fread(ciphertext, 1, file_size, input); // 解密密文 if (crypto_secretbox_open_easy(plaintext, ciphertext, file_size, nonce, key) != 0) { printf("Error decrypting %s\n", input_file); return 1; } // 写入明文到输出文件 fwrite(plaintext, 1, file_size, output); // 清理资源 fclose(input); fclose(output); free(ciphertext); free(plaintext); return 0; }
调用示例(参考)
int main(void) { FILE *f1 = fopen("tests/file.txt", "ab+"); if (!f1) { printf("Failed create test files.\n"); exit(EXIT_SUCCESS); } fprintf(f1, "HELLO\n\nworld!\n..\n\thello\n../14hf\n1"); fclose(f1); char *key = rand_string(32); encryptf("tests/file.txt", "tests/file_enc.txt", key); printf("File encrypted.\n"); decryptf("tests/file_enc.txt", "tests/file.txt", key); }
问题现象
解密后文件内容可正常还原,但末尾会出现无法显示的[NUL]字符。尝试手动移除libsodium的认证标签会导致解密失败,运行环境为搭载M2芯片的2023款MacBook Pro,需要解决冗余字符问题。
解决思路
核心原因
解密时错误地将**密文总长度(含MAC标签)**当作明文长度写入文件。crypto_secretbox_open_easy解密后,明文实际长度为密文长度 - crypto_secretbox_MACBYTES,当前直接把密文长度(含MAC)作为明文长度写入,导致末尾多出crypto_secretbox_MACBYTES个NUL字符。
修复步骤
1. 修正解密函数的明文写入逻辑
在解密函数中,替换原明文写入代码,计算正确的明文长度:
// 替换原fwrite行 size_t plaintext_size = file_size - crypto_secretbox_MACBYTES; if (plaintext_size > 0) { fwrite(plaintext, 1, plaintext_size, output); }
2. 额外优化建议
- 加密时打开输入文件用
"rb"(二进制模式),避免Windows下换行符转换导致文件大小计算错误;输出文件用"wb"代替"ab+",防止重复写入。 - 删除加密函数中未使用的
mac数组,减少冗余代码。 - 修正调用示例中的笔误:
fkey改为key。
内容的提问来源于stack exchange,提问作者thou mother
相关产品推荐
相关产品推荐

