youzipii 发表于 2024-3-21 16:51

RC4-c语言实现

参考:网络安全实验——RC4的实现
#include<stdio.h>
//交换
void Swap(unsigned char *S1, unsigned char *S2) {
        unsigned char tmp = *S1;
        *S1 = *S2;
        *S2 = tmp;
}

//获取字符串长度
int my_strlen(unsigned char *S) {
        int i = 0;
        int res = 0;
        while(*S++ != '\0') {
                res += 1;
                i++;
        }
        return res;
}

//初始化S盒
void init_S_Box(unsigned char *S, unsigned char *Key, int len) {
        int i = 0, j = 0;
        unsigned char T;
        unsigned char tmp = 0;
        for (i = 0; i < 256; i++)
        {
                S = i;                       // S_box升序置为0~255
                T = Key; // T使用key来进行填充的
        }
        //初始置换
        for (i = 0; i<256; i++)
        {
                j = (j + S + T) % 256; // 取一个s+k+j的索引
                //交换S和S
                Swap(&S, &S);
        }
}

//加解密函数,因为是异或的方式所以加密也是解密
void rc4(unsigned char *S, unsigned char *Str, int len) {
        //PRGA(伪随机数生成)
        int i = 0, j = 0, k = 0, t = 0;
       
        for (k = 0; k < len; k++) {
                i = (i + 1) % 256;
                j = (j + S) % 256;
                Swap(&S, &S);
                t = (S + S) % 256;
                Str = Str ^ S; //以活加密
        }
       
}

int main(){
        //用无符号字符型,因为一个char的大小刚好是一字节,无符号是为了防止出现负数
        unsigned char S_box = {};
        unsigned char Key = { "hello world"};
        unsigned char Str = "hello world!";
        int len = my_strlen(Key);
        printf("*************原始信息*************\n");
        printf("message = %s\n", Str);
        printf("Key = %s \nKey_length = %d\n\n", Key, len);
        init_S_Box(S_box, Key, len);   //初始化
        printf("*************加密*************\n");
        rc4(S_box, Str, len);//加密
        printf("cipher = %s\n\n", Str);
        printf("*************解密*************\n");
        init_S_Box(S_box, Key, len);   //初始化
        rc4(S_box, Str, len);
        printf("message = %s\n\n", Str);

}
这个不懂是不是字节码的问题,有些编码一个汉字是三个字节,有的又是两个,数据编码的转换有点复杂,用C语言可能会好点

爱飞的猫 发表于 2024-3-22 08:49

本帖最后由 爱飞的猫 于 2024-3-22 08:51 编辑

> 这个不懂是不是字节码的问题,有些编码一个汉字是三个字节,有的又是两个,数据编码的转换有点复杂,用C语言可能会好点

因为编码后的汉字是多字符的。UTF-8 的话是可以通过第一个字节的高位来判断长度,一个字符可以用多个字节来表示。

```c
int utf8_chr_len(char utf8_chr) {
    // 最高位未设置,单字节长度
    if ((utf8_chr & 0x80) == 0) {
      return 1;
    }

    // 之后数高位有多少个 1 直到遇到 0。目前的标准,单字符最高 6 字节,即二进制的 "1111110?"
    int result = {1};
    for(int shifts = 6; shifts >= 1; shifts--) {
      if (utf8_chr & (1 << shifts)) {
            result ++;
      } else {
            break;
      }
    }
    return result;
}

// 测试案例,输出 1 和 3
printf("utf8_chr_len: %d, %d\n", utf8_chr_len('A'), utf8_chr_len("中"));
```

---

- 可以引入 `string.h`,这样就能直接使用 `strlen` 函数了。
- `i` (不包括初始化时) 和 `j` 的类型其实可以使用 `uint8_t`,超过 `255` 会自动回到 0。
- 初始化 S 表的时候的 `i` 不可以,因为要表示 `256`。
- 从 C99 标准开始(即支持使用 `//` 注释的版本开始),不需要在函数头定义所有需要的变量了,可以在需要的地方直接插入。

另外 RC4 如果需要分段加解密,则需要记录 `i` 和 `j` 的值。例如加解密一个大文件,可以分批次读取到缓冲区操作。

最后,如果真的有加解密的使用需求,建议使用成熟的、测试过的库,例如 `openssl`。

---

修改后的代码:

```c
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

// 交换
void Swap(uint8_t *S1, uint8_t *S2) {
    uint8_t tmp = *S1;
    *S1 = *S2;
    *S2 = tmp;
}

struct rc4_ctx_t {
    uint8_t i;
    uint8_t j;
    uint8_t S;
};

// 初始化S盒
void rc4_init(struct rc4_ctx_t *ctx, const uint8_t *key, int key_len) {
    ctx->i = 0;
    ctx->j = 0;
    for (int i = 0; i < 256; i++) {
      ctx->S = i;
    }

    uint8_t j = {0};
    for (int i = 0; i < 256; i++) {
      j = j + ctx->S + key;
      Swap(&ctx->S, &ctx->S);
    }
}

// RC4 随机数生成与加解密
void rc4_cipher(struct rc4_ctx_t *ctx, uint8_t *Str, int len) {
    for (int k = 0; k < len; k++) {
      ctx->i++;
      ctx->j += ctx->S;

      Swap(&ctx->S, &ctx->S);

      // key 就是本轮生成的字节。
      uint8_t key = ctx->S + ctx->S;

      // 异或处理
      Str ^= key;
    }
}

// 摘抄自 https://stackoverflow.com/a/29865
void hexdump(void *ptr, int buflen) {
    unsigned char *buf = (unsigned char *) ptr;
    int i, j;
    for (i = 0; i < buflen; i += 16) {
      printf("%06x: ", i);
      for (j = 0; j < 16; j++)
            if (i + j < buflen)
                printf("%02x ", buf);
            else
                printf("   ");
      printf(" ");
      for (j = 0; j < 16; j++)
            if (i + j < buflen)
                printf("%c", isprint(buf) ? buf : '.');
      printf("\n");
    }
}

int main() {
    struct rc4_ctx_t rc4 = {};
    const char* key = "hello world";
    char message = {"hello world!"};

    printf("*************原始信息*************\n");
    printf("message = %s\n", message);
    printf("Key = %s \n\n", key);

    printf("*************加密*************\n");
    rc4_init(&rc4, (const uint8_t*)key, strlen(key));
    rc4_cipher(&rc4, message, sizeof(message));
    hexdump(message, sizeof(message));
    printf("*************解密*************\n");
    rc4_init(&rc4, (const uint8_t*)key, strlen(key));
    rc4_cipher(&rc4, message, sizeof(message));
    hexdump(message, sizeof(message));
    printf("*************结束*************\n");
}
```

上文中的 `hexdump` 方法渲染效果如下:

![](https://imgsrc.baidu.com/forum/pic/item/2f738bd4b31c8701029aaddd617f9e2f0708ffd9.png)



youzipii 发表于 2024-3-23 22:51

爱飞的猫 发表于 2024-3-22 08:49
> 这个不懂是不是字节码的问题,有些编码一个汉字是三个字节,有的又是两个,数据编码的转换有点复杂, ...

感谢指点{:1_893:}
页: [1]
查看完整版本: RC4-c语言实现