DES加解密算法 — python和C++实现

密码学引论作业——DES

   

   

DES实现

   

1 算法描述

   

1.1

   
         DES(Data Encryption Standard) 是分组对称密码算法。DES采用了64位的分组长度和56位的密钥长度,它将64位的输入经过一系列变换得到64位的输出。解密则使用了相同的步骤和相同的密钥。DES的密钥长度为64位,由于第n*8(n=1,2,…8)是校验位,因此实际参与加密的长度为56位,密钥空间含有2^56个密钥。

        DES算法利用多次组合替代算法和换位算法,分散和错乱的相互作用,把明文编制成密码强度很高的密文,它的加密和解密用的是同一算法。

        DES算法,是一种乘积密码,其在算法结构上主要采用了 F e s i t e l Fesitel Fesitel结构,通过轮函数迭代的方式来进行计算和工作。

        算法也会使用到数据置换技术,主要有初始置换IP和逆初始置换IP^-1两种类型。DES算法使用置换运算的目的是将原始明文的所有格式及所有数据全部打乱重排。而在轮加密函数中,即将数据全部打乱重排,同时在数据格式方面,将原有的32位数据格式,扩展成为48位数据格式,目的是为了满足S盒组对数据长度和数据格式规范的要求。
   

1.2 Fesitel结构

        加密过程中,先把待加密文本分为左右两部分 L 和 R L和R LR,然后经过一定轮数加密,每一轮加密过程都如下所示,

