Skip to main content

学习RC4及C语言手搓RC4算法

·708 words
CTF
Yalois
Author
Yalois
freedom

本文主要是了解RC4和清楚RC4步骤。重点是算法步骤。

了解RC4
#

RC4(Rivest Cipher 4)是一种流加密算法,由Ron Rivest在1987年设计。 它通过生成密钥流字节与明文进行异或操作来实现加密,它加解密使用相同的密钥,因此也属于对称加密算法。 RC4使用可变长度的密钥,长度范围为1到256个字节。

RC4算法主要代码是如何生成密钥流。 分为两步:

  1. 初始化阶段 KSA(Key-Scheduling Algorithm)
  2. 密钥流生成阶段 PRGA (pseudo-random generation algorithm)

KSA主要步骤如下:

  1. 初始化 S 盒:创建一个256长度的数组S,初始化为S[i] = i
  2. 初始化临时数组 T进行密钥填充:如果RC4密钥(key)长度为key_len,密钥重复填充到长度为 256 的数组 T 中, T[i] = key[ i % key_len]
  3. 打乱 S 盒:使用密钥对 S 盒进行置换。 伪代码:
for i from 0 to 255:
    S[i] = i
    T[i] = key[i % key_len] 

j = 0
for i from 0 to 255:
    j = (j + S[i] + T[i]) % 256
    swap(S[i], S[j])

PRGA主要步骤如下: 密钥流生成阶段的目的是生成伪随机的密钥流字节,用于与明文或密文进行异或操作。 伪代码如下:

i = 0, j = 0
while (需要生成密钥流字节长度):
    i = (i + 1) % 256
    j = (j + S[i]) % 256
    swap(S[i], S[j])
    k = S[(S[i] + S[j]) % 256]
    输出 k

k就是密钥流字节。这是一个字节。这个字节用于和明文异或。

我生成一个数组keystream存储密钥流字节,这个keystream应该是和明文长度一致的。加解密的时候按字节进行异或。

我写的简单代码。

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

// 将字符数组转换为十六进制格式并打印
void charArrayToHex(const unsigned char *array, int length) {
    for (int i = 0; i < length; i++) {
        printf("%02X ", array[i]);
    }
    printf("\n");
}

int main() {
    // 密钥和明文
    unsigned char key[] = "secret";
    unsigned char plaintext[] = "flag{Congratulation!}";

    // 计算密钥和明文的长度
    int key_len = strlen((char *)key);
    int plaintext_len = strlen((char *)plaintext);

    // 输出密钥和明文信息
    printf("加密明文: %s\n", plaintext);
    printf("密钥: %s\n", key);
    printf("密钥长度: %d\n", key_len);
    printf("明文长度: %d\n", plaintext_len);

    // 初始化变量
    unsigned char ciphertext[256] = {0}; // 密文
    unsigned char decryptedtext[256] = {0}; // 解密后的明文
    unsigned char S[256]; // S盒
    unsigned char T[256]; // 临时数组
    unsigned char keystream[256] = {0}; // 密钥流

    // KSA阶段(Key-Scheduling Algorithm)
    printf("KSA阶段...\n");
    for (int i = 0; i < 256; i++) {
        S[i] = i; // 初始化S盒
        T[i] = key[i % key_len]; // 用密钥填充T数组
    }

    int j = 0;
    for (int i = 0; i < 256; i++) {
        j = (j + S[i] + T[i]) % 256; // 打乱S盒
        unsigned char temp = S[i];
        S[i] = S[j];
        S[j] = temp;
    }
    printf("KSA阶段完成!\n");

    // PRGA阶段(Pseudo-Random Generation Algorithm)
    printf("PRGA阶段...\n");
    int i = 0;
    j = 0;
    for (int k = 0; k < plaintext_len; k++) {
        i = (i + 1) % 256;
        j = (j + S[i]) % 256;
        unsigned char temp = S[i];
        S[i] = S[j];
        S[j] = temp;
        keystream[k] = S[(S[i] + S[j]) % 256]; // 生成密钥流
    }
    printf("PRGA阶段完成!\n");

    // 加密阶段
    printf("加密阶段...\n");
    for (int k = 0; k < plaintext_len; k++) {
        ciphertext[k] = plaintext[k] ^ keystream[k]; // 异或操作加密
    }
    printf("加密完成! 密文: ");
    charArrayToHex(ciphertext, plaintext_len); // 输出密文的十六进制

    // 解密阶段
    printf("解密阶段...\n");
    for (int k = 0; k < plaintext_len; k++) {
        decryptedtext[k] = ciphertext[k] ^ keystream[k]; // 异或操作解密
    }
    printf("解密完成! 明文: %s\n", decryptedtext); // 输出解密后的明文
    printf("明文 HEX: ");
    charArrayToHex(decryptedtext, plaintext_len); // 输出明文的十六进制

    return 0;
}

