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
for i=0 to 255 do
j= ( j+S[i]+T[i])mod256;
swap(S[i],S[j]);
3、產生密鑰流
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]); } <span style="white-space:pre"> </span>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
解密文件:我愛小兔子!
由於RC4算法加密是采用的xor,所以,一旦子密鑰序列出現了重復,密文就有可能被破解。關於如何破解xor加密,請參看Bruce Schneier的Applied Cryptography一書的1.4節Simple XOR,在此我就不細說了。那么,RC4算法生成的子密鑰序列是否會出現重復呢?經過我的測試,存在部分弱密鑰,使得子密鑰序列在不到100萬字節內就發生了完全的重復,如果是部分重復,則可能在不到10萬字節內就能發生重復,因此,推薦在使用RC4算法時,必須對加密密鑰進行測試,判斷其是否為弱密鑰。
RC4在線加密解密網址: http://encode.chahuo.com