抓包后无意间看到了 rc4coded 的字样,很是好奇,于是上网查找,得知是一个很有名气的加密算法。在学习算法原理的时候,我发现很多资料都是相仿的,表示本菜鸡很不理解算法过程。最终在wiki找到了出处,于是在此做下笔记。
首先摘一段wiki上对rc4的描述:
RC4 generates a pseudorandom stream of bits (a keystream). As with any stream cipher, these can be used for encryption by combining it with the plaintext using bit-wise exclusive-or; decryption is performed the same way (since exclusive-or with given data is an involution). This is similar to the one-time pad except that generated pseudorandom bits, rather than a prepared stream, are used.
大致翻译是这样的:该算法首先生成一串伪随机数(keystream),这样就可以像其他的流密码一样,将keystream和明文通过亦或操作来加密。解码也是一样,因为亦或操作是一种复归操作(involution),即做两次又变成原来的形态。除了运用生成的伪随机序列而不是一个准备好的流之外,该算法和 OTP 算法一样。
参照其他博客的方法,首先介绍下面要是用的一些变量,以方便理解。
根据 wiki 的介绍:
我们在自己设一个 T 装伪随机数序列。
有了这些准备,开始介绍算法原理:
The permutation is initialized with a variable length key, typically between 40 and 2048 bits, using the key-scheduling algorithm (KSA). Once this has been completed, the stream of bits is generated using the pseudo-random generation algorithm (PRGA).
大致翻译:序列 S 用一个可变长度的 key 来初始化,一般 key 的长度在 40-2048bits (5Byte-256Byte)之间。初始化使用 KSA 算法。一旦完成这一步,用 PRGA 算法来用这一段序列生成伪随机数序列。
而有了伪随机数序列后,剩下要做的就是将明文和该序列以字节为单位执行亦或操作(生成的伪随机数序列的长度同明文长度相同)。所以 rc4 的最大难点就在于 KSA 和 PRGA 这两个算法了。我们来看一下:
The key-scheduling algorithm is used to initialize the permutation in the array "S". "keylength" is defined as the number of bytes in the key and can be in the range 1 ≤ keylength ≤ 256, typically between 5 and 16, corresponding to a key length of 40 – 128 bits. First, the array "S" is initialized to the identity permutation. S is then processed for 256 iterations in a similar way to the main PRGA, but also mixes in bytes of the key at the same time.
大致翻译:该算法用来初始化 S 中的序列。keylength 用来表示 key 的字节长度,改长度可以在 1-256 之间取值,通常在5-16之间,对应 key length 的 40-128bits。首先,S 被初始化为有序序列(0-255)。然后,用和 PRGA 主题类似的循环处理 S,但 key 的内容也将加入处理(这一段有点难于理解,大致的过程是将 key 中的内容和 S 中的内容相加对以256取模,以其结果作为数组索引调换 S 中的元素,目的是将有序的 S 打乱)。
下面是算法的伪代码:
for i from 0 to 255
S[i] := i
endfor
j := 0
for i from 0 to 255
j := (j + S[i] + key[i mod keylength]) mod 256
swap values of S[i] and S[j]
endfor
For as many iterations as are needed, the PRGA modifies the state and outputs a byte of the keystream. In each iteration, the PRGA:
Each element of S is swapped with another element at least once every 256 iterations.
这一段就不做翻译了,总的方向是通过算法将 S 的每个元素至少移动一次,即再次打乱 S 序列。再伪随机抽取其中的元素,组成伪随机数序列 T(原文是直接将提取的元素 k 作为 keystream[i] 直接同明文对应的字节进行亦或)。
伪代码:
i := 0
j := 0
while GeneratingOutput:
i := (i + 1) mod 256
j := (j + S[i]) mod 256
swap values of S[i] and S[j]
K := S[(S[i] + S[j]) mod 256]
output K
endwhile
算法中最关键的两个部分完成,下面就是将明文和 T 以字节为单位亦或了。由于亦或的复归性质,解码的原理同加密原理相同,不在讨论。
下面贴上自己的代码(C++实现),其中 S 用 state_arr 表示,T 用 temp_arr 表示,keystream 存放伪随机数序列,key 为密钥,content 为明文或密文。
用户需要在自己的源码中加入 #include "RC4.h" 来包含 rc4 类的声明和一些其他必要元素。通过默认初始化,用户需要通过 get_info() 成员添加密钥和待处理文本,再调用 rc4_encrypt/rc4decrypt() 进行加密解密。使用传值初始化的用户可以直接加密解密。
RC4.h
#ifndef RC4
#define RC4
#define SIZE_MAX_RC4 256
using uchar_t=unsigned char;
class rc4;
#include
#include
class rc4{
friend void swap_rc4(uchar_t&,uchar_t&);
public:
rc4()=default;
rc4(const std::string&,const std::string&);
void get_info(const std::string&,const std::string&);
std::string rc4_encrypt();
std::string rc4_decrypt();
//void debug();
private:
uchar_t state_arr[SIZE_MAX_RC4];
uchar_t temp_arr[SIZE_MAX_RC4];
std::string keystream;
std::string key;
std::string content;
void initialize();
void spawn_stream();
};
void swap_rc4(uchar_t&,uchar_t&);
#include "RC4.cpp"
#endif
RC4.cpp
void swap_rc4(uchar_t &a,uchar_t &b){
uchar_t temp=a;
a=b;
b=temp;
}
void rc4::initialize(){
int len=key.length();
for(int i=0;i