Libsodium Salsa20解密乱码问题排查与修复求助
Libsodium加解密乱码问题修复
问题概述
使用C语言结合Libsodium库实现加解密功能,编译时已添加-lsodium参数,但加密后执行解密操作输出乱码,无法还原原始数据。
原代码
#include <sodium.h> #include <stdio.h> #include <string.h> #include <stdbool.h> #include <ctype.h> // Preprocessor directives #define KEY_SIZE 32 #define NONCE_SIZE 8 #define INPUT_SIZE 128 bool is_valid_email(const char *email) { const char *at_ptr = strchr(email, '@'); if (at_ptr == NULL || at_ptr == email || at_ptr[1] == '\0') { return false; } const char *dot_ptr = strrchr(at_ptr, '.'); if (dot_ptr == NULL || dot_ptr == (at_ptr + 1) || dot_ptr[1] == '\0') { return false; } return true; } bool is_valid_password(const char *password) { int len = strlen(password); if (len < 12) { return false; } bool has_digit = false; bool has_upper = false; bool has_special = false; for (int i = 0; i < len; ++i) { if (isdigit(password[i])) { has_digit = true; } else if (isupper(password[i])) { has_upper = true; } else if (strchr("!?(),", password[i]) != NULL) { has_special = true; } } return has_digit && has_upper && has_special; } bool is_valid_ipv4(const char *ipv4) { int octets[4]; char extra; if (sscanf(ipv4, "%d.%d.%d.%d%c", &octets[0], &octets[1], &octets[2], &octets[3], &extra) != 4) { return false; } for (int i = 0; i < 4; ++i) { if (octets[i] < 0 || octets[i] > 255) { return false; } } return true; } void encrypt(const char *input, unsigned char *ciphertext, unsigned char *key, unsigned char *nonce, size_t input_len) { // Generate random key and nonce randombytes_buf(key, KEY_SIZE); randombytes_buf(nonce, NONCE_SIZE); // Encrypt the input fields crypto_stream_salsa20_xor(ciphertext, (const unsigned char *)input, input_len, nonce, key); } void decrypt(unsigned char *plaintext, unsigned char *ciphertext, size_t len, unsigned char *key, unsigned char *nonce) { // Decrypt the data crypto_stream_salsa20_xor(plaintext, ciphertext, len, nonce, key); } int main() { // Initialize libsodium if (sodium_init() < 0) { printf("The necessary libraries are not available, exiting\n"); return 1; } // Prompt user for mode selection (encrypt/decrypt) char mode[INPUT_SIZE]; printf("Enter mode (encrypt/decrypt): "); fgets(mode, INPUT_SIZE, stdin); // If user chooses to encrypt if (strncmp(mode, "encrypt\n", INPUT_SIZE) == 0) { char field1[INPUT_SIZE]; char field2[INPUT_SIZE]; char field3[INPUT_SIZE]; // Prompt user for input fields and validate them do { printf("Enter email: "); fgets(field1, INPUT_SIZE, stdin); field1[strcspn(field1, "\n")] = 0; } while (!is_valid_email(field1)); do { printf("Enter password (min 12 characters, at least one uppercase, one digit, and one special character !?(),): "); fgets(field2, INPUT_SIZE, stdin); field2[strcspn(field2, "\n")] = 0; } while (!is_valid_password(field2)); do { printf("Enter IPv4 address: "); fgets(field3, INPUT_SIZE, stdin); field3[strcspn(field3, "\n")] = 0; } while (!is_valid_ipv4(field3)); // Encrypt the input fields unsigned char key1[KEY_SIZE], key2[KEY_SIZE], key3[KEY_SIZE]; unsigned char nonce1[NONCE_SIZE], nonce2[NONCE_SIZE], nonce3[NONCE_SIZE]; unsigned char encrypted1[INPUT_SIZE]; unsigned char encrypted2[INPUT_SIZE]; unsigned char encrypted3[INPUT_SIZE]; encrypt(field1, encrypted1, key1, nonce1, strlen(field1)); encrypt(field2, encrypted2, key2, nonce2, strlen(field2)); encrypt(field3, encrypted3, key3, nonce3, strlen(field3)); // Write the encrypted data, key, and nonce to a file FILE *file = fopen("encrypted_data.bin", "wb"); if (file == NULL) { printf("Error: Unable to open file\n"); return 1; } fwrite(key1, 1, KEY_SIZE, file); fwrite(nonce1, 1, NONCE_SIZE, file); fwrite(key2, 1, KEY_SIZE, file); fwrite(nonce2, 1, NONCE_SIZE, file); fwrite(key3, 1, KEY_SIZE, file); fwrite(nonce3, 1, NONCE_SIZE, file); fclose(file); printf("Encryption successful. Data saved to encrypted_data.bin\n"); } else if (strncmp(mode, "decrypt\n", INPUT_SIZE) == 0) { // Read encrypted data, key, and nonce from the file FILE *file = fopen("encrypted_data.bin", "rb"); if (file == NULL) { printf("Error: Unable to open file\n"); return 1; } unsigned char encrypted1[INPUT_SIZE]; unsigned char encrypted2[INPUT_SIZE]; unsigned char encrypted3[INPUT_SIZE]; unsigned char key1[KEY_SIZE]; unsigned char nonce1[NONCE_SIZE]; unsigned char key2[KEY_SIZE]; unsigned char nonce2[NONCE_SIZE]; unsigned char key3[KEY_SIZE]; unsigned char nonce3[NONCE_SIZE]; fread(encrypted1, 1, INPUT_SIZE, file); fread(encrypted2, 1, INPUT_SIZE, file); fread(encrypted3, 1, INPUT_SIZE, file); fread(key1, 1, KEY_SIZE, file); fread(nonce1, 1, NONCE_SIZE, file); fread(key2, 1, KEY_SIZE, file); fread(nonce2, 1, NONCE_SIZE, file); fread(key3, 1, KEY_SIZE, file); fread(nonce3, 1, NONCE_SIZE, file); fclose(file); // Decrypt the data unsigned char decrypted1[INPUT_SIZE]; unsigned char decrypted2[INPUT_SIZE]; unsigned char decrypted3[INPUT_SIZE]; decrypt(decrypted1, encrypted1, INPUT_SIZE, key1, nonce1); decrypt(decrypted2, encrypted2, INPUT_SIZE, key2, nonce2); decrypt(decrypted3, encrypted3, INPUT_SIZE, key3, nonce3); // Display the decrypted data printf("Decrypted email: %s\n", decrypted1); printf("Decrypted password: %s\n", decrypted2); printf("Decrypted IPv4 address: %s\n", decrypted3); } else { printf("Invalid choice. Please enter 'encrypt' or 'decrypt'.\n"); } return 0; }
解密乱码输出
Decrypted email: ��������AN����L��ϑ)��,��j��?О\>7�Y�n���c���"�H�T��C�:�:���0�2q`%h��]"WRz��& Ҟ���FUC}���dc-��5��:\>�4{���Kh�:.���iu���ŷ*�-[ Decrypted password: h�:.���iu���ŷ*�-[ Decrypted IPv4 address: ϊgo��x�r%�kg�3Y�AYԡ����!�5 ���C�l~}\<]uL����H͈bڧ��#P�2 �F��lu�����5 � ���1K|%�*E����ҫ�1|�k6�H ���s~x_ �L�x�
错误原因分析
- 密文未写入文件:加密阶段仅将密钥和nonce写入
encrypted_data.bin,完全遗漏了密文数据,导致文件中无有效加密内容。 - 读写顺序完全颠倒:加密时写入顺序为
key1→nonce1→key2→nonce2→key3→nonce3,但解密时先读取encrypted1/2/3(实际读取的是文件开头的密钥数据),再读取密钥和nonce,导致用错误的密钥解密错误的数据。 - 未保存原始数据长度:解密时使用固定的
INPUT_SIZE作为解密长度,而非原始数据的实际长度,可能导致超出部分解密出乱码,且未手动添加字符串终止符\0,破坏输出格式。
修复后的完整代码
#include <sodium.h> #include <stdio.h> #include <string.h> #include <stdbool.h> #include <ctype.h> #define KEY_SIZE 32 #define NONCE_SIZE 8 #define INPUT_SIZE 128 bool is_valid_email(const char *email) { const char *at_ptr = strchr(email, '@'); if (at_ptr == NULL || at_ptr == email || at_ptr[1] == '\0') { return false; } const char *dot_ptr = strrchr(at_ptr, '.'); if (dot_ptr == NULL || dot_ptr == (at_ptr + 1) || dot_ptr[1] == '\0') { return false; } return true; } bool is_valid_password(const char *password) { int len = strlen(password); if (len < 12) { return false; } bool has_digit = false; bool has_upper = false; bool has_special = false; for (int i = 0; i < len; ++i) { if (isdigit(password[i])) { has_digit = true; } else if (isupper(password[i])) { has_upper = true; } else if (strchr("!?(),", password[i]) != NULL) { has_special = true; } } return has_digit && has_upper && has_special; } bool is_valid_ipv4(const char *ipv4) { int octets[4]; char extra; if (sscanf(ipv4, "%d.%d.%d.%d%c", &octets[0], &octets[1], &octets[2], &octets[3], &extra) != 4) { return false; } for (int i = 0; i < 4; ++i) { if (octets[i] < 0 || octets[i] > 255) { return false; } } return true; } void encrypt(const char *input, unsigned char *ciphertext, unsigned char *key, unsigned char *nonce, size_t input_len) { randombytes_buf(key, KEY_SIZE); randombytes_buf(nonce, NONCE_SIZE); crypto_stream_salsa20_xor(ciphertext, (const unsigned char *)input, input_len, nonce, key); } void decrypt(unsigned char *plaintext, unsigned char *ciphertext, size_t len, unsigned char *key, unsigned char *nonce) { crypto_stream_salsa20_xor(plaintext, ciphertext, len, nonce, key); // 添加字符串终止符,确保输出正常 plaintext[len] = '\0'; } int main() { if (sodium_init() < 0) { printf("所需库不可用,退出\n"); return 1; } char mode[INPUT_SIZE]; printf("输入模式(encrypt/decrypt):"); fgets(mode, INPUT_SIZE, stdin); if (strncmp(mode, "encrypt\n", INPUT_SIZE) == 0) { char field1[INPUT_SIZE], field2[INPUT_SIZE], field3[INPUT_SIZE]; do { printf("输入邮箱:"); fgets(field1, INPUT_SIZE, stdin); field1[strcspn(field1, "\n")] = 0; } while (!is_valid_email(field1)); do { printf("输入密码(至少12位,包含大写字母、数字、特殊字符!?(),):"); fgets(field2, INPUT_SIZE, stdin); field2[strcspn(field2, "\n")] = 0; } while (!is_valid_password(field2)); do { printf("输入IPv4地址:"); fgets(field3, INPUT_SIZE, stdin); field3[strcspn(field3, "\n")] = 0; } while (!is_valid_ipv4(field3)); unsigned char key1[KEY_SIZE], key2[KEY_SIZE], key3[KEY_SIZE]; unsigned char nonce1[NONCE_SIZE], nonce2[NONCE_SIZE], nonce3[NONCE_SIZE]; unsigned char encrypted1[INPUT_SIZE], encrypted2[INPUT_SIZE], encrypted3[INPUT_SIZE]; size_t len1 = strlen(field1), len2 = strlen(field2), len3 = strlen(field3); encrypt(field1, encrypted1, key1, nonce1, len1); encrypt(field2, encrypted2, key2, nonce2, len2); encrypt(field3, encrypted3, key3, nonce3, len3); FILE *file = fopen("encrypted_data.bin", "wb"); if (file == NULL) { printf("错误:无法打开文件\n"); return 1; } // 写入顺序:长度→密文→密钥→nonce,确保解密时顺序一致 fwrite(&len1, sizeof(size_t), 1, file); fwrite(encrypted1, 1, len1, file); fwrite(key1, 1, KEY_SIZE, file); fwrite(nonce1, 1, NONCE_SIZE, file); fwrite(&len2, sizeof(size_t), 1, file); fwrite(encrypted2, 1, len2, file); fwrite(key2, 1, KEY_SIZE, file); fwrite(nonce2, 1, NONCE_SIZE, file); fwrite(&len3, sizeof(size_t), 1, file); fwrite(encrypted3, 1, len3, file); fwrite(key3, 1, KEY_SIZE, file); fwrite(nonce3, 1, NONCE_SIZE, file); fclose(file); printf("加密成功,数据已保存至encrypted_data.bin\n"); } else if (strncmp(mode, "decrypt\n", INPUT_SIZE) == 0) { FILE *file = fopen("encrypted_data.bin", "rb"); if (file == NULL) { printf("错误:无法打开文件\n"); return 1; } unsigned char encrypted1[INPUT_SIZE], encrypted2[INPUT_SIZE], encrypted3[INPUT_SIZE]; unsigned char key1[KEY_SIZE], key2[KEY_SIZE], key3[KEY_SIZE]; unsigned char nonce1[NONCE_SIZE], nonce2[NONCE_SIZE], nonce3[NONCE_SIZE]; size_t len1, len2, len3; unsigned char decrypted1[INPUT_SIZE], decrypted2[INPUT_SIZE], decrypted3[INPUT_SIZE]; // 严格按照加密时的写入顺序读取 fread(&len1, sizeof(size_t), 1, file); fread(encrypted1, 1, len1, file); fread(key1, 1, KEY_SIZE, file); fread(nonce1, 1, NONCE_SIZE, file); fread(&len2, sizeof(size_t), 1, file); fread(encrypted2, 1, len2, file); fread(key2, 1, KEY_SIZE, file); fread(nonce2, 1, NONCE_SIZE, file); fread(&len3, sizeof(size_t), 1, file); fread(encrypted3, 1, len3,
相关产品推荐
相关产品推荐

