RC4算法对文件进行加密解密
在密码学中,RC4(来自Rivest Cipher 4的缩写)是一种流加密算法,密钥长度可变。它加解密使用相同的密钥,因此也属于对称加密算法。RC4是有线等效加密(WEP)中采用的加密算法,也曾经是TLS可采用的算法之一。
RC4序列密码是美国RSA数据安全公司设计的一种序列密码。其实最开始这家公司并没有公布RC4算法的设计细节,在人们已经通过逆向分析得到了算法之后,在97年RSA公司才公布了RC4密码算法。
RC4算法的优点就是算法简单、高效,适合软件实现。
RC4算法的加密解密流程
RC4加解密相当于是对合运算,所以只要输入密钥相同,那么执行相同的操作即可对数据实现加密和解密。
而RC4和基于以为寄存器的序列密码不同,它是基于非线性数据表变换的序列密码,它以一个足够大的数据表为基础,对表进行非线性变化,从而产生非线性的密钥序列。
基本流程如下:
首先,RC4取一个256字节构成的S表,并引入一个256字节的辅助表R
- 对S表进行线性填充
即初始化S表,另,S[n] = n; (0<=n<=255)
for(int i = 0;i < ROOM;i++)//初始化S表
S[i] = i;
- 用种子密钥填充R表,如果种子密钥的长度小于R表的长度(256),则依次重复填充,直至将R表填满
其实依次重复填充的弊端十分明显:
例如用户输入了密钥a,那么重复填充之后R表中全部为a
而用户输了密钥aa,那么重复填充之后R表也是全部为a
这样显然不是一个好的结果
- 还有一种做法是做Padding,即若密钥长度小于R表长度时,用固定格式的 padding来填充剩下的部分。
- 用R表对S表进行随机化处理
其操作算法如下:
①J = 0;
②对于I = 0 : 255
J = J + S[i] + R[j] mod 256;
swap(S[i],S[j]);//交换S[i]与S[j]
代码实现:
void random(unsigned char *S,std::string R,int length)//随机化S表 {
int i;
unsigned char temp;
unsigned char j = 0;
//R表长度肯定小于等于256,注意模其长度使得不超过其下标并达到重复效果
for( i = 0 ; i < ROOM ; i++ ){
j = j + S[i] + R[i % length];
temp = S[j];
S[j] = S[i];
S[i] = temp;
}
}
- 加密解密核心功能
生成了随机化S表之后,就可以对数据执行加密或解密算法了。
RC4在加密解密时可以看成一个有限状态自动机,通过不断产生新的状态来产生出密钥字节。
RC4的下一状态函数定义如下:
①I = 0 ,J = 0;//初始化状态
②I = I + 1 mod 256;
③J = J + S[i] mod 256
④swap(S[i],S[j]);//交换S[i]与S[j]
而RC4的输出函数定义为:
①h = S[i] + S[j] mod 256;
②k = S[h];
输出函数的输出k即产生出的密钥字节,让RC4有限状态自动机一个一个运转,便输出密钥字节序列。
在加密时,将密钥字节k与明文字节模二加便完成了加密,解密时将密钥字节k与密文字节模二加就完成了解密。
- 代码实现:
for( num = 0 , i = 0 , j = 0 ; num < size ; num++ ) //逐位生成序列
{
temp = S[i];
j = j + temp;
S[i] = S[j];
S[j] = temp;
temp = temp + S[i];
data[num] ^= S[temp];//data即加密或解密出的结果
i += 1;
}
总结
通过以上流程,RC4算法就基本完成了,而我们对一些功能和流程进行稍许加工,例如读取文件大小,满足用户加密解密的流程控制等。
最后一个小demo如下:
#include <string>
#include <stdlib.h>
#include <iostream>
#include <windows.h>
#define ROOM 256
void random(unsigned char *S,std::string R,int length);//随机化S表
bool Encryption(unsigned char *S,long size,char *file_name,int mode);
//加解密文件 因为是对合运算,所以只需要一个函数,用mode控制加密还是解密
long file_size(char* filename); //读取文件长度
int main (int argc, char *argv[])
{
int Option; //功能选择
unsigned char S[ROOM]; //S核心表
long file_length; //文件字节大小
char R_temp[ROOM + 1]; //R表的备份表,预留一个'\0'
std::string R; //R表
while(1)
{
R.clear(); //清空数据
file_length = 0;
R_temp[0] = '\0';
Option = -1;
std::cout << "Welcome! Please choose the Option below:" << std::endl;//模式选择
std::cout << "input 1:encrption;" << std::endl;
std::cout << "input 2:decrption;" << std::endl;
std::cout << "input 0:exit." << std::endl;
std::cin >> Option;
while( std::cin.fail() )//输入出错处理
{
std::cin.clear(); //复位标志位
std::cout << "数据类型不对,请重新输入:";
fflush(stdin); //清空缓冲区
std::cin >> Option;
}
if(Option == 0) break; //0则直接退出
else if(Option != 1 && Option != 2)
{
std::cout << "输入选择有误,请重新输入:" << std::endl; //其他数字
continue;
}
for(int i = 0;i < ROOM;i++)//初始化S表
S[i] = i;
std::cout << "请输入您的种子密钥" <<std::endl;
fflush(stdin); //清空之前的输入缓冲区
std::cin.get(R_temp,ROOM); //保证忽略输入中的空格
R = R_temp;
int R_length = R.length(); //定义length为R表中字符个数
random(S,R,R_length); //随机化S表
if(Option == 1)
{
char file_name[16]; //定义加密文件名
std::cout << "请输入您要加密的文件" << std::endl;
fflush(stdin); //清空之前的输入缓冲区
std::cin.get(file_name,16); //保证忽略输入中的空格
file_length = file_size(file_name);//计算文件字节大小
if(Encryption(S,file_length,file_name,Option)) //加密成功
{
std::cout << "退出请输入0,输入其他将回到初始界面" << std::endl;
std::cin >> Option;
}
else //加密失败,3s后返回主页面
{
std::cout << "加密失败!" << std::endl;
for( int temp = 3 ; temp > 0 ; temp-- )
{
std::cout << temp << "s后将回到初始界面" << std::endl;
Sleep(1000);
}
}
}
else if(Option == 2)
{
char file_name[16]; //定义解密文件名
std::cout << "请输入您要解密的文件" <<std::endl;
fflush(stdin); //清空之前的输入缓冲区
std::cin.get(file_name,16); //保证忽略输入中的空格
file_length = file_size(file_name);//计算文件字节大小
if(Encryption(S,file_length,file_name,Option)) //解密成功
{
std::cout << "退出请输入0,输入其他将回到初始界面" << std::endl;
std::cin >> Option;
}
else //解密失败,3s后返回主页面
{
std::cout << "解密失败!" << std::endl;
for( int temp = 3 ; temp > 0 ; temp-- )
{
std::cout << temp << "s后将回到初始界面" << std::endl;
Sleep(1000);
}
}
}
if(Option == 0)
break;
}
std::cout << "Thanks" << std::endl;
return 0;
}
void random(unsigned char *S,std::string R,int length)//随机化S表
{
int i;
unsigned char temp;
unsigned char j = 0;
//R表长度肯定小于等于256,注意模其长度使得不超过其下标并达到重复效果
for( i = 0 ; i < ROOM ; i++ )
{
j = j + S[i] + R[i % length];
temp = S[j];
S[j] = S[i];
S[i] = temp;
}
}
bool Encryption(unsigned char *S,long size,char *file_name,int mode)//加解密文件 因为是对合运算,所以只需要一个函数,用mode控制加密还是解密
{
FILE *pr; //定义读文件指针pr
pr = fopen(file_name,"rb"); //只读
if(pr == NULL) //如果打开文件错误,则退出
{
std::cout << "用户输入文件打开失败!" << std::endl;
return false;
}
FILE *pw; //定义写文件指针pw
unsigned char *data; //存储原始文件数据,并在运算过程中充当输出序列与原序列的异或结果
unsigned char i,j,temp;
long num;
data = (unsigned char *)malloc(size * sizeof(unsigned char));//分配内存
if(mode == 1)
pw = fopen("encryption.txt","wb"); //模式为只写且刷新文件
else
pw = fopen("decryption.txt","wb"); //模式为只写且刷新文件
if(pw == NULL) //如果打开文件错误,则退出
{
std::cout << "输出文件时出错!" << std::endl;
return false;
}
fread(data,sizeof(char),size,pr);//读取二进制流(需要加/解密的文件)
for( num = 0 , i = 0 , j = 0 ; num < size ; num++ ) //逐位生成序列
{
temp = S[i];
j = j + temp;
S[i] = S[j];
S[j] = temp;
temp = temp + S[i];
data[num] ^= S[temp];
i += 1;
}
if(mode == 1)
std::cout << "功能完成,生成encryption.txt文件" << std::endl;
else
std::cout << "功能完成,生成decryption.txt文件" << std::endl;
fwrite(data,sizeof(unsigned char),size,pw);//写入二进制流
fclose(pw);//关闭文件流
fclose(pr);
free(data);//释放内存
return true;
}
long file_size(char *filename) //读取文件长度
{
FILE *fp = fopen(filename,"r");
if(!fp)
return -1;
fseek(fp,0L,SEEK_END); //利用fseek函数将指针定位在文件结尾的位置
long size = ftell(fp); //利用ftell函数返回指针相对于文件开头的位置,以字节计算
fclose(fp);
return size;
}