# RC4加密算法(學習筆記)
## 簡介
RC4是一種串流加密法,密鑰長度可變。其屬於對稱式加密,且加解密過程一樣。
## 原理
### 初始化
1. 初始化S盒,其中包含所有0到255的元素(S[i] = i)
2. KSA——用一個密鑰進行一系列操作交換S盒,以根據密鑰混淆S盒。(因為不難,且code比中文好理解,故不詳述)
注:密鑰可以為任意長度,RC4會重複使用,直到將長度擴展到256字節
### 加密
1. 偽隨機算法PRGA(i,j從零開始,重複以下動作len(plaintext)次)
- i = (i + 1) mod 256
- j = (j + S[i]) mod 256
- 交換S[i]和S[j]的值。
- 根據交換後的S,計算k = (S[i] + S[j]) mod 256。
- 選擇S[k]作為密鑰流的下一個位元組。
2. 密文=明文xor由S[k]組成的密鑰流
## 解密
由於使用一樣的密鑰經過KSA和PRGA後會完全得到一樣的S盒和一樣的密鑰流
且A xor B xor B = A
故加密和解密的過程完全一樣
## 安全性
1. 由於RC4生成的密鑰流可以不斷延長的,所以每段明文都是由不同的部分加密。這就避免了單純使用異或加密時,密鑰長度小於明文的安全性問題。
2. 密鑰在經過密鑰調度算法(KSA)和偽隨機生成算法(PRGA)後,從密鑰流逆向反推密鑰變得極度困難,這也保證了安全性。
3. 但RC4仍有許多安全問題,現已逐漸被淘汰使用其他算法。例如AES(詳見[AES&伽羅瓦域/有限域](/2uEV2ydvTK6WjrcYm1DZ1A))
## 實作
```cpp=
#include<bits/stdc++.h>
using namespace std;
string key;
string plaintext;
string ciphertext;
unsigned char K[256];//一定要unsigned,範圍才是0~255
unsigned char S[256];
void KSA(){
for(int i = 0; i < 256; i++){//初始化S盒
S[i] = i;
}
for(int i = 0; i < 256; i++){//擴展密鑰
K[i] = key[i % key.size()];
}
int j = 0;
for(int i = 0; i < 256; i++){//置換,打亂S表
j = (j + S[i] + K[i]) % 256;
swap(S[i],S[j]);
}
return;
}
void PRGA_XOR(){//通過偽隨機算法PRGA
int i = 0, j = 0;
for(int p = 0; p < ciphertext.size(); p++){
i = (i + 1) % 256;
j = (j + S[i]) % 256;
swap(S[i], S[j]);
unsigned char k = (S[i] + S[j]) % 256;
ciphertext[p] = plaintext[p] ^ S[k];
}
return;
}
int main(){
cout << "請輸入明文(密文)\n";
cin >> plaintext;
cout << "請輸入密鑰\n";
cin >> key;
KSA();
ciphertext.resize(plaintext.size());
PRGA_XOR();
cout << ciphertext << '\n';
return 0;
}
```
### 加入Base64
寫完後我發現加密完後字元陣列裡面會出現超出ASCII碼規範範圍外的數字,會導致輸出的字元為亂碼,我認為這會造成使用和解密上的麻煩所以,我就決定其進行Base64編碼。加入Base64編碼後還可以讓輸入也更自由(不用再被ASCII碼限制)
>詳見:[Base64編碼](/Zs6Ltgm8QZ-LhbosB85C_Q)
```cpp=
#include<bits/stdc++.h>
using namespace std;
//--------------Base64---------------------
int sBase64[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
};
string Base64_encode(string str){
int len = str.size();
string res;
res.resize((len + 2) / 3 * 4);
int j = 0;
for(int i = 0; i < len / 3 * 3;i += 3){
int bits = ((str[i] & 0xff) << 16) | ((str[i+1] & 0xff) << 8) | (str[i+2] & 0xff);
res[j++] = sBase64[(bits >> 18) & 0x3f];
res[j++] = sBase64[(bits >> 12) & 0x3f];
res[j++] = sBase64[(bits >> 6) & 0x3f];
res[j++] = sBase64[bits & 0x3f];
}
if(len % 3 == 1){
int bits = (str[len - 1] & 0xff) << 4;
res[j++] = sBase64[(bits >> 6) & 0x3f];
res[j++] = sBase64[bits & 0x3f];
res[j++] = '=';
res[j] = '=';
}
else if(len % 3 == 2){
int bits = (str[len - 2] & 0xff) << 10 | (str[len - 1] & 0xff) << 2;
res[j++] = sBase64[(bits >> 12) & 0x3f];
res[j++] = sBase64[(bits >> 6) & 0x3f];
res[j++] = sBase64[bits & 0x3f];
res[j] = '=';
}
return res;
}
unsigned char BaseValue(char c){
if(c > 'a' - 1){
return c - 'a' + 26;
}
else if(c > 'A' - 1){
return c - 'A';
}
else if(c > '0' - 1){
return c - '0' + 52;
}
else if(c == '+'){
return 62;
}
else if(c == '/'){
return 63;
}
return -1;
}
string Base64_decode(string str){
int len = str.size();
string res;
for(int i = 0; i < len - 4; i+=4){
res.push_back((BaseValue(str[i]) << 2) | (BaseValue(str[i+1]) >> 4));
res.push_back((BaseValue(str[i+1]) << 4) | (BaseValue(str[i+2]) >> 2));
res.push_back((BaseValue(str[i+2]) << 6)| (BaseValue(str[i+3])));
}
res.push_back((BaseValue(str[len-4]) << 2) | (BaseValue(str[len-3]) >> 4));
if(str[len-2] != '='){
res.push_back((BaseValue(str[len-3]) << 4) | (BaseValue(str[len-2]) >> 2));
}
if(str[len-1] != '='){
res.push_back((BaseValue(str[len-2]) << 6) | (BaseValue(str[len-1])));
}
return res;
}
//--------------Base64---------------------
//---------------RC4-----------------------
string ciphertext;
unsigned char K[256];//一定要unsigned,範圍才是0~255
unsigned char S[256];
void KSA(string key){
for(int i = 0; i < 256; i++){//初始化S盒
S[i] = i;
}
for(int i = 0; i < 256; i++){//擴展密鑰
K[i] = key[i % key.size()];
}
int j = 0;
for(int i = 0; i < 256; i++){//置換,打亂S表
j = (j + S[i] + K[i]) % 256;
swap(S[i],S[j]);
}
return;
}
void PRGA_XOR(string plaintext){
int i = 0, j = 0;
for(int p = 0; p < plaintext.size(); p++){
i = (i + 1) % 256;
j = (j + S[i]) % 256;
swap(S[i], S[j]);
unsigned char k = (S[i] + S[j]) % 256;
ciphertext[p] = plaintext[p] ^ S[k];
}
return;
}
bool isASCII(string str){
for(int i = 0; i < str.size(); i++){
if(!isprint(str[i])){
return 0;
}
}
return 1;
}
//---------------RC4-----------------------
int main(){
string key;
string text;
cout << "注:RC4為對稱為對稱加密算法,加解密使用同一組密鑰" << '\n';
//cout << "注:RC4加密不支援string輸出,容易顯示亂碼(解密支援)" << '\n';
while(1){
cout << "要進行加密請輸入0,解密請輸入1" << '\n';
int flag1;cin >> flag1;
if(!flag1){//加密
cout << "輸入的明文以字節儲存(就是常見的1byte儲存一個)的請輸入0"<<'\n';
cout << "輸入的明文以Base64編碼的請輸入1" << '\n';
int flag2; cin >> flag2;
cin.ignore();
cout << "請輸入明文\n";
getline(cin, text);
if(flag2){
text = Base64_decode(text);
}
}
if(flag1){//解密
cout << "請輸入Base64編碼的密文" << '\n';
cin.ignore();
getline(cin,text);
text = Base64_decode(text);
}
cout << "請輸入密鑰(密鑰以字節儲存處理)\n";
getline(cin, key);
KSA(key);
ciphertext.clear();
ciphertext.resize(text.size());
PRGA_XOR(text);
if(!flag1){
cout << "密文的Base64編碼:" << Base64_encode(ciphertext) << '\n';
}
if(flag1){
cout << "明文為:" << ciphertext;
cout.clear();
cout << '\n';
cout << "明文的Base64編碼:" << Base64_encode(ciphertext) << '\n';
}
cout << "-------------" << '\n';
}
return 0;
}
```