【问题标题】:Xor encryption in C?C中的异或加密?
【发布时间】:2020-08-12 16:33:15
【问题描述】:

我尝试在 C 中学习 XOR,并在 github 上找到了this example。

#include <stdio.h>
#include <string.h>

void encryptDecrypt(char *input, char *output) {
    char key[] = {'K', 'C', 'Q'}; //Can be any chars, and any size array
    
    int i;
    for(i = 0; i < strlen(input); i++) {
        output[i] = input[i] ^ key[i % (sizeof(key)/sizeof(char))];
    }
}

int main (int argc, char *argv[]) {
    char baseStr[] = "kylewbanks.com";
    
    char encrypted[strlen(baseStr)];
    encryptDecrypt(baseStr, encrypted);
    printf("Encrypted:%s\n", encrypted);
    
    char decrypted[strlen(baseStr)];
    encryptDecrypt(encrypted, decrypted);
    printf("Decrypted:%s\n", decrypted);
}

你能解释一下这个说法吗?

output[i] = input[i] ^ key[i % (sizeof(key)/sizeof(char))];

PS:如果我更改 char baseStr[] 中的语句,输出是错误的,为什么?例如,如果 baseStr 的值为 Test,则输出将为:

Encrypted: *?�
Decrypted:Test�&

编辑

如果有人想要这个程序的工作版本,这里是:
#include <stdio.h>
#include <string.h>
#include <ctype.h>

void encryptDecrypt(char*, char*, char*);

int main()
{
    char message[500];
    char key[50];

    printf("Enter the message: ");
    fgets(message, 500, stdin);
    message[strcspn(message, "\n")] = 0;
    
    printf("Enter the key: ");
    fgets(key, 50, stdin);
    key[strcspn(key, "\n")] = 0;
    
    for(size_t i = 0; i < strlen(key); ++i)
        key[i] = toupper (key[i]);

    putchar('\n');

    char encryptedMessage[strlen(message) + 1];
    memset(encryptedMessage, '\0', sizeof(encryptedMessage));
    encryptDecrypt(message, encryptedMessage, key);
    printf("Encrypted message: %s\n", encryptedMessage);

    char decryptedMessage[strlen(message) + 1];
    memset(decryptedMessage, '\0', sizeof(decryptedMessage));
    encryptDecrypt(encryptedMessage, decryptedMessage, key);
    printf("Decrypted message: %s\n", decryptedMessage);

    return 0;
}

void encryptDecrypt(char *input, char *output, char *key)
{
    for (int i = 0; i < strlen(input); ++i)
        output[i] = input [i] ^ key [i % strlen(key)];
}

【问题讨论】:

  • 格式良好的 C 字符串末尾有一个零字节,称为 NUL 终止符。因此,无论baseStr 中的内容如何,​​该程序都无法正常工作。你对原始字符串很幸运,对Test 很不幸。要解决这个问题,encrypted 和decrypted 数组的大小需要大一字节,encryptDecrypt 函数需要将 NUL 终止符放在字符串的末尾。

标签: c encryption xor


【解决方案1】:

一块一块地拆开:

  1. (sizeof(key)/sizeof(char)) 这是key 数组的长度。在您的情况下,它是3。注意,sizeof(char) 是多余的,可以省略。
  2. key[i % 3] 这将key 的索引限制为0、1 或2,因此不会访问key 数组之外的内存。
  3. output[i] = input[i] ^ key[i % 3];您将 input 中的每个 character 与 key 进行异或运算,并将其分配给 output。

注意,如果你想打印encrypted 和decrypted,你应该像这样定义它们:

char encrypted[strlen(baseStr) + 1] = {'\0'};
char decrypted[strlen(baseStr) + 1] = {'\0'};

编辑

根据user3386109,在声明 VLA 时不允许提供初始化程序。因此,您应该执行(例如)以下操作:

memset(encrypted, '\0', sizeof(encrypted));
memset(decrypted, '\0', sizeof(decrypted));

