C语言libsodium输出与PHP集成困难排查求助
问题诊断与修复方案
看起来你碰到的问题是C程序生成的密封密钥数据格式不对,导致PHP的sodium_crypto_box_seal_open()解析失败返回null。既然你已经确认PHP逻辑没问题,那问题肯定出在C代码的输出上。我仔细检查了你的代码,发现两个关键的长度处理错误,这应该就是根源所在:
1. 写入密封密钥时的长度错误
在// Seal Key部分,你用sizeof sealed来写入文件,但sealed是指针类型,sizeof sealed返回的是指针本身的字节数(比如64位系统上是8字节),而不是实际的密封数据总长度。正确的长度应该是crypto_box_SEALBYTES + crypto_secretstream_xchacha20poly1305_KEYBYTES,也就是密封后的密钥总字节数。
错误代码:
fwrite(sealed,1,sizeof sealed,fp_o);
修复后:
const size_t sealed_len = crypto_box_SEALBYTES + crypto_secretstream_xchacha20poly1305_KEYBYTES; fwrite(sealed, 1, sealed_len, fp_o);
2. Base64解码公钥时的缓冲区大小错误
解析输入公钥的Base64时,你给sodium_base642bin传的第二个参数是sizeof(inputKeyBin),但inputKeyBin是指针,这个值同样是指针的大小,而非实际需要的公钥字节数crypto_box_PUBLICKEYBYTES。这会导致解码缓冲区不足,要么解析出错误的公钥,要么直接破坏内存,最终生成无效的密封数据。
错误代码:
if (sodium_base642bin(inputKeyBin,sizeof(inputKeyBin),inputKey,strlen(inputKey),NULL,&bin_len,&b64_end,sodium_base64_VARIANT_ORIGINAL) !=0)
修复后:
if (sodium_base642bin(inputKeyBin, crypto_box_PUBLICKEYBYTES, inputKey, strlen(inputKey), NULL, &bin_len, &b64_end, sodium_base64_VARIANT_ORIGINAL) != 0)
额外的内存管理建议(可选但重要)
你用sodium_malloc分配了好几块内存,但没调用sodium_free释放,长期运行可能会导致内存泄漏。建议在程序结束前或者不再使用这些内存时释放:
sodium_free(inputKeyBin); sodium_free(key); sodium_free(sealed); sodium_free(base64Out); sodium_free(base64OutX);
完整修复后的C代码
我把修复后的完整代码整理好了,你可以直接替换测试:
#include <sodium.h> #include "filedata.h" #include <stdio.h> #include <string.h> #define CHUNK_SIZE 8192 void doEncrypt(char * inKey,char * plainText,char * cipherText,char * outKey) { //Get input key //const char *inputKey=readFile(inKey); const char *inputKey="aQFsJJuU65zXKzqDfS1KuxqqebkER/cPqno+oZL5PgQ="; // testing with a fixed value.... unsigned char * inputKeyBin = sodium_malloc(crypto_box_PUBLICKEYBYTES); size_t bin_len; const char *b64_end; // 修复:使用正确的公钥缓冲区大小 if (sodium_base642bin(inputKeyBin, crypto_box_PUBLICKEYBYTES, inputKey, strlen(inputKey), NULL, &bin_len, &b64_end, sodium_base64_VARIANT_ORIGINAL) !=0) { printf("sodium_base642bin() failure\n"); exit(1); } // Encrypt file START unsigned char *key; unsigned char buf_in[CHUNK_SIZE]; unsigned char buf_out[CHUNK_SIZE+crypto_secretstream_xchacha20poly1305_ABYTES]; unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]; crypto_secretstream_xchacha20poly1305_state st; FILE *fp_t,*fp_s; unsigned long long out_len; size_t rlen; int eof; unsigned char tag; key = (unsigned char *)sodium_malloc(crypto_secretstream_xchacha20poly1305_KEYBYTES); crypto_secretstream_xchacha20poly1305_keygen(key); fp_s = fopen(plainText, "rb"); fp_t = fopen(cipherText, "wb"); crypto_secretstream_xchacha20poly1305_init_push(&st, header, key); fwrite(header, 1, sizeof header, fp_t); do { rlen = fread(buf_in, 1, sizeof buf_in, fp_s); eof = feof(fp_s); tag = eof ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0; crypto_secretstream_xchacha20poly1305_push(&st, buf_out, &out_len, buf_in, rlen, NULL, 0, tag); fwrite(buf_out, 1, (size_t) out_len, fp_t); } while (! eof); fclose(fp_t); fclose(fp_s); // Encrypt file END // Seal Key const size_t sealed_len = crypto_box_SEALBYTES + crypto_secretstream_xchacha20poly1305_KEYBYTES; unsigned char * sealed = sodium_malloc(sealed_len); if (crypto_box_seal(sealed, key, crypto_secretstream_xchacha20poly1305_KEYBYTES, inputKeyBin) !=0) { printf("crypto_box_seal() failure\n"); exit(1); } FILE *fp_o; fp_o = fopen(outKey,"wb"); // 修复:写入正确长度的密封数据 fwrite(sealed, 1, sealed_len, fp_o); fclose(fp_o); const size_t outSz = sodium_base64_encoded_len(sealed_len, sodium_base64_VARIANT_ORIGINAL); char * base64Out = sodium_malloc(outSz); sodium_bin2base64(base64Out, outSz, sealed, sealed_len, sodium_base64_VARIANT_ORIGINAL); printf("E: %s\n",base64Out); const size_t outSzX = sodium_base64_encoded_len(crypto_secretstream_xchacha20poly1305_KEYBYTES, sodium_base64_VARIANT_ORIGINAL); char * base64OutX = sodium_malloc(outSzX); sodium_bin2base64(base64OutX, outSzX, key, crypto_secretstream_xchacha20poly1305_KEYBYTES, sodium_base64_VARIANT_ORIGINAL); printf("K: %s\n",base64OutX); // 新增:释放所有sodium_malloc分配的内存 sodium_free(inputKeyBin); sodium_free(key); sodium_free(sealed); sodium_free(base64Out); sodium_free(base64OutX); }
验证步骤
- 编译修复后的C程序,运行生成加密文件和密封密钥的Base64输出
- 把
E:后面的Base64字符串复制到PHP代码的$sealedDataFromC变量里 - 此时PHP的
sodium_crypto_box_seal_open()应该能正确解析出密钥,不再返回null了
内容的提问来源于stack exchange,提问作者Little Code
相关产品推荐
相关产品推荐