image-20250203153841067

但是我这代码有个缺陷,keystream数组长度只有256。明文超过256应该就出毛病了。理论应该是和明文长度一致的。 可以不用keystream数组,直接在PRGA阶段对明文进行加解密。

我这份代码只是展示了最简单的流程,没有使用函数封装。

再来看一份DeepSeek的代码:

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

// RC4 上下文结构体
typedef struct {
    unsigned char S[256]; // S盒
    unsigned char T[256]; // 临时数组
    int i, j;             // 状态变量
} RC4_CTX;

// 初始化 RC4 上下文
void rc4_init(RC4_CTX *ctx, const unsigned char *key, int key_len) {
    int i, j = 0;

    // 初始化 S 盒
    for (i = 0; i < 256; i++) {
        ctx->S[i] = i;
        ctx->T[i] = key[i % key_len];
    }

    // 打乱 S 盒
    for (i = 0; i < 256; i++) {
        j = (j + ctx->S[i] + ctx->T[i]) % 256;
        // 交换 S[i] 和 S[j]
        unsigned char temp = ctx->S[i];
        ctx->S[i] = ctx->S[j];
        ctx->S[j] = temp;
    }

    // 初始化状态变量
    ctx->i = 0;
    ctx->j = 0;
}

// 生成密钥流字节
unsigned char rc4_keystream_byte(RC4_CTX *ctx) {
    ctx->i = (ctx->i + 1) % 256;
    ctx->j = (ctx->j + ctx->S[ctx->i]) % 256;

    // 交换 S[ctx->i] 和 S[ctx->j]
    unsigned char temp = ctx->S[ctx->i];
    ctx->S[ctx->i] = ctx->S[ctx->j];
    ctx->S[ctx->j] = temp;

    // 计算密钥流字节
    unsigned char k = ctx->S[(ctx->S[ctx->i] + ctx->S[ctx->j]) % 256];
    return k;
}

// RC4 加密/解密函数
void rc4_crypt(RC4_CTX *ctx, const unsigned char *input, unsigned char *output, int len) {
    for (int i = 0; i < len; i++) {
        // 生成密钥流字节并与输入异或
        output[i] = input[i] ^ rc4_keystream_byte(ctx);
    }
}

int main() {
    // 示例密钥和明文
    unsigned char key[] = "secret_key";
    unsigned char plaintext[] = "Hello, RC4!";
    int len = strlen((char *)plaintext);

    // 加密后的密文和解密后的明文
    unsigned char ciphertext[256];
    unsigned char decryptedtext[256];

    // 初始化 RC4 上下文
    RC4_CTX ctx;
    rc4_init(&ctx, key, strlen((char *)key));

    // 加密
    rc4_crypt(&ctx, plaintext, ciphertext, len);
    printf("Ciphertext: ");
    for (int i = 0; i < len; i++) {
        printf("%02x ", ciphertext[i]);
    }
    printf("\n");

    // 重新初始化 RC4 上下文(因为状态变量已经改变)
    rc4_init(&ctx, key, strlen((char *)key));

    // 解密
    rc4_crypt(&ctx, ciphertext, decryptedtext, len);
    decryptedtext[len] = '\0'; // 添加字符串结束符
    printf("Decrypted text: %s\n", decryptedtext);

    return 0;
}

文章有错误欢迎在评论区指正。 有疑问也可以在评论区讨论。

参考
#

DeepSeek https://www.cnblogs.com/shelmean/p/14281332.html