RC4加密演算法的原理及實現
RC4於1987年提出,和DES演算法一樣,是一種對稱加密演算法,也就是說使用的金鑰為單鑰(或稱為私鑰)。但不同於DES的是,RC4不是對明文進行分組處理,而是位元組流的方式依次加密明文中的每一個位元組,解密的時候也是依次對密文中的每一個位元組進行解密。
RC4演算法的特點是演算法簡單,執行速度快,而且金鑰長度是可變的,可變範圍為1-256位元組(8-2048位元),在如今技術支援的前提下,當金鑰長度為128位元時,用暴力法搜尋金鑰已經不太可行,所以可以預見RC4的金鑰範圍任然可以在今後相當長的時間裡抵禦暴力搜尋金鑰的攻擊。實際上,如今也沒有找到對於128bit金鑰長度的RC4加密演算法的有效攻擊方法。
在介紹RC4演算法原理之前,先看看演算法中的幾個關鍵變數:
1、金鑰流:RC4演算法的關鍵是根據明文和金鑰生成相應的金鑰流,金鑰流的長度和明文的長度是對應的,也就是說明文的長度是500位元組,那麼金鑰流也是500位元組。當然,加密生成的密文也是500位元組,因為密文第i位元組=明文第i位元組^金鑰流第i位元組;
2、狀態向量S:長度為256,S[0],S[1].....S[255]。每個單元都是一個位元組,演算法執行的任何時候,S都包括0-255的8位元數的排列組合,只不過值的位置發生了變換;
3、臨時向量T:長度也為256,每個單元也是一個位元組。如果金鑰的長度是256位元組,就直接把金鑰的值賦給T,否則,輪轉地將金鑰的每個位元組賦給T;
4、金鑰K:長度為1-256位元組,注意金鑰的長度keylen與明文長度、金鑰流的長度沒有必然關係,通常金鑰的長度趣味16位元組(128位元)。
RC4的原理分為三步:
1、初始化S和T
for i=0 to 255 do
S[i]=i;
T[i]=K[ imodkeylen ];
2、初始排列S
j=0;
for i=0 to 255 do
j= ( j+S[i]+T[i])mod256;
swap(S[i],S[j]);
3、產生金鑰流
i,j=0;
for r=0 to len do //r為明文長度,r位元組
i=(i+1) mod 256;
j=(j+S[i])mod 256;
swap(S[i],S[j]);
t=(S[i]+S[j])mod 256;
k[r]=S[t];
下面給出RC4加密解密的C++實現:
加密類:
/*
加密類
*/
class RC4 {
public:
/*
建構函式,引數為金鑰長度
*/
RC4(int kl):keylen(kl) {
srand((unsigned)time(NULL));
for(int i=0;i<kl;++i){ //隨機生產長度為keylen位元組的金鑰
int tmp=rand()%256;
K.push_back(char(tmp));
}
}
/*
由明文產生密文
*/
void encryption(const string &,const string &,const string &);
private:
unsigned char S[256]; //狀態向量,共256位元組
unsigned char T[256]; //臨時向量,共256位元組
int keylen; //金鑰長度,keylen個位元組,取值範圍為1-256
vector<char> K; //可變長度金鑰
vector<char> k; //金鑰流
/*
初始化狀態向量S和臨時向量T,供keyStream方法呼叫
*/
void initial() {
for(int i=0;i<256;++i){
S[i]=i;
T[i]=K[i%keylen];
}
}
/*
初始排列狀態向量S,供keyStream方法呼叫
*/
void rangeS() {
int j=0;
for(int i=0;i<256;++i){
j=(j+S[i]+T[i])%256;
//cout<<"j="<<j<<endl;
S[i]=S[i]+S[j];
S[j]=S[i]-S[j];
S[i]=S[i]-S[j];
}
}
/*
生成金鑰流
len:明文為len個位元組
*/
void keyStream(int len);
};
void RC4::keyStream(int len) {
initial();
rangeS();
int i=0,j=0,t;
while(len--){
i=(i+1)%256;
j=(j+S[i])%256;
S[i]=S[i]+S[j];
S[j]=S[i]-S[j];
S[i]=S[i]-S[j];
t=(S[i]+S[j])%256;
k.push_back(S[t]);
}
}
void RC4::encryption(const string &plaintext,const string &ks,const string &ciphertext) {
ifstream in;
ofstream out,outks;
in.open(plaintext);
//獲取輸入流的長度
in.seekg(0,ios::end);
int lenFile=in.tellg();
in.seekg(0, ios::beg);
//生產金鑰流
keyStream(lenFile);
outks.open(ks);
for(int i=0;i<lenFile;++i){
outks<<(k[i]);
}
outks.close();
//明文內容讀入bits中
unsigned char *bits=new unsigned char[lenFile];
in.read((char *)bits,lenFile);
in.close();
out.open(ciphertext);
//將明文按位元組依次與金鑰流異或後輸出到密文檔案中
for(int i=0;i<lenFile;++i){
out<<(unsigned char)(bits[i]^k[i]);
}
out.close();
delete []bits;
}
解密類:
/*
解密類
*/
class RC4_decryption{
public:
/*
建構函式,引數為金鑰流檔案和密文檔案
*/
RC4_decryption(const string ks,const string ct):keystream(ks),ciphertext(ct) {}
/*
解密方法,引數為解密檔名
*/
void decryption(const string &);
private:
string ciphertext,keystream;
};
void RC4_decryption::decryption(const string &res){
ifstream inks,incp;
ofstream out;
inks.open(keystream);
incp.open(ciphertext);
//計算密文長度
inks.seekg(0,ios::end);
const int lenFile=inks.tellg();
inks.seekg(0, ios::beg);
//讀入金鑰流
unsigned char *bitKey=new unsigned char[lenFile];
inks.read((char *)bitKey,lenFile);
inks.close();
//讀入密文
unsigned char *bitCip=new unsigned char[lenFile];
incp.read((char *)bitCip,lenFile);
incp.close();
//解密後結果輸出到解密檔案
out.open(res);
for(int i=0;i<lenFile;++i)
out<<(unsigned char)(bitKey[i]^bitCip[i]);
out.close();
}
程式實現時,需要注意的是,狀態向量陣列S和臨時向量陣列T的型別應設為unsigned char,而不是char。因為在一些機器下,將char預設做為signed char看待,在演算法中計算下標i,j的時候,會涉及char轉int,如果是signed的char,那麼將char的8位拷貝到int的低8位後,還會根據char的符號為,在int的高位補0或1。由於金鑰是隨機產生的,如果遇到金鑰的某個位元組的高位為1的話,那麼計算得到的陣列下標為負數,就會越界。
程式執行示例
main函式:
int main(){
RC4 rc4(16); //金鑰長16位元組
rc4.encryption("明文.txt","金鑰流.txt","密文.txt");
RC4_decryption decrypt("金鑰流.txt","密文.txt");
decrypt.decryption("解密檔案.txt");
}
明文:我愛小兔子!
密文:'柀L&t餥6洲
金鑰流:鎛膺嚬3屽u
解密檔案:我愛小兔子!