这个不懂是不是字节码的问题,有些编码一个汉字是三个字节,有的又是两个,数据编码的转换有点复杂,用C语言可能会好点
因为编码后的汉字是多字符的。UTF-8 的话是可以通过第一个字节的高位来判断长度,一个字符可以用多个字节来表示。
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("中"[0]));
- 可以引入
string.h
,这样就能直接使用 strlen
函数了。
i
(不包括初始化时) 和 j
的类型其实可以使用 uint8_t
,超过 255
会自动回到 0。
- 初始化 S 表的时候的
i
不可以,因为要表示 256
。
- 从 C99 标准开始(即支持使用
//
注释的版本开始),不需要在函数头定义所有需要的变量了,可以在需要的地方直接插入。
另外 RC4 如果需要分段加解密,则需要记录 i
和 j
的值。例如加解密一个大文件,可以分批次读取到缓冲区操作。
最后,如果真的有加解密的使用需求,建议使用成熟的、测试过的库,例如 openssl
。
修改后的代码:
#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[256];
};
// 初始化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] = i;
}
uint8_t j = {0};
for (int i = 0; i < 256; i++) {
j = j + ctx->S[i] + key[i % key_len];
Swap(&ctx->S[i], &ctx->S[j]);
}
}
// 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[ctx->i];
Swap(&ctx->S[ctx->i], &ctx->S[ctx->j]);
// key 就是本轮生成的字节。
uint8_t key = ctx->S[ctx->i] + ctx->S[ctx->j];
// 异或处理
Str[k] ^= 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[i + j]);
else
printf(" ");
printf(" ");
for (j = 0; j < 16; j++)
if (i + j < buflen)
printf("%c", isprint(buf[i + j]) ? buf[i + j] : '.');
printf("\n");
}
}
int main() {
struct rc4_ctx_t rc4 = {};
const char* key = "hello world";
char message[512] = {"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
方法渲染效果如下: