信息安全技术——Many Time Pad攻击实验

C++实现Many Time Pad攻击

新人小白第一次写文章,若有错误请指正,别喷我555

一、实验要求:

我们已经证明一次一密在已知密文攻击下是安全的。本次实验将尝试探索当密钥被重复使用时的已知密文攻击方法。

有11个使用同一个密钥按照one time pad方法进行加密得到的密文。请使用前10个密文进行分析,并对目标密文进行解密。其中消息是[a-zA-Z]以及空格组成的字符,使用ascii编码,密文使用hex编码。

1.分析攻击过程和思路。

2.   提供正确运行的程序,加上必要的注释及运行结果截图。

3.   设计、开发中的问题及实验体会。

二、原理分析:

开始之前,先分析密文格式,很明显是16进制的表示方法,每两个数一组,表示一个asiic字符(后文提到的所有c1i、p1i、ki 默认是一个字符,即2位16进制、8位2进制数:32510ba9babebbbefd001547a810e67149caee11d945cd7fc81a05e9f85aac650e9052ba6a8cd8257bf14d13e6f0a803b54fde9e77472dbff89d71b57bddef121336cb85ccb8f3315f4b52e301d16e9f52f904

首先,由于异或运算的特点,采用相同密钥加密获得的密文进行异或等价于对应明文之间的异或(密钥k,密文c,明文p)即:

c1=p1⊕ki

c2=p2⊕ki

c1⊕c2=p1⊕ki⊕p2⊕ki=p1⊕p2

其次,英文句子的特点是单词之间留有空格(空字符),而空字符与小(大)写字母异或得到对应的大(小)写字母:A∼Z的asiic码为0100 0001~0101 1010,a∼z的asiic码为0110 0001~0111 1010,而空格的asiic码为0010 0000,实在是太巧了。

公式表示为:
'space'⊕(a∼z)=(A∼Z)

'space'⊕(A∼Z)=(a∼z)

三、代码逻辑:

由以上两个特点,如果两个密文异或的结果落在A∼Z或a∼z的区间内,则这两个密文中很有可能有某一个是空格;若一个密文和同列所有密文异或结果多次落在A∼Z或a∼z的区间内,则其明文为空格的概率就更高了,即:

若c1i⊕c2i=p1i⊕p2i=(A∼Z)or(a∼z) , c1i⊕c3i=p1i⊕p3i=(A∼Z)or(a∼z)...... 

c1i⊕c4i=p1i⊕p4i=(A∼Z)or(a∼z)  中有多数成立

则猜测 p1i='space'

至此,我们通过横向比较确定了某密文的明文为空格,那么将该位置密文与空字符异或将得到该位置的密钥key,即:

若 p1i='space'

则 ki=c1i⊕p1i=c1i⊕'space'

以上步骤对每个密文的每两位重复计算,得到可以解析的所有ki (空格足够多即可)

最后,对于待计算密文,把密钥和密文异或算出明文:

p1=c1⊕k

举个例子:

给定c1=74,c2=1D,c3=1A:

先看看c1原文是不是空格:

c1⊕c2=0111 0100⊕0001 1101=0110 1001=69 ,对应10进制105:字母i

c1⊕c3=0111 0100⊕0001 1010=0110 1110=74 ,对应10进制116:字母t

则p1有可能为空格,计算密钥:

k=c1⊕p1=c1⊕'space'=0111 0100⊕0010 0000=54 ,对应10进制84,字母T

四、C++代码片段:

1.全局变量

本次实验所需解密的密文长度为166位16进制数,即84个字符。

初始化密钥数组以及密钥确定度数组,手动复制11个密文。

#include<iostream>
#include<iomanip>
#include<string>
using namespace std;
# define LEN 166           //需确定的密钥长度
char key[LEN];             //密钥数组
int key1[LEN] = { 0 };     //密钥确定程度计数
string ciphertext[11] = {  //密文组
	"315c4eeaa8b5f8aaf9174145bf43e1784b8fa00dc71d885a804e5ee9fa40b16349c146fb778cdf2d3aff021dfff5b403b510d0d0455468aeb98622b137dae857553ccd8883a7bc37520e06e515d22c954eba5025b8cc57ee59418ce7dc6bc41556bdb36bbca3e8774301fbcaa3b83b220809560987815f65286764703de0f3d524400a19b159610b11ef3e",
	"234c02ecbbfbafa3ed18510abd11fa724fcda2018a1a8342cf064bbde548b12b07df44ba7191d9606ef4081ffde5ad46a5069d9f7f543bedb9c861bf29c7e205132eda9382b0bc2c5c4b45f919cf3a9f1cb74151f6d551f4480c82b2cb24cc5b028aa76eb7b4ab24171ab3cdadb8356f",
	"32510ba9a7b2bba9b8005d43a304b5714cc0bb0c8a34884dd91304b8ad40b62b07df44ba6e9d8a2368e51d04e0e7b207b70b9b8261112bacb6c866a232dfe257527dc29398f5f3251a0d47e503c66e935de81230b59b7afb5f41afa8d661cb",
	"32510ba9aab2a8a4fd06414fb517b5605cc0aa0dc91a8908c2064ba8ad5ea06a029056f47a8ad3306ef5021eafe1ac01a81197847a5c68a1b78769a37bc8f4575432c198ccb4ef63590256e305cd3a9544ee4160ead45aef520489e7da7d835402bca670bda8eb775200b8dabbba246b130f040d8ec6447e2c767f3d30ed81ea2e4c1404e1315a1010e7229be6636aaa",
	"3f561ba9adb4b6ebec54424ba317b564418fac0dd35f8c08d31a1fe9e24fe56808c213f17c81d9607cee021dafe1e001b21ade877a5e68bea88d61b93ac5ee0d562e8e9582f5ef375f0a4ae20ed86e935de81230b59b73fb4302cd95d770c65b40aaa065f2a5e33a5a0bb5dcaba43722130f042f8ec85b7c2070",
	"32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd2061bbde24eb76a19d84aba34d8de287be84d07e7e9a30ee714979c7e1123a8bd9822a33ecaf512472e8e8f8db3f9635c1949e640c621854eba0d79eccf52ff111284b4cc61d11902aebc66f2b2e436434eacc0aba938220b084800c2ca4e693522643573b2c4ce35050b0cf774201f0fe52ac9f26d71b6cf61a711cc229f77ace7aa88a2f19983122b11be87a59c355d25f8e4",
	"32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd90f1fa6ea5ba47b01c909ba7696cf606ef40c04afe1ac0aa8148dd066592ded9f8774b529c7ea125d298e8883f5e9305f4b44f915cb2bd05af51373fd9b4af511039fa2d96f83414aaaf261bda2e97b170fb5cce2a53e675c154c0d9681596934777e2275b381ce2e40582afe67650b13e72287ff2270abcf73bb028932836fbdecfecee0a3b894473c1bbeb6b4913a536ce4f9b13f1efff71ea313c8661dd9a4ce",
	"315c4eeaa8b5f8bffd11155ea506b56041c6a00c8a08854dd21a4bbde54ce56801d943ba708b8a3574f40c00fff9e00fa1439fd0654327a3bfc860b92f89ee04132ecb9298f5fd2d5e4b45e40ecc3b9d59e9417df7c95bba410e9aa2ca24c5474da2f276baa3ac325918b2daada43d6712150441c2e04f6565517f317da9d3",
	"271946f9bbb2aeadec111841a81abc300ecaa01bd8069d5cc91005e9fe4aad6e04d513e96d99de2569bc5e50eeeca709b50a8a987f4264edb6896fb537d0a716132ddc938fb0f836480e06ed0fcd6e9759f40462f9cf57f4564186a2c1778f1543efa270bda5e933421cbe88a4a52222190f471e9bd15f652b653b7071aec59a2705081ffe72651d08f822c9ed6d76e48b63ab15d0208573a7eef027",
	"466d06ece998b7a2fb1d464fed2ced7641ddaa3cc31c9941cf110abbf409ed39598005b3399ccfafb61d0315fca0a314be138a9f32503bedac8067f03adbf3575c3b8edc9ba7f537530541ab0f9f3cd04ff50d66f1d559ba520e89a2cb2a83",
	"32510ba9babebbbefd001547a810e67149caee11d945cd7fc81a05e9f85aac650e9052ba6a8cd8257bf14d13e6f0a803b54fde9e77472dbff89d71b57bddef121336cb85ccb8f3315f4b52e301d16e9f52f904"
};

2.16进制异或

先把16进制转为10进制再做异或;i2c和c2i分别是10转16和16转10。

char i2c(int s) {        //10转16
	switch (s) {
	case 0:return '0'; break;
	case 1:return '1'; break;
	case 2:return '2'; break;
	case 3:return '3'; break;
	case 4:return '4'; break;
	case 5:return '5'; break;
	case 6:return '6'; break;
	case 7:return '7'; break;
	case 8:return '8'; break;
	case 9:return '9'; break;
	case 10:return 'a'; break;
	case 11:return 'b'; break;
	case 12:return 'c'; break;
	case 13:return 'd'; break;
	case 14:return 'e'; break;
	case 15:return 'f'; break;
	}
}
int c2i(char s) {         //16转10
	switch (s) {
	case '0':return 0; break;
	case '1':return 1; break;
	case '2':return 2; break;
	case '3':return 3; break;
	case '4':return 4; break;
	case '5':return 5; break;
	case '6':return 6; break;
	case '7':return 7; break;
	case '8':return 8; break;
	case '9':return 9; break;
	case 'a':return 10; break;
	case 'b':return 11; break;
	case 'c':return 12; break;
	case 'd':return 13; break;
	case 'e':return 14; break;
	case 'f':return 15; break;
	}
}
int Xor(char x, char y) {  //16进制异或
	return c2i(x) ^ c2i(y);
}

3.计算密钥

跟上文的:三、代码逻辑 思路完全一样,总之就是把密钥算出来了。

细节:2位16进制(8位二进制)的异或方法;存密钥的时候要转回16进制。

可能有人认为按题目意思来看,待解析密文不可以用作计算密钥,但实际上可以解析出更精确的密钥,若还是觉得不合适就把11改成10。

for (int i = 0; i < 11; ++i) { //对每一个密文
		for (int x = 0; x < LEN; x += 2) {  //对每个密文的每个字符(两位16进制确定一个字符)
			int count = 0;
			for (int j = 0; j < 11; ++j) {  //把每个字符跟其余密文的同位置字符异或
				int n = Xor(ciphertext[i][x],ciphertext[j][x]) * 16 + Xor(ciphertext[i][x + 1],ciphertext[j][x + 1]);//异或后计算字符asiic,看不懂去学计算机组成原理
				if ((n >= 65 && n <= 90) || (n >= 97 && n <= 122) || n == 32 || n == 58 || n == 44) { ++count; }//如果异或的结果落在字母或标点符号的区间内,则密钥确定程度+1
			}
			if (count >= 2) {  //数字设小点就行
				if (key1[x] < count) {  //同位置取确定度最高的(原文最有可能是空格的密文),跟空格异或计算出密钥
					key[x] = i2c(Xor(ciphertext[i][x],2)); 
					key1[x] = count;
					key[x + 1] = i2c(Xor(ciphertext[i][x + 1],0)); 
					key1[x + 1] = count;
				}
			}
		}
	}
	for (int x = 0; x < LEN; ++x) { cout << key[x]; } //密钥打印出来看看

密钥打印出来看看:66396e89c9dbd8cb9874352acd6395102eafce78aa7fed28a06e6bc98d29c50b69b033db19f8aa401a9c6d708f80c066c763fef0123148cdd8e802d05ba98777335daefcecd59c433a6b268b60bf4ef03c9a61

4.解析密文

把待解析密文和上一步计算出来的密钥异或算出明文。

for (int x = 0; x < LEN; x += 2) {
		if (key1[x]) { //如果该位置密钥已经过解析
			int n = Xor(ciphertext[10][x], key[x]) * 16 + Xor(ciphertext[10][x + 1], key[x + 1]);
			if ((n >= 65 && n <= 90) || (n >= 97 && n <= 122) || n == 32 || n == 58 || n == 44) { //如果该位置明文很正常
				cout << char(n);
			}
			else { cout << "*"; }
		}
		else { cout << "?"; }
	}

明文:The secuet message is: Whtn using aastream cipher, never use the key more than once

看上去还有一些问题,但总体准确度可以接受。

五、完整代码和感想:

#include<iostream>
#include<iomanip>
#include<string>
using namespace std;
# define LEN 166           //需确定的密钥长度
char key[LEN];             //密钥数组
int key1[LEN] = { 0 };     //密钥确定程度计数
string ciphertext[11] = {  //密文组
	"315c4eeaa8b5f8aaf9174145bf43e1784b8fa00dc71d885a804e5ee9fa40b16349c146fb778cdf2d3aff021dfff5b403b510d0d0455468aeb98622b137dae857553ccd8883a7bc37520e06e515d22c954eba5025b8cc57ee59418ce7dc6bc41556bdb36bbca3e8774301fbcaa3b83b220809560987815f65286764703de0f3d524400a19b159610b11ef3e",
	"234c02ecbbfbafa3ed18510abd11fa724fcda2018a1a8342cf064bbde548b12b07df44ba7191d9606ef4081ffde5ad46a5069d9f7f543bedb9c861bf29c7e205132eda9382b0bc2c5c4b45f919cf3a9f1cb74151f6d551f4480c82b2cb24cc5b028aa76eb7b4ab24171ab3cdadb8356f",
	"32510ba9a7b2bba9b8005d43a304b5714cc0bb0c8a34884dd91304b8ad40b62b07df44ba6e9d8a2368e51d04e0e7b207b70b9b8261112bacb6c866a232dfe257527dc29398f5f3251a0d47e503c66e935de81230b59b7afb5f41afa8d661cb",
	"32510ba9aab2a8a4fd06414fb517b5605cc0aa0dc91a8908c2064ba8ad5ea06a029056f47a8ad3306ef5021eafe1ac01a81197847a5c68a1b78769a37bc8f4575432c198ccb4ef63590256e305cd3a9544ee4160ead45aef520489e7da7d835402bca670bda8eb775200b8dabbba246b130f040d8ec6447e2c767f3d30ed81ea2e4c1404e1315a1010e7229be6636aaa",
	"3f561ba9adb4b6ebec54424ba317b564418fac0dd35f8c08d31a1fe9e24fe56808c213f17c81d9607cee021dafe1e001b21ade877a5e68bea88d61b93ac5ee0d562e8e9582f5ef375f0a4ae20ed86e935de81230b59b73fb4302cd95d770c65b40aaa065f2a5e33a5a0bb5dcaba43722130f042f8ec85b7c2070",
	"32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd2061bbde24eb76a19d84aba34d8de287be84d07e7e9a30ee714979c7e1123a8bd9822a33ecaf512472e8e8f8db3f9635c1949e640c621854eba0d79eccf52ff111284b4cc61d11902aebc66f2b2e436434eacc0aba938220b084800c2ca4e693522643573b2c4ce35050b0cf774201f0fe52ac9f26d71b6cf61a711cc229f77ace7aa88a2f19983122b11be87a59c355d25f8e4",
	"32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd90f1fa6ea5ba47b01c909ba7696cf606ef40c04afe1ac0aa8148dd066592ded9f8774b529c7ea125d298e8883f5e9305f4b44f915cb2bd05af51373fd9b4af511039fa2d96f83414aaaf261bda2e97b170fb5cce2a53e675c154c0d9681596934777e2275b381ce2e40582afe67650b13e72287ff2270abcf73bb028932836fbdecfecee0a3b894473c1bbeb6b4913a536ce4f9b13f1efff71ea313c8661dd9a4ce",
	"315c4eeaa8b5f8bffd11155ea506b56041c6a00c8a08854dd21a4bbde54ce56801d943ba708b8a3574f40c00fff9e00fa1439fd0654327a3bfc860b92f89ee04132ecb9298f5fd2d5e4b45e40ecc3b9d59e9417df7c95bba410e9aa2ca24c5474da2f276baa3ac325918b2daada43d6712150441c2e04f6565517f317da9d3",
	"271946f9bbb2aeadec111841a81abc300ecaa01bd8069d5cc91005e9fe4aad6e04d513e96d99de2569bc5e50eeeca709b50a8a987f4264edb6896fb537d0a716132ddc938fb0f836480e06ed0fcd6e9759f40462f9cf57f4564186a2c1778f1543efa270bda5e933421cbe88a4a52222190f471e9bd15f652b653b7071aec59a2705081ffe72651d08f822c9ed6d76e48b63ab15d0208573a7eef027",
	"466d06ece998b7a2fb1d464fed2ced7641ddaa3cc31c9941cf110abbf409ed39598005b3399ccfafb61d0315fca0a314be138a9f32503bedac8067f03adbf3575c3b8edc9ba7f537530541ab0f9f3cd04ff50d66f1d559ba520e89a2cb2a83",
	"32510ba9babebbbefd001547a810e67149caee11d945cd7fc81a05e9f85aac650e9052ba6a8cd8257bf14d13e6f0a803b54fde9e77472dbff89d71b57bddef121336cb85ccb8f3315f4b52e301d16e9f52f904"
};
int c2i(char s) {   //16转10
	switch (s) {
	case '0':return 0; break;
	case '1':return 1; break;
	case '2':return 2; break;
	case '3':return 3; break;
	case '4':return 4; break;
	case '5':return 5; break;
	case '6':return 6; break;
	case '7':return 7; break;
	case '8':return 8; break;
	case '9':return 9; break;
	case 'a':return 10; break;
	case 'b':return 11; break;
	case 'c':return 12; break;
	case 'd':return 13; break;
	case 'e':return 14; break;
	case 'f':return 15; break;
	}
}
char i2c(int s) {  //10转16
	switch (s) {
	case 0:return '0'; break;
	case 1:return '1'; break;
	case 2:return '2'; break;
	case 3:return '3'; break;
	case 4:return '4'; break;
	case 5:return '5'; break;
	case 6:return '6'; break;
	case 7:return '7'; break;
	case 8:return '8'; break;
	case 9:return '9'; break;
	case 10:return 'a'; break;
	case 11:return 'b'; break;
	case 12:return 'c'; break;
	case 13:return 'd'; break;
	case 14:return 'e'; break;
	case 15:return 'f'; break;
	}
}
int Xor(char x, char y) { //16进制异或
	return c2i(x) ^ c2i(y);
}
int main() {
	for (int i = 0; i < 11; ++i) { //对每一个密文
		for (int x = 0; x < LEN; x += 2) {  //对每个密文的每个字符(两位16进制确定一个字符)
			int count = 0;
			for (int j = 0; j < 11; ++j) {  //把每个字符跟其余密文的同位置字符异或
				int n = Xor(ciphertext[i][x],ciphertext[j][x]) * 16 + Xor(ciphertext[i][x + 1],ciphertext[j][x + 1]);//异或后计算字符asiic,看不懂去学计算机组成原理
				if ((n >= 65 && n <= 90) || (n >= 97 && n <= 122) || n == 32 || n == 58 || n == 44) { ++count; }//如果异或的结果落在字母或标点符号的区间内,则密钥确定程度+1
			}
			if (count >= 2) {  //数字设小点就行
				if (key1[x] < count) {  //同位置取确定度最高的(原文最有可能是空格的密文),跟空格异或计算出密钥
					key[x] = i2c(Xor(ciphertext[i][x],2)); 
					key1[x] = count;
					key[x + 1] = i2c(Xor(ciphertext[i][x + 1],0)); 
					key1[x + 1] = count;
				}
			}
		}
	}
	for (int x = 0; x < LEN; ++x) { cout << key[x]; } //密钥打印出来看看
	cout << endl;
	for (int x = 0; x < LEN; x += 2) {
		if (key1[x]) { //如果该位置密钥已经过解析
			int n = Xor(ciphertext[10][x], key[x]) * 16 + Xor(ciphertext[10][x + 1], key[x + 1]);
			if ((n >= 65 && n <= 90) || (n >= 97 && n <= 122) || n == 32 || n == 58 || n == 44) { //如果该位置明文很正常
				cout << char(n);
			}
			else { cout << "*"; }
		}
		else { cout << "?"; }
	}
}

第一次写文章,来谈谈感想...

搞明白原理以后,这个实验其实写起来特别简单。当我跟舍友讨论,说这实验用c写比用python方便的时候,他来了一句“c太难了根本写不出来;我用python都写了快100行,你用c不得200多行?”。一赌气,就用c写出来了(其实也就不到100行嘛)。

说实在的做这个实验没什么实际意义,想让我们搞明白原理手算一道就足够了。大部分写代码的时间都用在格式转换上了,么得意思,建议看到这里的直接白嫖(前提是搞清楚原理之后!!!)。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值