使用Libsodium的secretbox_open_easy无法解密加密文件内容求助
Libsodium secretbox_open_easy解密失败排查方案
首先明确:secretbox_open_easy的返回值不对应标准errno,perror是基于全局errno变量输出信息的,当该函数返回非0但未修改errno时,perror就会显示“Success”——这个提示没有实际参考价值,别被它误导。
接下来重点排查secretbox_open_easy的参数及相关流程:
密文长度必须准确:Libsodium的secretbox密文长度是「明文长度 + crypto_secretbox_MACBYTES」,解密时传入的密文长度必须严格等于这个值。如果加密后保存文件时只存了加密内容、漏掉了末尾的MAC校验块,解密时长度不足必然失败。
Nonce必须完全一致:确保解密用的nonce和加密时的二进制字节完全相同,注意nonce长度是
crypto_secretbox_NONCEBYTES,别出现以下错误:把二进制nonce当作文本字符串存储/读取导致截断、编码转换错误、读取时字节序不一致。密钥生成参数完全匹配:虽然你用了相同密码和盐,但要核对
crypto_pwhash的所有参数:- 哈希算法(比如
crypto_pwhash_ALG_DEFAULT) - 内存限制(
crypto_pwhash_MEMLIMIT_INTERACTIVE等常量) - 操作次数(
crypto_pwhash_OPSLIMIT_INTERACTIVE等常量) - 生成的密钥长度必须是
crypto_secretbox_KEYBYTES,盐的长度必须是crypto_pwhash_SALTBYTES
任何一项和加密时不一致,都会生成不同的密钥,导致解密失败。
- 哈希算法(比如
输出缓冲区大小足够:解密的明文缓冲区大小必须至少是「密文长度 - crypto_secretbox_MACBYTES」,如果缓冲区过小,可能触发函数返回失败甚至内存溢出。
参数顺序不能搞混:
secretbox_open_easy的参数顺序是:int crypto_secretbox_open_easy(unsigned char *m, const unsigned char *c, unsigned long long clen, const unsigned char *n, const unsigned char *k);依次是明文缓冲区、密文、密文长度、nonce、密钥,别把明文和密文缓冲区搞反,或者nonce与密钥的顺序颠倒。
关键代码对比示例
加密流程关键片段
#define PLAINTEXT_LEN 1024 unsigned char plaintext[PLAINTEXT_LEN] = "待加密内容"; unsigned char ciphertext[PLAINTEXT_LEN + crypto_secretbox_MACBYTES]; unsigned char nonce[crypto_secretbox_NONCEBYTES]; unsigned char key[crypto_secretbox_KEYBYTES]; unsigned char salt[crypto_pwhash_SALTBYTES]; // 生成随机盐和nonce(加密时做一次,然后和密文一起保存) randombytes_buf(salt, sizeof(salt)); randombytes_buf(nonce, sizeof(nonce)); // 从密码生成密钥 crypto_pwhash(key, sizeof(key), "你的密码", strlen("你的密码"), salt, crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE, crypto_pwhash_ALG_DEFAULT); // 执行加密 crypto_secretbox_easy(ciphertext, plaintext, PLAINTEXT_LEN, nonce, key); // 将salt、nonce、ciphertext写入文件(注意写入完整的ciphertext,包括MAC)
解密流程关键片段
// 从文件读取salt、nonce、ciphertext unsigned char salt[crypto_pwhash_SALTBYTES]; unsigned char nonce[crypto_secretbox_NONCEBYTES]; // 假设读取到的密文长度是CIPHERTEXT_LEN = PLAINTEXT_LEN + crypto_secretbox_MACBYTES unsigned char ciphertext[CIPHERTEXT_LEN]; unsigned char key[crypto_secretbox_KEYBYTES]; // 明文缓冲区大小 = CIPHERTEXT_LEN - crypto_secretbox_MACBYTES unsigned char plaintext[CIPHERTEXT_LEN - crypto_secretbox_MACBYTES]; // 用相同参数生成密钥 crypto_pwhash(key, sizeof(key), "你的密码", strlen("你的密码"), salt, crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE, crypto_pwhash_ALG_DEFAULT); // 执行解密 int ret = crypto_secretbox_open_easy(plaintext, ciphertext, CIPHERTEXT_LEN, nonce, key); if (ret != 0) { // 直接判断返回值,非0即认证失败(密钥/nonce错误、密文篡改等) fprintf(stderr, "解密失败:密文认证不通过\n"); return 1; }
内容的提问来源于stack exchange,提问作者Swan Geon
相关产品推荐
相关产品推荐

