简介:
我们都知道现在基本上所有的加密算法都是公开的,也就是在一个算法和密文是公开的环境中去寻找明文。由于算法的复杂度很高,这导致大部分算法在有限的时间内是找不到明文的。这对小文件加密无疑是一个很好的方法,但是对于大文件来说这会消耗大量的时间和CPU的性能。为了可以更加高效的完成对大文件的加密我们可以不公开算法,即自己重新定义算法对文件加密。由于算法是不公开的,攻击者只能获得密文因而破译的难度也大大提升了。下面会介绍到我们的加密算法和加密过程以及遇到的问题和解决方案。
问题
- 简单的加密肯定会被破解,所以即使算法不公开也不能很简单;
由于不公开算法带来了破译难度的增加,但是简单的加密算法还是很容易被破解。所以我们要做到算法不太简单的情况下而且效率要高的方法。 - 如果攻击者同时知道了明文和密文那么攻击难度也会下降;
由于采用的是简单的加密算法所以一旦攻击者同时获得明文和密文那么对之后的密文破解难度会大幅度下降。这里我们要做到即使攻击者同时获得明文密文也不会很容易破解或者说不可能破解。
DES加密算法
这里我们先要简单的介绍一下DES加密算法,因为我们之后会用到。当然如果你不了解该算法也是可以的。
算法介绍
DES英文全称Data Encryption Standard,是目前最为流行的加密算法之一。DES是对称加密算法,也就是说它需要使用同一个密钥来加密和解密。同时DES还是一种分组加密算法,该算法每次处理固定长度的数据段,称之为分组。DES分组的大小是64位,如果加密的数据长度不是64位的倍数,可以按照某种具体的规则来填充位。
Created with Raphaël 2.3.0 64比特明文 初始置换IP 轮1 轮2... 轮16 逆初始置换IP
这里原始密钥是需要进行密钥扩展,扩展16次每次都对应每一轮加密操作的新密钥。
- 初始置换
明文 M = m1m2…m64
IP(M) = m45m34…m4 - 初始逆置换IP1
IP-1 [IP(M)]=M - 16轮迭代
Li=Ri-1
Ri=Li-1(xor)F(Ri-1,Ki)
经过初始置换后,将64位明文分为左右两组,各32位,然后进行16轮的迭代。
- 扩展置换E
Ri=Li-1(xor)F(Ri-1,Ki)
作用是为了使输入的32bit数据扩展为48bit然后和子密钥Ki进行异或运算。
算法部分代码
- 二进制的位置进行转换
void Change_bit(bool *Data_out, int Num)
{
int i, j;
static bool Temp[8] = {0};
for (i = 0; i < Num / 8; i++)
{
Bitcopy(Temp, Data_out, Num / 8);
for (j = 0; j < Num / 8; j++)
{
Data_out[j] = Temp[Num / 8 - 1 - j];
}
Data_out += Num / 8;
}
}
- 置换算法
void TableReplace(bool *Data_out, bool *Data_in, const char *Table, int Num)
{
int i = 0;
static bool Temp[256] = {0};
for (i = 0; i < Num; i++)
{
Temp[i] = Data_in[Table[i] - 1];
}
Bitcopy(Data_out, Temp, Num);
}
- 执行异或操作
void Xor(bool *Message_out, bool *Message_in, int Num) //执行异或
{
int i;
for (i = 0; i < Num; i++)
{
Message_out[i] = Message_out[i] ^ Message_in[i];
}
}
- S盒变换
void S_change(bool *Data_out, bool *Data_in)
{
int i;
int r = 0, c = 0; // S盒的行和列;
for (i = 0; i < 8; i++, Data_in = Data_in + 6, Data_out = Data_out + 4)
{
r = Data_in[0] * 2 + Data_in[5] * 1;
c = Data_in[1] * 8 + Data_in[2] * 4 + Data_in[3] * 2 + Data_in[4] * 1;
ByteToBit(Data_out, &S_Box[i][r][c], 4);
}
}
- F函数
void F_change(bool Data_out[32], bool Data_in[48])
{
int i;
static bool Message_E[48] = {0}; //存放E置换的结果;
TableReplace(Message_E, Data_out, E_Table, 48); // E表置换
Xor(Message_E, Data_in, 48);
S_change(Data_out, Message_E); // S盒变换
TableReplace(Data_out, Data_out, P_Table, 32); // P置换
}
Y算法简介描述
由于加密算法不公开所以大家在这里看到的算法实现都是很简单的。我们先介绍算法的实现。
算法实现
考虑到如果只是对每一个字节进行加密也就是8位的算法这个是很容易被破解掉的。这里要考虑到基本所有的加密算法都会导致文件大小变大,所以我们要在尽量不让文件大小扩展的情况下加密文件。一下举两个例子:
- 如果加密算法只是对每一字节做相同的移动(即凯撒密码),虽然破解该算法的时间复杂度是线性的,但是我们只需要执行28=256就一定会有答案无论你的密码设计的有多复杂;
- 假设我们去做随机映射,那么通过上述第一种方法破解基本是不可能的,但是如果攻击者同时拥有一份明文盒密文那么破解的概率就很大或者说100%。
我们要避免上诉问题的出现就不能简单的只是对每一字节做移动或者说做随机移动。这里我们可以这样做:
- 找一个64位的密钥K
- 将明文补充为8字节大小的整数倍
- 将明文的每64位同密钥做异或处理
这样做可以避免凯撒密码遇到的问题(因为做的相当于随机映射),比如对于同一个字节B其操作后的结果可能有4个值所以每个字节并不是简单的移动。但是如果攻击者知道了一份明文和密文也还是可以破解的。这里我们引入随机数。
随机数的引入
我们知道通过上述加密算法每次加密的结果是一致的,这其实来讲是不太合适的,这种映射很容易被破解掉。所以我们要想一种方法做到相同的密文我出来的明文是不一样的。
如果每次加密前我们生成一个随机数R,对文件加密时我们让随机数也参与。可以通过先将随机数和我们的密钥做异或操作生成新的密钥去加密文件,这样每次密钥不一样那加密出来的密文也就不一样了。实现数学公式如下:
- Knewi = R Xor Ki
- Knewi(M)
DES的引入
通过引入随机数我们现在可以保证这个数据的安全了,但是解密的时候还是需要随机数的。这里我们将随机数保存到文件本身即加密后到文件大小会扩大16字节。这个扩大我们是可以接受的。
如果将随机数明文传输还是不安全的,所以这里我们可以通过上述提到的DES对称加密算法对我们的随机数加密。DES加密的密钥位Kdesi 。
整体算法实现思路
- 创建Y加密算法加密密钥Kyi
- 创建DES加密算法加密密钥Kdesi
- 生成64位随机数R
- 生成新的Y算法加密密钥Knewyi
Knewyi = R Xor Kyi - 用新的密钥加密明文M生成密文M‘
Knewyi(M) = M’ - 通过DES算法加密随机数R生R‘(十六进制数)
Kdesi(R) = R‘ - 合并密文M’和R‘
M’’ = M’ + R’ - 传输M‘’
这就是整体的加密算法实现过程。
部分代码实现
- 生成64位随机数
void get64bit_random(int *random)
{
int r[9];
getrand(r);
int num = 0;
int k = 0;
for (size_t i = k;; i += 4, num = 0)
{
int index = i % 7;
num += r[index] * pow(10, index % 3);
num += r[index + 1] * pow(10, (index + 1) % 3);
num += r[index + 2] * pow(10, (index + 2) % 3);
random[k++] = num % 256;
if(k==8)
break;
}
}
将时间做为随机数生成的种子保证每次生成的随机数不一致。获取一个9位数的数据,从头开始连续取三位生成一个新的3位数,将这个三位数对28 取余做为随机数的第一个字节。按此步骤循环8次每次步长4迭代生成。最后得到8个字节的随机数。
- 加密文件
char *encryptfile(char *file,unsigned long int *len,int *pwd){
unsigned long int n = 0;
char *nfile = NULL;
int left = *len % 8;
//不足8字节要补足8个字节
if(left!=0){
*len = *len + 8 - left + 16;
}else{
*len = *len + 16;
}
nfile = (char*)calloc(1,*len);
strcpy(nfile,file);
for (size_t i = 0; i < 8 - left && 8 - left != 8; i++)
{
nfile[*len+i] = '\0';
}
free(file);
file = NULL;
int k = 0;
for(n=0;n<strlen(nfile);n++)
{
nfile[n] = nfile[n] ^ pwd[k++];
if(k==8)
k = 0;
}
return nfile;
}
首先通过判断文件大小判断其是否位8字节整数倍大小不足则以0补齐。同时还需要将文件再扩容16个字节用来保存随机数的加密结果。之后将新文件的每一位与密钥做异或处理后保存位密文文件。
- 解密
int decryption(char *e_path, char *d_path,int *opwd)
{
unsigned long int length = 0;
char *encrypt_file_text = NULL;
encrypt_file_text = readfile(&length, e_path);
char *decrypt_file_text = NULL;
char m_random[16]; //随机数密文
int random[8];//随机数
int npwd[8];//新密码
//获取加密的随机数并将密文文件中的随机数加密数据修改为\0
getrandom(encrypt_file_text,m_random,length);
//解密随机数
decryptrandom(random,m_random);
//生成新的密码
get_newpwd(random,opwd,npwd);
decrypt_file_text = decryptfile(encrypt_file_text, length, npwd);
int k = 0;
while (decrypt_file_text[length-1-k]=='\0')
{
k++;
}
char *nfile = NULL;
nfile = (char*)calloc(1,length-k);
strncpy(nfile,decrypt_file_text,length-k);
printf("k=%d\n");
savefile(nfile, length, d_path);
}
解密是加密的逆运算,当拿到密文后会提取最后的随机数并将随机数通过DES算法解密为原来的随机数,然后将这个随机数通加密密钥异或生成新的密钥,最后通过生成的新密钥解密密文得到明文M。
运行情况
- 生成随机数并和密钥生成新的密钥
pwd:1100101100010101011010100011001111100010110000010110001001111011
ran:0010111001000110001010011111011001010001111101100100010100101110
npw:1110010101010011010000111100010110110011001101110010011101010101
第一行是原密钥;第二行是生成的64位随机数;第三行是新的密钥;
- 加密后的文件内容
%�jj�O�$�k`�W�)�m�X�F35DE3765AAF2D7C
- 文件明文
sjlkfjsdakfjlskadfjlsadkf
运行分析
时间复杂性:加密的核心操作是位异或,便利所有的字节做异或处理整个的时间复杂性位O(n)。
空间复杂性:简单对文件读写,没有开辟新的堆栈所以在空间上也是O(n)。
虽然时间和空间复杂性均为线性的,但是由于文件的大小实际上是有限制的,所以在该算法的复杂性上都是可以容忍的。
908M文件加密时间