加密算法中最广泛使用也是最有用的运算之一是 XOR。有必要理解 XOR 为什么如此有助于加密算法。XOR(在密码术中使用时)是一种位(bitwise)数字函数,其定义域是一个位对而值域是一个结果位。(在形式逻辑中使用时,它有细微差异,但还是同构的。)大多数读者可能已经很熟悉 XOR 的结果表,但还是让我们来看一下,以帮助回忆一下:
XOR(1, 1) --> 0
XOR(1, 0) --> 1
XOR(0, 1) --> 1
XOR(0, 0) --> 0
上表中我们以前缀形式书写 XOR 函数,但是大多数编程语言使用中缀形式。不必担心符号 — 上表只是帮助阐明 XOR 函数的本质。还有,在大多数编程语言中,称为 XOR 的运算(或更准确地说是“按位 XOR”)所做的比上表所显示的要多,而上表只是作为一般的法则。也就是说,在象 C、Perl 或 Python 之类的运算中,“^”实际上是两位域(或 ASCII 字符、整数等等,认为是位字段)中的每个对应位的布尔 XOR。原则上,拥有一位 XOR 的语言可以通过在各个位的位置上循环来模拟位域 XOR 的运算行为(但是复合位域 XOR 的计算效率要高很多)。
那么 XOR 到底特殊在哪里呢?首先,假设我们希望对一个明文位执行密码替代。我们希望防止攻击者对我们明文位转换后将要得到的值进行任何预测(即使是统计上的)。使用 XOR,明文的 0 位可能成为密文的 1 或 0,这取决于用作“加密位”(定义域对中的第二位)是 1 还是 0。对明文中的 1 也是同样的。转换的完全不可预测性对于密码术而言是理想的(除非您有权访问加密位)。
XOR 的另一个重要特性是无损。实际上,XOR 是直接可逆的。也就是说,如果我们有 Cb = Pb XOR Kb,那么就自动知道 Pb = Cb XOR Kb。即,如果(且仅如果)两次使用相同的加密位,则对第一次 XOR 运算的结果再应用 XOR 运算将返回原始(明文)位。XOR 与另一个布尔运算的行为对比:
AND(1, 1) --> 1
AND(1, 0) --> 0
AND(0, 1) --> 0
AND(0, 0) --> 0
在执行 AND 运算的过程中,我们丢失了信息。假设我们知道 0 = Pb AND Kb。没有加密位我们确实无法重新计算出 Pb。但是,如果 Kb 恰好为 0,即使拥有加密位,我们也无法重新计算出 Pb!我们根本无法取回 Pb 的值。
尽管 XOR 象它的运算行为一样好,但是它不象一些人幼稚地(或不怀好意地)宣称的那样好。而现实世界中有数目惊人的应用程序使用仅仅由 XOR 构成的加密。提醒一下,在一种情况下这工作得很好 — 一次性密码本(OTP)。如果您恰好有和待加密的明文一样多的密钥资料,可以证明 XOR 是理想的加密方法(假设密钥资料是真正随机的,也就是说,它具有的平均信息量和其长度相等,因此其语言比率为 1)。
许多有缺陷的算法只使用很小数量的密钥资料,将每个明文块与密钥块进行 XOR 运算,然后就将结果称为密文。对于单一块这很有效。但是当您开始重用同一密钥块来加密多个密文时,整个安全性就土崩瓦解了。
幼稚的 XOR 加密到底是如何显示其弱点的呢?基本上,它无力防范频率分析。假设我们使用 8 字节长的明文块和相应 8 字节长的加密密钥。(块更长也没有太大区别;虽然这会要求更多的已知密文,但应用的参数还是相同的。)找到一些密文并只临时忽略密文中除字节 1、9、17 等以外的任何东西。
对应于每块首部密文的明文将仍然和整个明文具有相同频率的规律性。并且每个相同的明文字节都会转换成相同的密文字节。因此通过知道明文的 13% 是由字母“E”组成的(假设是英语散文),我们所需要做的全部就是寻找以相同频率出现的密文字节值(这里我们通过忽略大小写和标点来简化示例,但这对于概念并不重要)。一旦找到了这些对应的明文和密文字节,立即就可以通过 KB = PB XOR CB 得出密钥字节,举个例子:KB = 'E ' XOR 'q '。一旦知道了密钥字节,我们就可以对所有其明文不是“E”的密文值解密,而无须进一步工作。对 [2,10,18,...] 和 [3,11,19,...] 密文字节重复相同过程,以此类推。
下面就写一个简单的程序(网上有这样的简单例程)
#include "stdafx.h"
#include<stdio.h>
#include<windows.h>
main()
{
FILE *fp,*fp2;int i,j; //i用作记录0到255之间任一个数,j用作记录当前XOR的字节位置
byte *buffer,*buffer1,*buffer2,*buffer3;
buffer =(byte*)malloc(16); //分配16个字节给buffer指向的位置
buffer2 =(byte*)malloc(16*256); //分配16*256个字节给buffer2指向的位置
fp = fopen("animation.cfg", "rb");//打开怀疑加密了的文件
buffer3=buffer2; //buffer3保存buffer2初始时的地址
for (i=0;i<=255;i++)
{
buffer1=buffer; //buffer1保存buffer初始时的地址
fseek(fp, 0, 0); //重定位到animation.cfg文件的开头
fread(buffer1, 16, 1, fp); //读取animation.cfg文件的开头16字节数据到buffer
for(j=0;j<16;j++)
{
*buffer1=*buffer1^i; //animation.cfg文件的开头第一个字节和0~255之间
//任一个数值异或,结果保存在buffer1所指的位置上
*buffer2=*buffer1; //将buffer1所指向的数据复制到buffer2所指的位置
buffer1=buffer1+1; //buffer1指针加1,准备读取下一个数据
buffer2=buffer2+1; //buffer2指针加1,准备存储下一个数据
}
}
fp2 = fopen("test", "wb+"); //打开一个二进制文件,准备将数据写入
fwrite(buffer3, 16*256, 1, fp2);//buffer3保存buffer2初始时的地址,这个语句将buffer2初始时的地址所指的数据,用二进制方式写入16×256字节
fclose(fp2); //关闭文件句柄
fclose(fp);
}
仔细再看看程序还是有一点问题的,我试了一下,待加密的字符串依然在存在,只不过加密后多了那么多字符确实是对原字符串加密后的 i应该从1开始 就避免了原字符串的存在,这样虽然起到加密的作用,但无缘无故多出那么多的空间,浪费啊。我建议把这样的 几句程序放到第一个for循环里
buffer2 =(byte*)malloc(16*256); //分配16*256个字节给buffer2指向的位置
fp = fopen("animation.cfg", "rb");//打开怀疑加密了的文件
buffer3=buffer2; //buffer3保存buffer2初始时的地址
另外分配内存的空间最好根据文件的大小来设置 ,
fseek(fp,0,SEEK_END); //定位到文件末
nFileLen = ftell(fp); //文件长度
减少空间的浪费
下面我经过我重新更改过的程序:
#include "stdafx.h"
#include<stdio.h>
#include<windows.h>
void main()
{
FILE *fp,*fp2;int i,j; //i用作记录0到255之间任一个数,j用作记录当前XOR的字节位置
byte *buffer,*buffer1,*buffer2,*buffer3;//指针buffer和buffer1一组,指针buffer2和buffer3 一组,buffer指针会变动,buffer指针永远指向buffer的第一个字节位置,buffer2和buffer3同样道理
fp = fopen("config1.ini", "rb"); //打开怀疑加密了的文件
fseek(fp,0,SEEK_END); //定位到文件末
int k = ftell(fp); //文件长度
for (i=0;i<=255;i++)
{
///////////////////////////////////////////////////////////////////////////////
buffer =(byte*)malloc(k); //分配k个字节给buffer指向的位置
buffer2 =(byte*)malloc(k); //分配k个字节给buffer2指向的位置
buffer3=buffer2; //buffer3保存buffer2初始时的地址
///////////////////////////////////////////////////////////////////////////////
buffer1=buffer; //buffer1保存buffer初始时的地址
fseek(fp, 0, 0); //重定位到文件的开头
fread(buffer1, k, 1, fp); //读取文件的开头k字节数据到buffer
for(j=0;j<k;j++)
{
*buffer1=*buffer1^i; //文件的开头第一个字节和0~255之间
//任一个数值异或,结果保存在buffer1所指的位置上
*buffer2=*buffer1; //将buffer1所指向的数据复制到buffer2所指的位置
buffer1=buffer1+1; //buffer1指针加1,准备读取下一个数据
buffer2=buffer2+1; //buffer2指针加1,准备存储下一个数据
}
}
fp2 = fopen("config2.ini", "wb+"); //打开一个二进制文件,准备将数据写入
fwrite(buffer3, k, 1, fp2);//buffer3保存buffer2初始时的地址,这个语句将buffer2初始时的地址所指的数据,用二进制方式写入16×256字节
fclose(fp2); //关闭文件句柄
fclose(fp);
}
作者:caowei880123