{ L n + 1 = R n R n + 1 = L n ⊕ F ( K n , R n ) \begin{cases} L_{n+1}=R_n\\R_{n+1}=L_n\oplus F(K_n,R_n)\end{cases} {Ln+1=RnRn+1=LnF(Kn,Rn)
每次的 R n + 1 R_{n+1} Rn+1都是输入的 L n L_n Ln F F F函数结果的异或, F F F函数的输入是本轮密钥 K n K_n Kn和本轮输入 R n Rn Rn
Fesitel结构示意图
        经过若干轮加密后,在最后一轮得到的 R n 和 L n R_n和L_n RnLn交换后拼接成为输出,即 O u t = R n + L n Out=R_n+L_n Out=Rn+Ln
 

2 具体实现

   

2.1 步骤

   

输入处理:

        输入任意长的字符串,或16进制串。分组处理,先把每个字符转换成16进制的ASCii码,用 ‘0x0’ 补足长度,令其长度是16的整数倍。然后每位16进制ascii值转化为四位二进制码。并分成64bit的组。对每组都使用DES加密。
   

密钥准备

        我们的密钥是16位长的16进制串,也就是64位长的二进制串,密钥要先经过一个 P C _ 1 PC\_1 PC_1置换,得到56bit去掉校验位并打乱顺序的bit串 K 56 K56 K56,然后 K 56 K56 K56左右等分成 C 0 C_0 C0 D 0 D_0 D0 C 0 C_0 C0左移 i 1 i_1 i1 位生成 C 1 C_1 C1 C 1 C_1 C1左移 i 2 i_2 i2 位生成 C 2 C_2 C2…,同理生成 D 1 D_1 D1 D 16 D_{16} D16,其中 i k i_k ik是由左移表 K e y _ l e f t _ s h i f t Key\_left\_shift Key_left_shift 决定的。

PC_1 = [								# 64->56置换表
		57, 49, 41, 33, 25, 17, 9,
        1, 58, 50, 42, 34, 26, 18,
        10, 2, 59, 51, 43, 35, 27,
        19, 11, 3, 60, 52, 44, 36,
        63, 55, 47, 39, 31, 23, 15,
        7, 62, 54, 46, 38, 30, 22,
        14, 6, 61, 53, 45, 37, 29,
        21, 13, 5, 28, 20, 12, 4,
]
 Kleft_shift = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1] #左移表
 

        得到 C i C_i Ci D i D_i Di(共16组)后,拼接得到 K i ′ K_i' Ki,然后 K i ′ K_i' Ki 通过 P C _ 2 PC\_2 PC_2置换矩阵得到轮密钥 K i K_i Ki,注意: P C _ 2 PC\_2 PC_2置换矩阵是把56位的 K i ′ K_i' Ki置换成了48位的 K i K_i Ki
   

16轮加密

        我们现在有分好组的明文二进制串,加密过程是对一个64bit串进行加密运算。DES中明文加密的流程图如下,64bit的明文先通过 I P IP IP置换矩阵,然后分为 L 0 L_0 L0 R 0 R_0 R0两部分,经过16轮的 F e s i t e l Fesitel Fesitel加密,最后拼接后经过 I P _ 1 IP\_1 IP_1置换矩阵得到密文。关键在于其中的 F F F 函数的实现。

在这里插入图片描述

 

#E拓展,唯一能记住的盒子
E = [	32,1,2,3,4,5,
        4,5,6,7,8,9,
        8,9,10,11,12,13,
        12,13,14,15,16,17,
        16,17,18,19,20,21,
        20,21,22,23,24,25,
        24,25,26,27,28,29,
        28,29,30,31,32,1,
     ]
#IP置换
IP = [
        58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4,
        62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8,
        57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
        61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7
]
#IP逆置换
IP_1=[
        40,8,48,16,56,24,64,32,39,7,47,15,55,23,63,31,
        38,6,46,14,54,22,62,30,37,5,45,13,53,21,61,29,
        36,4,44,12,52,20,60,28,35,3,43,11,51,19,59,27,
        34,2,42,10,50,18,58,26,33,1,41, 9,49,17,57,25
]

        我们可以注意到 I P IP IP I P _ 1 IP\_1 IP_1两个矩阵是有关系的,比如 I P _ 1 IP\_1 IP_1的第一位是40,而 1 1 1就是 I P IP IP的第40位,于是有如下关系式: I P _ 1 [ i ] = I P . i n d e x ( i ) IP\_1[i] = IP.index(i) IP_1[i]=IP.index(i)
 

F函数

         F F F函数的输入是 32 b i t 32bit 32bit R R R 48 b i t 48bit 48bit K i K_i Ki,首先要先对 R R R 做一个 E E E 拓展,变成 48 b i t 48bit 48bit,然后与 K i K_i Ki进行异或,异或得到的 48 b i 48bi 48bit串,分成 8 8 8 组,每组是连续的 6 b i t 6bit 6bit。我们利用这 6 b i t 6bit 6bit,来获得一个 S _ b o x S\_box S_box的坐标。 S _ b o x S\_box S_box共有8个矩阵,分别对应 8 8 8 6 b i t 6bit 6bit的分组。
        每个分组的 6 b i t 6bit 6bit,首位两个bit拼接作为矩阵行坐标,中间四个bit拼接作为列坐标。例如第一个分组是"100101",行坐标就是 0 b 11 0b11 0b11,就是3,列坐标就是"0010"就是2,这样行坐标有4种情况,列坐标有16种情况,所以 S _ b o x S\_box S_box的矩阵是4行16列的。

S_box = [[
                0xe, 0x4, 0xd, 0x1, 0x2, 0xf, 0xb, 0x8, 0x3, 0xa, 0x6, 0xc, 0x5, 0x9, 0x0, 0x7,
                0x0, 0xf, 0x7, 0x4, 0xe, 0x2, 0xd, 0x1, 0xa, 0x6, 0xc, 0xb, 0x9, 0x5, 0x3, 0x8,
                0x4, 0x1, 0xe, 0x8, 0xd, 0x6, 0x2, 0xb, 0xf, 0xc, 0x9, 0x7, 0x3, 0xa, 0x5, 0x0,
                0xf, 0xc, 0x8, 0x2, 0x4, 0x9, 0x1, 0x7, 0x5, 0xb, 0x3, 0xe, 0xa, 0x0, 0x6, 0xd,
        ],
        [
                0xf, 0x1, 0x8, 0xe, 0x6, 0xb, 0x3, 0x4, 0x9, 0x7, 0x2, 0xd, 0xc, 0x0, 0x5, 0xa,
                0x3, 0xd, 0x4, 0x7, 0xf, 0x2, 0x8, 0xe, 0xc, 0x0, 0x1, 0xa, 0x6, 0x9, 0xb, 0x5,
                0x0, 0xe, 0x7, 0xb, 0xa, 0x4, 0xd, 0x1, 0x5, 0x8, 0xc, 0x6, 0x9, 0x3, 0x2, 0xf,
                0xd, 0x8, 0xa, 0x1, 0x3, 0xf, 0x4, 0x2, 0xb, 0x6, 0x7, 0xc, 0x0, 0x5, 0xe, 0x9,
        ],
        [
                0xa, 0x0, 0x9, 0xe, 0x6, 0x3, 0xf, 0x5, 0x1, 0xd, 0xc, 0x7, 0xb, 0x4, 0x2, 0x8,
                0xd, 0x7, 0x0, 0x9, 0x3, 0x4, 0x6, 0xa, 0x2, 0x8, 0x5, 0xe, 0xc, 0xb, 0xf, 0x1,
                0xd, 0x6, 0x4, 0x9, 0x8, 0xf, 0x3, 0x0, 0xb, 0x1, 0x2, 0xc, 0x5, 0xa, 0xe, 0x7,
                0x1, 0xa, 0xd, 0x0, 0x6, 0x9, 0x8, 0x7, 0x4, 0xf, 0xe, 0x3, 0xb, 0x5, 0x2, 0xc,
        ],
        [
                0x7, 0xd, 0xe, 0x3, 0x0, 0x6, 0x9, 0xa, 0x1, 0x2, 0x8, 0x5, 0xb, 0xc, 0x4, 0xf,
                0xd, 0x8, 0xb, 0x5, 0x6, 0xf, 0x0, 0x3, 0x4, 0x7, 0x2, 0xc, 0x1, 0xa, 0xe, 0x9,
                0xa, 0x6, 0x9, 0x0, 0xc, 0xb, 0x7, 0xd, 0xf, 0x1, 0x3, 0xe, 0x5, 0x2, 0x8, 0x4,
                0x3, 0xf, 0x0, 0x6, 0xa, 0x1, 0xd, 0x8, 0x9, 0x4, 0x5, 0xb, 0xc, 0x7, 0x2, 0xe,
        ],
        [
                0x2, 0xc, 0x4, 0x1, 0x7, 0xa, 0xb, 0x6, 0x8, 0x5, 0x3, 0xf, 0xd, 0x0, 0xe, 0x9,
                0xe, 0xb, 0x2, 0xc, 0x4, 0x7, 0xd, 0x1, 0x5, 0x0, 0xf, 0xa, 0x3, 0x9, 0x8, 0x6,
                0x4, 0x2, 0x1, 0xb, 0xa, 0xd, 0x7, 0x8, 0xf, 0x9, 0xc, 0x5, 0x6, 0x3, 0x0, 0xe,
                0xb, 0x8, 0xc, 0x7, 0x1, 0xe, 0x2, 0xd, 0x6, 0xf, 0x0, 0x9, 0xa, 0x4, 0x5, 0x3,
        ],
        [
                0xc, 0x1, 0xa, 0xf, 0x9, 0x2, 0x6, 0x8, 0x0, 0xd, 0x3, 0x4, 0xe, 0x7, 0x5, 0xb,
                0xa, 0xf, 0x4, 0x2, 0x7, 0xc, 0x9, 0x5, 0x6, 0x1, 0xd, 0xe, 0x0, 0xb, 0x3, 0x8,
                0x9, 0xe, 0xf, 0x5, 0x2, 0x8, 0xc, 0x3, 0x7, 0x0, 0x4, 0xa, 0x1, 0xd, 0xb, 0x6,
                0x4, 0x3, 0x2, 0xc, 0x9, 0x5, 0xf, 0xa, 0xb, 0xe, 0x1, 0x7, 0x6, 0x0, 0x8, 0xd,
        ],
        [
                0x4, 0xb, 0x2, 0xe, 0xf, 0x0, 0x8, 0xd, 0x3, 0xc, 0x9, 0x7, 0x5, 0xa, 0x6, 0x1,
                0xd, 0x0, 0xb, 0x7, 0x4, 0x9, 0x1, 0xa, 0xe, 0x3, 0x5, 0xc, 0x2, 0xf, 0x8, 0x6,
                0x1, 0x4, 0xb, 0xd, 0xc, 0x3, 0x7, 0xe, 0xa, 0xf, 0x6, 0x8, 0x0, 0x5, 0x9, 0x2,
                0x6, 0xb, 0xd, 0x8, 0x1, 0x4, 0xa, 0x7, 0x9, 0x5, 0x0, 0xf, 0xe, 0x2, 0x3, 0xc,
        ],
        [
                0xd, 0x2, 0x8, 0x4, 0x6, 0xf, 0xb, 0x1, 0xa, 0x9, 0x3, 0xe, 0x5, 0x0, 0xc, 0x7,
                0x1, 0xf, 0xd, 0x8, 0xa, 0x3, 0x7, 0x4, 0xc, 0x5, 0x6, 0xb, 0x0, 0xe, 0x9, 0x2,
                0x7, 0xb, 0x4, 0x1, 0x9, 0xc, 0xe, 0x2, 0x0, 0x6, 0xa, 0xd, 0xf, 0x3, 0x5, 0x8,
                0x2, 0x1, 0xe, 0x7, 0x4, 0xa, 0x8, 0xd, 0xf, 0xc, 0x9, 0x0, 0x3, 0x5, 0x6, 0xb,
        ],
]
# P盒
P_box = [
        16, 7,20,21,29,12,28,17,
        1 ,15,23,26, 5,18,31,10,
        2 ,8 ,24,14,32,27, 3, 9,
        19,13,30, 6,22,11, 4,25,
]

        对每个 6 b i t 6bit 6bit分组都得到一个16进制数,然后16进制数转二进制串是 4 b i t 4bit 4bit,就实现了 48 b i t 48bit 48bit压缩成 32 b i t 32bit 32bit,得到的 32 b i t 32bit 32bit二进制串再经过 P _ b o x P\_box P_box得到 F F F函数的输出。
 

2.2 Python实现速度测试

   
核心代码

#F函数
def F(R,Ki): #R是32bits,Ki是48bits,
        R = translation(R, E)      #对R进行E拓展
        R = ''.join('0' if R[i]==Ki[i] else '1' for i in range(48))  #对R和Ki进行异或
        ij = [R[i:i+6] for i in range(0,48,6)]     # 按6bit分组
        S_box_loca = [int(ij[n][0]+ij[n][-1],2) * 16 + int(ij[n][1:-1],2) for n in range(8)] # 首尾拼接做行,中间四位做列,这里的行列不用减1,因为4行16列,已经包括0的可能性了
        R = ''.join(hex_bin[hex((S_box[n][S_box_loca[n]]))[-1]] for n in range(8))
        # 过P盒
        R = translation(R,P_box)
        return R

def festial(l,r,Ki):
        l_n = r
        r_n = F(l_n,Ki)
        r_n = ''.join('0'if l[i] ==r_n[i] else '1' for i in range(32))
        return l_n,r_n
def Enc_Dec(Input,Key,choice): #choice决定是加密还是解密
        Input_n= divide(Input)
        result=''
        Kn = get_Kn(Key)
        if choice=='2':
                Kn.reverse()
        for p in Pn:
                p = ''.join(hex_bin[i] for i in P)      #转为二进制
                #print(p)
                p = translation(p, IP)                  #IP置换
                # print(p)
                l = p[0:32]
                r = p[32:]
                for i in range(16):
                        l,r = feistel(l,r,Kn[i])       #16轮feistel
                l,r = r,l
                result = result + l+r
        result = translation(result,IP_1)               #IP_1 置换
        return result

python运行10次,求平均值运行时间如下:

测试1测试2测试3
0.0004034280776977539s0.00040314197540283205s0.0005030155181884766s

求得运行时间平均值 0.0004365 s 0.0004365s 0.0004365s

 
2.3 C++ 实现速度测试

   
核心代码:

string F(string r, string Ki)
{
	//cout << "r:\t" << r << endl;
	string r48 = "";
	string r32 = "";
	trans(r48, r, E, 48);  			//过E盒
	for (int i = 0; i < 48; i++)
	{
		r48[i] = r48[i] == Ki[i] ? '0' : '1';
	}
	//cout << "Ki^E\t" << r48 << endl;
	int p2[8] = { 0 };
	int p3[8] = { 0 };
	for (int i = 0; i < 8; i++) {
		char* str = &r48[i * 6];	//string字符串里单个字符是char类型
		p2[i] += str[0] == '1' ? 2 : 0;
		p2[i] += str[5] == '1' ? 1 : 0;
		for (int j = 1; j < 5; j++)
		{
			p3[i] += str[j] == '1' ? int(pow(2, 4 - j)) : 0;
		}
		//cout << endl;
		//cout << "p2[" << i << "]:\t" << p2[i] << "\tp3[" << i << "]:\t" << p3[i] << endl;*/
	}
	r32 = "";
	for (int i = 0; i < 8; i++)
	{
		r32 += int_bin(S_box[i][p2[i]][p3[i]]);
	}
	//cout << "S:\t" << r32 << endl;
	trans(r, r32, P_box, 32);
	//cout << "P:\t" << r << endl;
	return r;
}
void Feistel(string& l, string& r, string Ki)
{
	string r_n;
	string l_n;
	l_n = r;
	r_n = F(r, Ki);
	//r_n ^= l;
	for (int i = 0; i < 32; i++)
	{
		r_n[i] = r_n[i] == l[i] ? '0' : '1';
	}
	l = l_n;
	r = r_n;
}
string Enc_Dec(string In, string Key, int choice)
{
	string result = "";
	char* Kn_chr;
	get_Kn(Kn_chr, Key);
	string Kn_str[16];
	for (int i = 0; i < 16; i++)
	{
		int ij = i * 48;
		for (int j = 0; j < 48; j++)
		{
			Kn_str[i] += Kn_chr[ij + j];
		}
		//cout << "K" << i + 1 << ":\t" << Kn_str[i] << endl;;
	}
	delete Kn_chr;
	//Ki准备好后就要处理输入,分组进行加密,所以要得到一个Input_n[]
	int div = 0;
	string* In_n = Divide(In, div);
	string In_i = "";
	string res = "";
	string l = "";
	string r = "";
	for (int i = 0; i < div; i++)
	{
		In_i = hex_bin(In_n[i], 16);
		trans(In_i, In_i, IP, 64);  //IP置换
		l = In_i.substr(0, 32);
		r = In_i.substr(32, 64);
		for (int j = 0; j < 16; j++)
		{
			//cout << "\t\tRound:" << j + 1 << endl;
			Feistel(l, r, Kn_str[j]);
			//cout << endl;
		}
		res = r + l;
		trans(res, res, IP_1, 64);//IP逆置换
		result += res;
	}
	return result;
}

C++运行100次,求平均值运行时间如下:

测试1测试2测试3
0.00004s0.00005s0.00004s

        求得运行时间平均值 0.0000433 s 0.0000433s 0.0000433s,即 0.043 m s 0.043ms 0.043ms。我们可以看到C++使用String库函数,同样的算法结构是比python快1个数量级的。
 
 

题目

 

1 第一题

        实现DES加密算法,设明文为02468aceeca86420,密钥为0f1571c947d9e859,求对应的密文(明文左侧从1开始编号)。估计算法的运行时间。

答:
具体数据如前文所示

pythonC++
0.0004365s0.000043s

2 第二题

   

2.1 原轮函数

   
将代码轮数改为6轮,仍以第一题原文和密钥进行加密:

在这里插入图片描述
   
明文修改1bit:
在这里插入图片描述
   
密钥修改1bit:
在这里插入图片描述

选取其他位置的比特重复进行修改并记录密文。

 

2.2 删除E扩散

   
将E扩散修改为使得每个4bit的两端填充0,即相当于每次只查S盒的第0行 :


   

明文修改1bit:

在这里插入图片描述

   
密钥修改1bit:

选取其他位置的比特重复进行修改并记录密文。

 

2.3 删除S-box

   
将S盒的代码修改为取每个S-box输入的中间4bit为输出:


   

明文修改1bit:

在这里插入图片描述
   
密钥修改1bit:

在这里插入图片描述
   

选取其他位置的比特重复进行修改并记录密文。

 

2.4 删除 P P P置换

   
将P置换代码删除:

   
明文修改1bit:
在这里插入图片描述
   

密文修改1bit:

在这里插入图片描述

选取其他位置的比特重复进行修改并记录密文。
 

2.5数据统计

在这里插入图片描述

 
在这里插入图片描述
 
 

2.6 结论

        通过分析对比,在题目要求下的6轮轮函数下,不论是修改1bit明文还是1bit密钥,都会很快发生雪崩效应。

        删除E扩散、S盒或P置换都会对加密的扩散性产生影响。其中,删除S盒对加密算法的影响最大,其完全破坏了雪崩效应。在六轮轮函数运算中,每次的结果中变动的比特位数均为个位数。故其扩散性很差,六轮后大部分密文没有发生变化。

        E扩散和P置换也对雪崩效应有一定影响。综合分析,三者均对扩散起着至关重要的作用,影响密码的安全性,并且S盒的影响最大。

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值