You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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�

错误原因分析

  1. 密文未写入文件:加密阶段仅将密钥和nonce写入encrypted_data.bin,完全遗漏了密文数据,导致文件中无有效加密内容。
  2. 读写顺序完全颠倒:加密时写入顺序为key1→nonce1→key2→nonce2→key3→nonce3,但解密时先读取encrypted1/2/3(实际读取的是文件开头的密钥数据),再读取密钥和nonce,导致用错误的密钥解密错误的数据。
  3. 未保存原始数据长度:解密时使用固定的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,
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 13:09:34