【讨论】:

  • 在声明 VLA 时不允许提供初始化程序。
  • @user3386109 感谢您的洞察力。
  • 如果我写 char encrypted[strlen(baseStr) + 1] = {'\0'};我得到这个错误 test.c:16:5: error: variable-sized object may not be initialized char encrypted[strlen(baseStr) + 1] = {'\0'}; ^~~~ test.c:16:44: 警告:数组初始值设定项中的多余元素 char encrypted[strlen(baseStr) + 1] = {'\0'};
  • @link23 是的,您需要从数组声明中删除 = {'\0'}。可变长度数组 (VLA) 不允许使用初始化程序。您要么需要按照此答案中的建议使用 memset 函数,要么按照 Schwern 的回答中的建议使用 output[len] = '\0';。
  • @link23 在 C 中,字符串是字符数组,带有一个特殊字符标记结束,'\0'。这就是strlen 和朋友在没有明确告知的情况下确定字符串长度的方式。
【解决方案2】:

PS:如果我更改 char baseStr[] 中的语句,输出是错误的,为什么?例如,如果 baseStr 的值为 Test,则输出将为:

Encrypted: *?�
Decrypted:Test�&

这是因为encryptDecrypt 使用strlen 来确定“字符串”的结尾。 strlen 依靠读取空字节来确定字符串的结尾。加密时,您有一个以空字符结尾的字符串。但是加密不会产生一个空终止的字符串,它会产生一个它不会空终止的字节流。

我们可以解决这个问题。

void encryptDecrypt(char *input, char *output) {
    char key[] = {'K', 'C', 'Q'}; //Can be any chars, and any size array

    size_t len = strlen(input);
    for(size_t i = 0; i < len; i++) {
        printf("%zu\n", i);
        output[i] = input[i] ^ key[i % (sizeof(key)/sizeof(char))];
    }
    output[len] = '\0';
}

我不知道这是否是一个好的修复。 strlen 可能对解密不安全,因为加密字符串的一个字节可能包含一个空字节。函数获取输入的长度可能更聪明。

void encryptDecrypt(char *input, char *output, size_t *strlen)

// We know baseStr is a string and can use strlen.
char encrypted[strlen(baseStr)];
encryptDecrypt(baseStr, encrypted, strlen(baseStr));

// The encrypted string is the same length as `baseStr`.
char decrypted[strlen(baseStr)];
encryptDecrypt(encrypted, decrypted, strlen(baseStr));

output[i] = input[i] ^ key[i % (sizeof(key)/sizeof(char))];

(sizeof(key)/sizeof(char)) 正在获取key 中的元素数量。 sizeof 产生字节数。所以(sizeof(key)/sizeof(char)) 通过将数组的大小除以每个元素的大小来生成元素的数量。

char 总是 1 个字节,我们可以去掉 sizeof(char) 让事情变得简单一些。

output[i] = input[i] ^ key[i % sizeof(key)];

这是对input 的每个字符与key 的每个字符进行异或。因为key和input的长度可能不同,而key可能更短, it must wrap around and reuse the keyagain.%`是取模运算符,所以会除以余数。

  • 0 % 3 = 0
  • 1 % 3 = 1
  • 2 % 3 = 2
  • 3 % 3 = 0
  • 4 % 3 = 1

这也称为“时钟数学”。

这样input的第一个字符与key的第一个字符异或,第二个与第二个,第三个与第三个异或,但input的第四个字符异或第一个字符为key。

您可以通过打印 i 和键索引来查看此操作。

    for(size_t i = 0; i < len; i++) {
        printf("%zu\n", i);
        size_t ki = i % sizeof(key);
        printf("i = %zu, ki = %zu\n", i, ki);
        output[i] = input[i] ^ key[ki];
    }

【讨论】:

    【解决方案3】:

    为:

    output[i] = input[i] ^ key[i % (sizeof(key)/sizeof(char))];
    

    sizeof(key)/sizeof(char) 真的是获取列表中元素的数量:3/1

    I % numelemement 是获得 I/numelements 的剩余部分 - 所以在这种情况下,当你上升到 I 位置时,将是 0,1,2,0,1,2(在这种情况下)

    所以

    output_value = input_value XOR 键[余数]

    你不是 null 终止输入字符串,所以 strlen 调用与原始字符串不同。

    【讨论】:

      【解决方案4】:

      %s 格式说明符用于 C 风格的字符串。您不能将它用于任意字符数组。如您所见,它无法知道要打印多少个字符。

      那个代码很糟糕。它在其输入上调用strlen。所以它的输入必须是一个C风格的字符串。但它的输出不是 C 风格的字符串,它只是一个字符数组。缺乏对称性使得代码难以使用和理解。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-05-16
        • 1970-01-01
        • 2011-11-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-01
        • 2011-01-03
        相关资源
        最近更新 更多