通俗易懂,十分钟读懂DES,详解DES加密算法原理,DES攻击手段以及3DES原理。Python DES实现源码
Posted Demonslzh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通俗易懂,十分钟读懂DES,详解DES加密算法原理,DES攻击手段以及3DES原理。Python DES实现源码相关的知识,希望对你有一定的参考价值。
文章目录
1、什么是DES
DES(Data Encryption Standard)是一种对称加密算法。它是在20世纪70年代初期由IBM研发的。它的设计目标是提供高度的数据安全性和性能,并且能够在各种硬件和软件平台上实现。
2、DES的基本概念
DES使用56位的密钥和64位的明文块进行加密。DES算法的分组大小是64位,因此,如果需要加密的明文长度不足64位,需要进行填充;如果明文长度超过64位,则需要使用分组模式进行分组加密。
虽然DES算法的分组大小是64位,但是由于DES算法的密钥长度只有56位,因此DES算法存在着弱点,容易受到暴力破解和差分攻击等攻击手段的威胁。因此,在实际应用中,DES算法已经不再被广泛使用,而被更加安全的算法所取代,如AES算法等。
尽管DES已经被取代,但它在密码学的历史上仍然具有重要意义。通过DES可以帮助我们了解对称密钥加密算法的基本概念和运作原理。
3、DES的加密流程
当输入了一条64位的数据之后,DES将通过以下步骤进行加密。在第4部分中,我们对每个流程进行详解。
-
1、初始置换(IP置换):将输入的64位明文块进行置换和重新排列,生成新的64位数据块。
-
2、加密轮次:DES加密算法共有16个轮次,每个轮次都包括四个步骤:
-
a. 将64位数据块分为左右两个32位块。
-
b. 右侧32位块作为输入,经过扩展、异或、置换等操作生成一个48位的数据块。这个48位的数据块被称为“轮密钥”,它是根据加密算法的主密钥生成的子密钥。
-
c. 将左侧32位块和轮密钥进行异或运算,结果作为新的右侧32位块。
-
d. 将右侧32位块与原来的左侧32位块进行连接,生成一个新的64位数据块,作为下一轮的输入。
-
-
3 末置换(FP置换):在最后一个轮次完成后,将经过加密的数据块进行置换和重新排列,得到加密后的64位密文。
总的来说,DES加密的过程就是通过一系列置换、异或、扩展等运算,将明文分成若干个小块,然后根据主密钥生成一系列的轮密钥,利用轮密钥对每个小块进行加密,最终将加密结果重新组合成一个整体,得到密文。
4、DES算法步骤详解
4.1 初始置换(Initial Permutation,IP置换)
IP置换是将输入的64位明文块进行置换和重新排列,生成新的64位数据块。
目的:增加加密的混乱程度,使明文中的每一位都能够对后面的加密过程产生影响,提高加密强度。
我们将把64位的顺序按下表中规定的顺序放置,图中的数字是在64位明文中每个比特的索引位置。注意,在DES中,这个置放规则是固定的。
即将原来位于第58个位置的数据放在第1个位置,原来位于第50个位置的元素放在第2个位置,第42个放在第3个,34->4以此类推…
初始置换的逆置换(Final Permutation,FP置换)是将加密后的数据块进行置换和重新排列,得到最终的加密结果,与初始置换相对应。
4.2 加密轮次
初始置换完成后,明文被划分成了相同长度(32位)的左右两部分,记作L0,R0。接下来就会进行16个轮次的加密了。
我们从单独一个轮次来看。首先把目光聚焦在R0这里。
右半部分R0会作为下一轮次的左半部分L1的输入。其次,R0会补位到48位和本轮次生成的48位K0(马上讲K0的生成)输入到F轮函数中去。F函数的输出结果为32位,结果F(R0,K0)
会和L0
进行异或运算作为下一轮次右半部分R1的输入。
以此类推,重复16轮运算。所以,上面描述的过程可以用以下公式表述。
4.3 F轮函数
我们讲到在每轮加密中,会将R和K输入到F中,接下来我们看看F函数中做了哪些处理。
4.3.1 拓展R到48位
将32位的R0右半部分进行扩展,得到一个48位的数据块。同样的,数据拓展也是根据一个固定的置换表。红框中就是我们要补位的数据。
由此可见,扩展过程的每一位都是根据上述的置换表从输入的32位数据块中提取出来的。原始数据的第32位被补充到了新增列的第一个,第5位被补充到了第二个新增列的第一个,以此类推…
4.3.2 子密钥K的生成
DES算法采用了每轮子密钥生成的方式来增加密钥的复杂性和安全性。每轮子密钥都是由主密钥(64位)通过密钥调度算法(Key Schedule Algorithm)生成的。DES算法的密钥调度算法可以将64位的主密钥分成16个子密钥,每个子密钥48位,用于每轮加密中与输入数据进行异或运算。
通过子密钥生成的流程图来看下整个过程。
-
1、将64位主密钥经过置换选择1(Permuted Choice 1简写为PC-1)后输出了56位,将其分为左右两个28位的数据块,分别记为C0和D0。同上面我们讲过的置换规则一样,PC-1置换函数也是一个固定的置换表。
从PC-1的置换表中可以看到,舍弃掉的8位数据是原始数据中每8位数据的最后一位,也就是我们所熟知的奇偶检验位。这8位被丢弃是因为它们对于密钥的安全性没有贡献,而且能够使DES算法的计算速度更快。 -
2、对C0和D0进行循环左移操作。循环左移完成后生成C1和D1。因此,在16个轮次的计算当中会得到16个32位的数据块C1-C16和D1-D16。在DES中循环左移也有固定的规则。
对于i=1,2,…,16,对于Ci和Di,若i为1,2,9或16,则循环左移一位,否则循环左移两位。
-
3、 对于C1,D1,将它们经过置换选择2(Permuted Choice 2 简写位PC-2)后,得到48位的子密钥K1,用于每轮加密中与输入数据进行异或运算。PC-2置换的输入是由PC-1置换生成的56位的密钥,而它的输出是48位的子密钥。PC-2置换将56位的密钥重新排列,丢弃了8位并选取了其中的48位作为子密钥。PC-2的置换规则如下:
14 | 17 | 11 | 24 | 1 | 5 |
---|---|---|---|---|---|
3 | 28 | 15 | 6 | 21 | 10 |
23 | 19 | 12 | 4 | 26 | 8 |
16 | 7 | 27 | 20 | 13 | 2 |
41 | 52 | 31 | 37 | 47 | 55 |
30 | 40 | 51 | 45 | 33 | 48 |
44 | 49 | 39 | 56 | 34 | 53 |
46 | 42 | 50 | 36 | 29 | 32 |
即PC-2置换表的第一行表示选择了输入密钥中的第14、17、11、24、1和5位,并将它们作为输出子密钥的前6位。以此类推…
- 4、至此,经过PC-2后的结果就是我们当前轮次的子密钥K1了。在整个DES加密过程中会生成16个48位子密钥K1-K16,分别用于DES算法中的16轮加密过程,从而保证每轮加密所使用的密钥都是不同的,增加了破解的难度。
4.3.3 当前轮次的子密钥与拓展的48位R进行异或运算
当前轮次的子密钥Ki与拓展的48位Ri进行异或运算。运算结果会作为接下来S盒替换的输入
4.3.4 S盒替换(Substitution Box substitution)
S盒替换(Substitution Box substitution)是一种在密码学中广泛使用的加密技术。它是将明文中的一组比特映射到密文中的一组比特的过程,用于增强密码的安全性。DES中S盒替换用于将上一轮异或运算的48位结果映射到32位输出中去。
同样的,S盒也是一种置换表。在DES的每一轮计算中S盒都是不一样的。这里我以第一轮计算中的S盒为例。从上图中我们看到,S盒内部有8个S块,记作S1-S8。每个S块都会接收6位字符作为输入并输出四位字符。这里我们以第一个S盒S1为例。他是一个4*16的置换表。
14 | 4 | 13 | 1 | 2 | 15 | 11 | 8 | 3 | 10 | 6 | 12 | 5 | 9 | 0 | 7 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 15 | 7 | 4 | 14 | 2 | 13 | 1 | 10 | 6 | 12 | 11 | 9 | 5 | 3 | 8 |
4 | 1 | 14 | 8 | 13 | 6 | 2 | 11 | 15 | 12 | 9 | 7 | 3 | 10 | 5 | 0 |
15 | 12 | 8 | 2 | 4 | 9 | 1 | 7 | 5 | 11 | 3 | 14 | 10 | 0 | 6 | 13 |
例如输入101010
到S1中。S1会将这六位的第一位和第六位拿出来10
作为S1的行,中间四位0101
拿出来作为S1的列。我们转换成十进制,此时映射到这个S盒的位置就是(2,5)
对应S盒的第3行第6列(索引都从0开始数)。
所以这个输入的结果是6,将6转化为二进制110,S盒的输出是4位,所以得S(101010)=0110
因此,可以看到S盒其实是一种非线性的加密技术,它能够抵御许多传统的密码分析攻击,如差分攻击和线性攻击。
4.3.5 P盒替换
P盒替换将S盒替换的32位输出作为输入,经过上述固定的替换表进行替换后即为最后F轮函数的结果。
该结果F(R0,K0)与L0进行异或运算得到下一轮的右半部分R1
4.4 逆置换(Inverse Permutation)
在经过16轮次计算后,DES会对最后的结果进行最后一次置换。即为最后的输出结果。
5、DES的优缺点
优点:
- 安全性高:DES算法使用密钥进行加密和解密,相同的明文使用不同的密钥加密后得到的密文是不同的。密钥越长,加密的安全性就越高。
- 算法简单:DES算法的加密和解密过程非简单,基于对称加密,使用相同的key进行加解密。
- 适用广泛:DES算法是最早也是最广泛使用的加密算法之一,被广泛应用于电子商务、电子邮件、虚拟私人网络等领域,具有广泛的适用性和可移植性。
缺点:
- 密钥长度较短:DES算法使用56位密钥,虽然在当时足够安全,但在当前计算机的处理能力下,已经不足以保证加密的安全性,易受到暴力破解攻击。
- 无法抵抗差分密码分析攻击:DES算法无法抵抗差分密码分析攻击,这种攻击可以通过比较相同明文的密文,分析加密算法的行为并推断出密钥。
- 比较慢:由于DES算法是一种分组密码算法,需要对64位的明文进行加密,加密速度比较慢,不适用于对大量数据进行实时加密和解密。
6、DES的攻击方法
由于DES从诞生距今已经很多年了,但是仍然有部分老旧的系统会使用DES进行加密。因为其密钥长度较短(仅56位)和已知的弱点,因此容易受到以下攻击。
-
穷举攻击(Brute-Force Attack):由于DES算法的密钥长度较短,可能受到暴力破解攻击,攻击者可以通过穷举所有可能的密钥来尝试破解密文。尽管DES算法的加密速度比较慢,但现代计算机的计算能力很强,可以在合理时间内进行暴力破解攻击。
-
差分密码分析攻击(Differential Cryptanalysis Attack):差分密码分析是一种比较高效的攻击方式,可以通过对明文和密文之间的差异进行分析,推导出密钥。对于DES算法,攻击者可以通过分析不同的输入和输出差异,以及密钥可能取值的概率,从而获得密钥。
-
线性密码分析攻击(Linear Cryptanalysis Attack):线性密码分析是一种比较有效的攻击方式,可以通过线性近似计算找到密钥。对于DES算法,攻击者可以通过构造一些线性逼近,以及计算相应的概率,从而推导出密钥。
-
工作密钥攻击(Known Plaintext Attack):在工作密钥攻击中,攻击者可以获得一些已知明文和相应的密文,然后利用这些信息来推导出密钥。对于DES算法,攻击者可以通过获得足够的已知明文和密文,来推导出密钥。
-
生日攻击(Birthday Attack):生日攻击是一种利用概率学的攻击方式,可以在相对较短的时间内找到具有相同散列值的两个不同的输入。对于DES算法,攻击者可以使用生日攻击来找到两个不同的密钥,这些密钥都可以加密相同的明文。
7、3DES(Triple DES)
我们讲到了DES目前而言是不安全的。因此也诞生了3DES这样的算法来对DES进行加强。3DES顾名思义,就i是使用DES加密3次,使用3个密钥进行加解密。
为什么是3DES不是2DES
3DES使用了三个密钥,将DES算法的加密过程重复三次,从而大大增强了安全性。3DES的密钥长度为168位,远高于DES算法的56位密钥长度和2DES算法的112位密钥长度。2DES暴力破解的时间复杂度为O(257),仍然有很大的可能被暴力破解。
举个例子看下:
对于明文P,密文C, C由经过两次加密Ek1,Ek2得到K1,K2分别是第一二次加密的密钥。
- 遍历P到第一次加密的K1,最多有256种可能。
- 由于加解密是相同的key。所以我们同时还能对C进行暴力解密找key。同样有最多256种可能。
- 当我们遍历能找到一个Ek1(P)=Dk2(C) 使用这个密钥去加解密不同的明文和加密结果(P’,C’)。
- 如果能成功,那么此次暴力破解的时间复杂度为O(257)。因此依旧不够安全。
8、Python实现DES
由于篇幅过长,需要源码可以前往wechat公众号关注"一颗程序树",回复"DES"获取源码。
数据加密算法--详解DES算法原理与实现
DES算法简介
DES(Data Encryption Standard)是目前最为流行的加密算法之一。DES是对称的,也就是说它使用同一个密钥来加密和解密数据。
DES还是一种分组加密算法,该算法每次处理固定长度的数据段,称之为分组。DES分组的大小是64位,如果加密的数据长度不是64位的倍数,可以按照某种具体的规则来填充位。
从本质上来说,DES的安全性依赖于虚假表象,从密码学的术语来讲就是依赖于“混乱和扩散”的原则。混乱的目的是为隐藏任何明文同密文、或者密钥之间的关系,而扩散的目的是使明文中的有效位和密钥一起组成尽可能多的密文。两者结合到一起就使得安全性变得相对较高。
DES算法具体通过对明文进行一系列的排列和替换操作来将其加密。过程的关键就是从给定的初始密钥中得到16个子密钥的函数。要加密一组明文,每个子密钥按照顺序(1-16)以一系列的位操作施加于数据上,每个子密钥一次,一共重复16次。每一次迭代称之为一轮。要对密文进行解密可以采用同样的步骤,只是子密钥是按照逆向的顺序(16-1)对密文进行处理。
计算16个子密钥
上面提到DES算法的第一步就是从初始密钥中计算得出16个子密钥。图示1展示了这个过程。DES使用一个56位的初始密钥,但是这里提供的是一个64位的值,这是因为在硬件实现中每8位可以用于奇偶校验,在软件实现中多出的位只是简单的忽略掉。要获得一个56位的密钥,可以执照表1的方式执行密钥转换。解释一下表1,按照从左往右从上往下的方式看,表格中每个位置P包含初始密钥中位在转换后的密钥中所占的位置。比如,初始密钥中的第57位就是转换后的密钥中的第1位,而初始密钥中的第49位则变成转换后的密钥中的第2位,以此类推...。(数据位的计数顺序按照从左到右从1开始的)
表1:DES中密钥的转换表(DesTransform[56])
将密钥转换为56位后,接下来计算子密钥。首先,将56位的密钥分为两个28位的组。然后,针对每个子密钥,根据子密钥的序列值(也就是16个子密钥中的第几个)旋转这两组值(旋转的位数见表2),然后重新合并。之后,再按照表3所示对重组后的密钥进行置换,使56位的子密钥缩小为48位(注意表3只有48位,丢弃了8位)。这个排列过程就称为置换选择。
针对16个子密钥,每个子密钥重复一次该过程。这里的目的是保证将初始密钥中的不同位在每一轮排列后应用于加密的数据上。
表2:针对DES子密钥每一轮的旋转次数(Round轮,Rotations旋转次数)(DesRotations)
表3:DES子密钥的置换选择(Despermuted[48])
图1:在DES中计算子密钥的过程
加密和解密数据块
经过上述过程,我们已经准备好了子密钥。接着就可以加密和解密数据块了。图2展示了这个过程。
图2:DES中加密和解密数据块
从表4所示的方式置换64位的数据块开始,该置换过程称为初始置换。该过程并不会增加DES的安全性,但这种做法在16位和32位的总线出现之前将使得数据更容易加载到DES芯片中。虽然这种处理已经不合时宜,但该置换过程仍然保留以满足DES标准。
表4:DES中数据的初始置换(DesInitial[64])
经过初始置换后,64位的数据块分为两个32位的组,L0和R0。
完成初始置换后,数据块将重复执行16轮一系列的操作。每一轮操作(i)的目的是计算出Li和Ri ,这些结果将用在下一轮操作中直到最终得到数据R16和L16。
每一轮以Li-1和Ri-1开始,然后根据表5所示进行扩展置换,将Ri-1从32位扩展到48位。该置换的主要目的是在加密数据的过程中制造一些雪崩效应,使用数据块中的1位将在下一步操作中影响更多位,从而产生扩散效果。
一旦扩展置换完成,计算出48位的结果值与这一轮子密钥Ki的异或值(XOR,符号计为⊕)。这将产生48位的中间值,记为Rint。
如果将E计为扩展置换的结果,则本轮到目前为止的操作可以表示为:
Rint = E(Ri-1) ⊕ Ki
表5:DES中数据块的扩展置换(DesExpansion[48])
下一步,Rint 需要通过8个单独的S盒执行8次替换操作。每个S盒(j)从Rint的6j 到 6j+6 的位置取出6位,并为其在表6中查出1个4位的值,将该值写到缓冲区的4j位置处(如图3)。
图3:DES中的8个S盒
读表6,查找S盒(j)。通过前面取出的6位值,根据第1位和最后1位组成的2位值找到表6中的行号,而根据中间剩下的4位来确定表6中的列号。比如,在图3中,Rint中的第3个6位组是101011。因此,在表6中查找到的第3个S盒是9。因为行号等于112 = 3,列号等于01012 = 5(查表时从索引0开始计数)。S盒为数据增加了不确定性,除了给DES带来安全性外,没什么特别的。
表6:DES中数据块的S盒替换
一旦完成了S盒替换,得到的结果又变为一个32位的值。接下来再通过P盒来置换。如下表7所示。
表7:DES中数据块的P盒置换
到目前为止,我们把这一轮的操作想象为一个函数,一般记为f。如果 bj 代表Rint中的第j个6位组,Sj 代表第j个S盒,而P代表P盒置换,则该函数可以定义为:
f = P(S1(b1),S2(b2),...,S8(b8))
每一轮的最后一个操作是计算 f 的32位结果值与传入本轮操作的原始数据的左分组Li-1之间的异或值。
一旦完成,将左右两个分组交换然后开始下一轮。
在最后一轮中,不用交换左右分组。
把所有的步骤连起来,在每一轮中计算Li和Ri的步骤可以精确表示为:
Li = Ri-1
Ri = Li-1 ⊕ f(Ri-1,Ki)
当全部的16轮操作都结束后,将最后的右分组R16和最后剩下的左分组L16连接起来,组成一个64位的分组R16L16。(回顾一下,在最后一轮中左右分组并没有交换。最后的右分组在左边而最后的左分组在右边。)
最后一步是将R16L16按照表8所示的置换进行置换。简而言之,就是撤消之前的初始置换。
加密数据时,最终结果就是一个64位的密文,而当解密数据时,最终结果就是64位的明文了。
表8:DES中数据块的最终置换
DES的接口定义
des_encipher
void des_encipher(const unsigned char *plaintext, unsigned char *ciphertext, unsigned char *key);
返回值:无
描述:采用DES算法,将明文plaintext的一个64位的明文组加密。在key中指定64位的密钥(最后8位将被忽略掉,实际得到56位的密钥)。ciphertext是返回的64位的密文组。
由调用者负责管理ciphertext所需要的空间。要加密一段较大的数据,可以按照分组加密模式调用des_encipher。为了得到较高的效率,des_encipher可以重用之前的调用中计算出来的子密钥,这可以通过在之后的调用中将NULL传给key,以此来开启这种功能。
复杂度:O(1)
des_decipher
void des_decipher(const unsigned char *ciphertext, unsigned char *plaintext, unsigned char *key);
返回值:无
描述:采用DES算法将密文ciphertext的一个64位分组解密。该函数假设ciphertext包含的是通过des_encipher加密过的密文。在key中指定64位的密钥(最后8位将被忽略掉,实际得到56位的密钥)。plaintext是返回的64位的明文组。由调用者负责管理plaintext所需要的空间。要解密一大段的数据,可以按照分组加密模式调用des_decipher。为了获得较高的效率,des_decipher可以重用之前调用中计算出来的子密钥。可以在随后的调用中将NULL传给key,以此来开启这种功能。
复杂度:O(1)
DES算法的实现
考虑到DES算法中涉及的位操作很多,因此DES算法通常都是在硬件中实现。DES算法中的图表和术语(通过线、框画的流程图,以及诸如S盒、P盒这样的术语)使其更倾向于在硬件中实现,当然,软件实现也有它的价值所在。在软件开发中,通过几种基本的指令操作来帮助实现DES中的各种置换、转换以及替换操作都是很有效的。
des_encipher
函数des_encipher将明文的一个64位的明文分组通过DES算法加密。
由于DES的一个很好的特点是同样的过程既能用来加密数据也能用来解密数据,因此des_encipher只需要简单的调用des_main,而des_decipher同样也只需要调用des_main即可。
函数des_main通过使用其参数direction来确定到参数source提供的数据是明文还是密文。direction参数只是简单地修改子密钥的顺序。
在des_encipher中,direction设置为encipher。
函数des_main()首先检测参数key是否为NULL。这将允许des_encipher的调用者重用上一次调用时计算出来的子密钥。将子密钥数组subkeys声明为static类型。如果key不为NULL,将计算子密钥。
要计算子密钥,可以按照前面介绍过的步骤(计算16个子密钥)来执行。key的转换是通过函数permute来实现的,这里就是根据一个特定的表在一个缓冲区中置换位。假设表中的每个位置(i)上都存在一个值p,函数permute通过将位置p的位移动到位置(i)上来完成对传入的buffer的置换。
要置换密钥,将表Des_Transform(同表1)传给函数permute。必要的旋转操作可以通过调用位操作bit_rot_left来实现。该操作将buffer按照指定的位数向左旋转。每一轮要正确旋转28位的子密钥分组,将表Des_Rotations(同表2)中合适的元素传给bit_rot_left。通过调用permute,并把传入表Des_permuted(同表3),来对每一个子密钥做置换选择。
要加密一个数据块,首先要执行初始置换。为实现这一步,首先调用函数permute并将表Des_Initial(同表4)传入。然后,将数据分为两个32位的分组:lblk以及rblk。回顾一下,加密数据的大部分工作都是将一系列的操作重复执行16轮。每一轮的主要工作都花在计算函数(f)的值上,将值保存在fblk中。
每一轮操作开始时,将对rblk执行一个扩展置换。为了实现这个步骤,将调用函数permute,并把表Des_Expansion(同表5)传入。然后,通过调用位操作bit_xor来计算扩展后的rblk和某个适当的子密钥的异或值。无论是加密还是解密数据,与本轮操作相关的子密钥都需要参与执行。一旦完成了异或计算,将对结果执行一系列的S盒替换操作。Des_Sbox(同表6)定义了8个用于DES算法中的S盒。对于当前fblk中的每个6位分组,第1位和最后1位联合起来确定Des_Sbox中的行号,而中间的4位用来确定列号。最后执行P盒置换来完成函数f的计算。通过调用permute函数并传入表Des_Pbox(同表7)来实现这个步骤。计算出lblk与函数f的值的异或结果,并交换lblk和rblk来结束一轮的操作。
将上述过程重复16次,每轮一次。当全部16轮操作完成后,将rblk拷贝到target的前32位中,将lblk拷贝到之后的32位中(按照要求,最后一轮不交换lblk和rblk)。最终,通过调用permute并把Des_Final(同表8)传入来完成最后的置换操作。
des_encipher的时间复杂度是O(1),因为加密数据块的所有步骤都能在恒定的时间内完成。
des_decipher
函数des_decipher将一个64位的密文分组通过DES算法进行解密。同des_encipher一样,des_decipher实际通过调用des_main来完成解密任务,但这里direction需要设置为decipher。因此,des_decipher的工作方式同des_encipher一样,只是这里的子密钥需要以逆序的方式参与。具体来说,就是在des_main中,针对每一轮i(从0开始计数),参与计算的子密钥为subkeys数组中下标为(15-i)的元素。
des_decipher的时间复杂度为O(1),因为解密数据块中的所有步骤都可以在恒定的时间内完成。
示例1:数据加密的头文件(DES算法和RSA算法通用头文件)
/*encrypt.h*/ #ifndef ENCRYPT_H #define ENCRYPT_H /*在一个安全实现中,Huge 最少要400位10进制数字*/ typedef unsigned long Huge; /*为RSA公钥定义一个数据结构*/ typedef struct RsaPubKey_ { Huge e; Huge n; }RsaPubkey; /*为RSA私钥定义一个数据结构*/ typedef struct RsaPriKey_ { Huge d; Huge n; }RsaPriKey; /*函数声明*/ void des_encipher(const unsigned char *plaintext, unsigned char *ciphertext, const unsigned char *key); void des_decipher(const unsigned char *ciphertext, unsigned char *plaintext, const unsigned char *key); void rsa_encipher(Huge plaintext, Huge *ciphertext, RsaPubKey pubkey); void rsa_decipher(Huge ciphertext,Huge *plaintext, RsaPriKey prikey); #endif // ENCRYPT_H
示例2:DES算法的实现
/*des.c*/ #include <math.h> #include <stdlib.h> #include <string.h> #include "bit.h" #include "encrypt.h" /*定义一个密钥置换的映射*/ static const int DesTransform[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 }; /*定义用于计算子密钥的旋转次数*/ static const int DesRotations[16] = { 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1 }; /*定义用于子密钥置换选择的映射*/ static const int DesPermuted[48] = { 14,17,11,24,1,5,3,28,15,6,21,10, 23,19,12,4,26,8,16,7,27,20,13,2, 41,52,31,37,47,55,30,40,51,45,33,48, 44,49,39,56,34,53,46,42,50,36,29,32 }; /*定义用于数据块初始化转换的映射*/ static const int DesInitial[64] = { 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 }; /*定义用于数据块扩展转换的映射*/ static const int DesExpansion[48] = { 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,22,23,24,25, 24,25,26,27,28,29,28,29,30,31,32,1 }; /*定义用于数据块中S盒转换的S盒表*/ static const int DesSbox[8][4][16] = { { {14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7}, {0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8}, {4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0}, {15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13}, }, { {15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10}, {3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5}, {0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15}, {13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9}, }, { {10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8}, {13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1}, {13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7}, {1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12}, }, { {7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15}, {13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9}, {10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4}, {3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14}, }, { {2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9}, {14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6}, {4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14}, {11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3}, }, { {12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11}, {10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8}, {9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6}, {4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13}, }, { {4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1}, {13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6}, {1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2}, {6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12}, }, { {13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7}, {1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2}, {7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8}, {2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11}, } }; /*定义用于数据块转换的P盒映射表*/ static const int DesPbox[32] = { }; /*定义用于数据块最终转换的映射*/ static const int DesFinal[64] = { }; /*定义一个枚举类型,用于选择是加密或是解密数据*/ typedef enum DesEorD_ {encipher,decipher} DesEorD; /*permute函数 用于转换、改变位序列*/ static void permute(unsigned char *bits, const int *mapping, int n) { unsigned char *temp[8]; int i; /*使用n位映射映射缓冲区*/ memset(temp, 0, (int)ceil(n/8)); for(i=0; i<n; i++) bit_set(temp, i, bit_get(bits,mapping[i]-1)); memcpy(bits, temp, (int)ceil(n/8)); return; } /*des_main函数 加密或解密数据的计算函数*/ static int des_main(const unsigned char *source, unsigned char *target, const char *key, DesEorD direction) { static unsigned char subkeys[16][7]; unsigned char temp[8], lkey[4], rkey[4], lblk[6], rblk[6], fblk[6], xblk[6], sblk; int row,col,i,j,k,p; /*如果key等于NULL,重用上次调用时计算出来的子密钥,否则计算子密钥*/ if(key != NULL) { /*创建一个key的副本*/ memcpy(temp,key,8); /*将key转换并压缩至56位*/ permute(temp,DesTransform,56); /*将key分为两个28位的组*/ memset(lkey,0,4); memset(rkey,0,4); for(j=0; j<28; j++) bit_set(lkey, j, bit_get(temp,j)); for(j=0; j<28; j++) bit_set(rkey, j, bit_get(temp,j+28)); /*计算每一轮的子密钥*/ for(i=0; i<16; i++) { /*根据定义好的位数对每一块进行旋转*/ bit_rot_left(lkey,28,DesRotations[i]); bit_rot_left(rkey,28,DesRotations[i]); /*重新合并两个块*/ for(j=0; j<28; j++) bit_set(subkeys[i],j,bit_get(lkey,j)); for(j=0; j<28; j++) bit_set(subkeys[i],j+28,bit_get(rkey,j)); /*对子密钥做转换选择并压缩至48位*/ permute(subkeys[i],DesPermuted,48); } } /*创建source参数的副本*/ memcpy(temp, source, 8); /*初始转换数据块*/ permute(temp, DesInitial, 64); /*将源数据块分为大小为32位的左右两个数据块*/ memcpy(lblk, &temp[0], 4); memcpy(rblk, &temp[4], 4); /*加密或解密源数据*/ for(i=0; i<16; i++) { /*开始f缓冲冲的计算*/ memcpy(fblk,rblk,4); /*置换、扩展右数据块的拷贝,使其达到48位*/ permute(fblk, DesExpansion, 48); /*根据direction的值来应用适当的子密钥*/ if(direction == encipher) { /*加密数据,子密钥组以递增的顺序应用*/ bit_xor(fblk, subkeys[i], xblk, 48); memcpy(fblk, xblk, 6); } else { /*解密数据,子密钥组以递减的顺序应用*/ bit_xor(fblk, subkeys[15-i], xblk, 48); meycpy(fblk, xblk, 6); } /*执行S盒替换*/ p=0; for(j=0; j<8; j++) { /*计算出S盒表中的行号和列号*/ row = (bit_get(fblk,(j*6)+0)*2) + (bit_get(fblk,(j*6)+5)*1); col = (bit_get(fblk,(j*6)+1)*8) + (bit_get(fblk,(j*6)+2)*4) + (bit_get(fblk,(j*6)+3)*2) + (bit_get(fblk,(j*6)+4)*1); /*为当前的6位数据块做S盒替换*/ sblk = (unsigned char)DesSbox[j][row][col]; for (k=4; k<8; k++) { bit_set(fblk,p,bit_get(&sblk,k)); p++; } } /*为f缓冲区执行P盒替换*/ permute(fblk, DesPbox, 32); /*计算左数据块与f缓冲区的异或值*/ bit_xor(lblk, fblk, xblk, 32); /*设置本轮的左数据块*/ memcpy(lblk, rblk, 4); /*设置本轮的右数据块*/ memcpy(rblk, xblk, 4); } /*将目标正文设置为重新连接的左右两个数据块*/ memcpy(&target[0], rblk, 4); memcpy(&target[4], lblk, 4); /*执行最终置换*/ permute(target, DesFinal, 64); return 0; } /*des_encipher DES加密数据*/ void des_encipher(const unsigned char *plaintext, unsigned char *ciphertext, const unsigned char *key) { des_main(plaintext, ciphertext, key, encipher); return; } /*des_decipher DES解密数据*/ void des_decipher(const unsigned char *ciphertext, unsigned char *plaintext, const unsigned char *key) { des_main(ciphertext, plaintext, key, decipher); return; }
以上是关于通俗易懂,十分钟读懂DES,详解DES加密算法原理,DES攻击手段以及3DES原理。Python DES实现源码的主要内容,如果未能解决你的问题,请参考以下文章