http://www.hankcs.com/security/des-algorithm-illustrated.html
http://blog.sina.com.cn/s/blog_7722acb50102x93c.html
Des 加密 流程:
举个例子,取十六进制秘钥K为:
K = 133457799BBCDFF1
K = 13 34 57 79 9B BC DF F1
K 在内存中共占8个字节,如果是 小端模式,低位在前,高位在后,bit0—bit7 ,低字节在前高字节在后
其对应的,实际的字节内容为:
0xC8 ,0x2C ,0xEA ,0x9E ,0xD9 ,0x3D ,0xFB ,0x8F
得到K的二进制表示:
K = 00010011 00110100 01010111 01111001 10011011 10111100 11011111 11110001
13 34 57 79 9B BC DF F1
C8 2C EA 9E D9 3D FB 8F
对每一位进行编号:
01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | |||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | |||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | |||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 0 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | |||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 |
2.密钥置换
不考虑每个字节的第8位,DES的密钥由64位减至56位,每个字节的第8位作为奇偶校验位。产生的56位密钥由下表生成(注意表中没有8,16,24,32,40,48,56和64这8位):
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
57 | 49 | 41 | 33 | 25 | 17 | 09 | 01 | 58 | 50 | 42 | 34 | 26 | 18 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 |
10 | 02 | 59 | 51 | 43 | 35 | 27 | 19 | 11 | 03 | 60 | 52 | 44 | 36 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 1 |
63 | 55 | 47 | 39 | 31 | 23 | 15 | 07 | 62 | 54 | 46 | 38 | 30 | 22 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 |
14 | 06 | 61 | 53 | 45 | 37 | 29 | 21 | 13 | 05 | 28 | 20 | 12 | 04 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
我们将得到56位的新秘钥:
K+ = 1111000 0110011 0010101 0101111 0101010 1011001 1001111 0001111
然后,将这个秘钥拆分为左右两部分,C0 和 D0,每半边都有28位。
比如,对于新秘钥,我们得到:
C0 = 1111000 0110011 0010101 0101111
D0 = 0101010 1011001 1001111 0001111
对于C0 ,我们现在创建16个块Cn , 1<=n<=16。每次Cn 都是由前面的Cn-1 移位而来。
具体说来,对于n = 1, 2, …, 16,在前一轮移位的结果上,使用下表进行一些次数的左移操作。
根据轮数,将Cn分别循环左移1位或2位
对于D0 ,我们现在创建16个块Dn , 1<=n<=16。每次Dn 都是由前面的Dn-1 移位而来。
具体说来,对于n = 1, 2, …, 16,在前一轮移位的结果上,使用下表进行一些次数的左移操作。
根据轮数,将Dn分别循环左移1位或2位
什么叫左移?左移指的是将除第一位外的所有位往左移一位,将第一位移动至最后一位。实际上是循环左移
Iteration Number of
Number Left Shifts
1 1
2 1
3 2
4 2
5 2
6 2
7 2
8 2
9 1
10 2
11 2
12 2
13 2
14 2
15 2
16 1
这意味着,C3 是C2 移位而来的,具体来说,通过2次左移位;C16 则是由C15 通过1次左移得到的。
同样,D3 是D2 移位而来的,具体来说,通过2次左移位;D16 则是由D15 通过1次左移得到的。
第一轮是循环左移1位。C0循环左移1位后得到C1如下:
Cx | content | index | num |
---|---|---|---|
C0 | 1111000011001100101010101111 | 1 | 1 |
C1 | _1110000110011001010101011111 | 2 | 1 |
C2 | __1100001100110010101010111111 | 3 | 2 |
C3 | ____0000110011001010101011111111 | 4 | 2 |
C4 | ______0011001100101010101111111100 | 5 | 2 |
C5 | ________1100110010101010111111110000 | 6 | 2 |
C6 | __________0011001010101011111111000011 | 7 | 2 |
C7 | ____________1100101010101111111100001100 | 8 | 2 |
C8 | ______________0010101010111111110000110011 | 9 | 1 |
C9 | _______________0101010101111111100001100110 | 10 | 2 |
C10 | _________________0101010111111110000110011001 | 11 | 2 |
C11 | ___________________0101011111111000011001100101 | 12 | 2 |
C12 | _____________________0101111111100001100110010101 | 13 | 2 |
C13 | _______________________0111111110000110011001010101 | 14 | 2 |
C14 | _________________________1111111000011001100101010101 | 15 | 2 |
C15 | ___________________________1111100001100110010101010111 | 16 | 1 |
C16 | ____________________________1111000011001100101010101111 |
D0循环左移1位后得到D1如下:
Dx | content | index | num |
---|---|---|---|
D0 | 0101010101100110011110001111 | 1 | 1 |
D1 | _1010101011001100111100011110 | 2 | 1 |
D2 | __0101010110011001111000111101 | 3 | 2 |
D3 | ____0101011001100111100011110101 | 4 | 2 |
D4 | ______0101100110011110001111010101 | 5 | 2 |
D5 | ________0110011001111000111101010101 | 6 | 2 |
D6 | __________1001100111100011110101010101 | 7 | 2 |
D7 | ____________0110011110001111010101010110 | 8 | 2 |
D8 | ______________1001111000111101010101011001 | 9 | 1 |
D9 | _______________0011110001111010101010110011 | 10 | 2 |
D10 | _________________1111000111101010101011001100 | 11 | 2 |
D11 | ___________________1100011110101010101100110011 | 12 | 2 |
D12 | _____________________0001111010101010110011001111 | 13 | 2 |
D13 | _______________________0111101010101011001100111100 | 14 | 2 |
D14 | _________________________1110101010101100110011110001 | 15 | 2 |
D15 | ___________________________1010101010110011001111000111 | 16 | 1 |
D16 | ____________________________0101010101100110011110001111 |
我们现在就可以得到第n轮的新秘钥Kn( 1<=n<=16)了。具体做法是,对每对拼合后的子秘钥CnDn,按表PC-2执行变换:
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
每对子秘钥有56位,但PC-2仅仅使用其中的48位。
去掉第9、18、22、25、35、38、43、54位,从56位变成48位,再按表的位置置换。
C1和D1合并之后,再经过置换选择表2生成48位的子秘钥K1。置换选择表2(PC-2)如下:
C1和D1再次经过循环左移变换,生成C2和D2,C2和D2合并,通过PC-2生成子秘钥K2。
以此类推,得到子秘钥K1~K16。需要注意其中循环左移的位数。
于是,第n轮的新秘钥Kn 的第1位来自组合子秘钥CnDn的第14位,第2位来自第17位,依次类推,知道新秘钥的第48位来自组合秘钥的第32位。
比如,对于第1轮的组合秘钥,我们有:
C1D1 = 1110000 1100110 0101010 1011111 1010101 0110011 0011110 0011110
01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | |
0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | |
1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | |
43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | |
0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 |
通过PC-2的变换后,得到:
K1 = 000110 110000 001011 101111 111111 000111 000001 110010
14 | 17 | 11 | 24 | 01 | 05 | 03 | 28 | 15 | 06 | 21 | 10 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | |
23 | 19 | 12 | 04 | 26 | 08 | 16 | 07 | 27 | 20 | 13 | 02 | |
0 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | |
41 | 52 | 31 | 37 | 47 | 55 | 30 | 40 | 51 | 45 | 33 | 48 | |
1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | |
44 | 49 | 39 | 56 | 34 | 53 | 46 | 42 | 50 | 36 | 29 | 32 | |
0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 0 |
同理,对于其他秘钥得到:
共得到16个子密钥:
K1 = 000110 110000 001011 101111 111111 000111 000001 110010
K2 = 011110 011010 111011 011001 110110 111100 100111 100101
K3 = 010101 011111 110010 001010 010000 101100 111110 011001
K4 = 011100 101010 110111 010110 110110 110011 010100 011101
K5 = 011111 001110 110000 000111 111010 110101 001110 101000
K6 = 011000 111010 010100 111110 010100 000111 101100 101111
K7 = 111011 001000 010010 110111 111101 100001 100010 111100
K8 = 111101 111000 101000 111010 110000 010011 101111 111011
K9 = 111000 001101 101111 101011 111011 011110 011110 000001
K10 = 101100 011111 001101 000111 101110 100100 011001 001111
K11 = 001000 010101 111111 010011 110111 101101 001110 000110
K12 = 011101 010111 000111 110101 100101 000110 011111 101001
K13 = 100101 111100 010111 010001 111110 101011 101001 000001
K14 = 010111 110100 001110 110111 111100 101110 011100 111010
K15 = 101111 111001 000110 001101 001111 010011 111100 001010
K16 = 110010 110011 110110 001011 000011 100001 011111 110101
关于子秘钥的话题就到此为止了,接下来我们看看信息本身
DES是一个基于组块的加密算法,这意味着无论输入还是输出都是64位长度的。也就是说DES产生了一种最多264种的变换方法。每个64位的区块被分为2个32位的部分,左半部分L和右半部分R。(这种分割只在特定的操作中进行。)
比如,取明文M为:
M = 0123456789ABCDEF
这里的M是16进制的,将M写成二进制,我们得到一个64位的区块:
M = 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
L = 0000 0001 0010 0011 0100 0101 0110 0111
R = 1000 1001 1010 1011 1100 1101 1110 1111
内存中的排布:
M = 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
80 C4 A2 E6 91 D5 B3 F7
char PlainText[8] = { 0x80, 0xC4, 0xA2, 0xE6, 0x91, 0xD5, 0xB3, 0xF7 };
M的第一位是0,最后一位是1,我们从左读到右。
第二步:加密数据的每个64位区块
对于明文数据M,我们计算一个初始变换IP(Initial permutation)。IP是重新变换数据M的每一位产生的。产生过程由下表决定,表格的下标对应新数据的下标,表格的数值x表示新数据的这一位来自旧数据的第x位。
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
参照上表,M的第58位成为IP的第1位,M的第50位成为IP的第2位,M的第7位成为IP的最后一位。
比如,对M的区块执行初始变换,得到:
M = 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
IP = 1100 1100 0000 0000 1100 1100 1111 1111 1111 0000 1010 1010 1111 0000 1010 1010
这里M的第58位是1,变成了IP的第1位。M的第50位是1,变成了IP的第2位。M的第7位是0,变成了IP的最后一位。
接着将变换IP分为32位的左半边L0 和32位的右半边R0 。
比如,对于上例,我们得到:
L0 = 1100 1100 0000 0000 1100 1100 1111 1111
R0 = 1111 0000 1010 1010 1111 0000 1010 1010
我们接着执行16个迭代,对1<=n<=16,使用一个函数f。函数f输入两个区块——一个32位的数据区块和一个48位的秘钥区块Kn ——输出一个32位的区块。定义+表示异或XOR。那么让n从1循环到16,我们计算
Ln = Rn-1
Rn = Ln-1 + f(Rn-1,Kn)
这样我们就得到最终区块,也就是n = 16 的 L16R16。这个过程说白了就是,我们拿前一个迭代的结果的右边32位作为当前迭代的左边32位。对于当前迭代的右边32位,将它和上一个迭代的f函数的输出执行XOR运算。
比如,对n = 1,我们有:
K1 = 000110 110000 001011 101111 111111 000111 000001 110010
L1 = R0 = 1111 0000 1010 1010 1111 0000 1010 1010
R1 = L0 + f(R0,K1)
剩下的就是f函数是如何工作的了。
为了计算f,我们首先拓展每个Rn-1,将其从32位拓展到48位。这是通过使用一张表来重复Rn-1中的一些位来实现的。我们称这个过程为函数E。也就是说函数E(Rn-1)输入32位输出48位。
定义E为函数E的输出,将其写成8组,每组6位。这些比特是通过选择输入的某些位来产生的,具体选择顺序按下表实现:
E BIT-SELECTION TABLE
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
也就是说E(Rn-1) 开头的三个比特分别来自Rn-1的第32、1和2位。E(Rn-1) 末尾的2个比特分别来自Rn-1的第32位和第1位。
比如,给定R0 ,我们可以计算出E(R0) :
R0 = 1111 0000 1010 1010 1111 0000 1010 1010
E(R0) = 011110 100001 010101 010101 011110 100001 010101 010101
(注意输入的每4位一个分组被拓展为输出的每6位一个分组。)
接着在f函数中,我们对输出E(Rn-1) 和秘钥Kn执行XOR运算:
Kn + E(Rn-1)
比如,对K1 , E(R0),我们有:
K1 = 000110 110000 001011 101111 111111 000111 000001 110010
E(R0) = 011110 100001 010101 010101 011110 100001 010101 010101
K1+E(R0) = 011000 010001 011110 111010 100001 100110 010100 100111.
到这里我们还没有完成f函数的运算,我们仅仅使用一张表将Rn-1 从32位拓展为48位,并且对这个结果和秘钥Kn执行了异或运算。我们现在有了48位的结果,或者说8组6比特数据。我们现在要对每组的6比特执行一些奇怪的操作:我们将它作为一张被称为“S盒”的表格的地址。每组6比特都将给我们一个位于不同S盒中的地址。在那个地址里存放着一个4比特的数字。这个4比特的数字将会替换掉原来的6个比特。最终结果就是,8组6比特的数据被转换为8组4比特(一共32位)的数据。
将上一步的48位的结果写成如下形式:
Kn + E(Rn-1) =B1B2B3B4B5B6B7B8,
每个Bi 都是一个6比特的分组,我们现在计算
S1(B1)S2(B2)S3(B3)S4(B4)S5(B5)S6(B6)S7(B7)S8(B8)
其中,Si(Bi) 指的是第i个S盒的输出。
为了计算每个S函数S1, S2,…, S8,取一个6位的区块作为输入,输出一个4位的区块。决定S1的表格如下:
S1
Column Number
Row
No. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 14 4 13 1 2 15 11 8 3 10 6 12 5 9 0 7
1 0 15 7 4 14 2 13 1 10 6 12 11 9 5 3 8
2 4 1 14 8 13 6 2 11 15 12 9 7 3 10 5 0
3 15 12 8 2 4 9 1 7 5 11 3 14 10 0 6 13
Row/Column | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 14 | 4 | 13 | 1 | 2 | 15 | 11 | 8 | 3 | 10 | 6 | 12 | 5 | 9 | 0 | 7 |
1 | 0 | 15 | 7 | 4 | 14 | 2 | 13 | 1 | 10 | 6 | 12 | 11 | 9 | 5 | 3 | 8 |
2 | 4 | 1 | 14 | 8 | 13 | 6 | 2 | 11 | 15 | 12 | 9 | 7 | 3 | 10 | 5 | 0 |
3 | 15 | 12 | 8 | 2 | 4 | 9 | 1 | 7 | 5 | 11 | 3 | 14 | 10 | 0 | 6 | 13 |
如果S1 是定义在这张表上的函数,B是一个6位的块,那么计算S1(B) 的方法是:B的第一位和最后一位组合起来的二进制数决定一个介于0和3之间的十进制数(或者二进制00到11之间)。设这个数为i。B的中间4位二进制数代表一个介于0到15之间的十进制数(二进制0000到1111)。设这个数为j。查表找到第i行第j列的那个数,这是一个介于0和15之间的数,并且它是能由一个唯一的4位区块表示的。这个区块就是函数S1 输入B得到的输出S1(B)。比如,对输入B = 011011 ,第一位是0,最后一位是1,决定了行号是01,也就是十进制的1 。中间4位是1101,也就是十进制的13,所以列号是13。查表第1行第13列我们得到数字5。这决定了输出;5是二进制0101,所以输出就是0101。也即S1(011011) = 0101。
对于S1(011000)解析: 第一位是0,最后一位是0,决定了行号是0,中间4位为1100,对应的是十进制12. 查表 第0行
第12列,这个数字是5 ,5 对应的4比特的 二进制是 0101,
这里设置为 bit0 =0 ,bit1= 1, bit2 = 0 , bit3 = 1 ,但是有的实现里面是设置为:
bit0 = 1 , bit1 = 0 , bit2 = 1 , bit3 = 0
同理,定义这8个函数S1,…,S8的表格如下所示:
S1
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
S2
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
S3
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
S4
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
S5
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
S6
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
S7
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
S8
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
例子:对于第一轮,我们得到这8个S盒的输出:
K1 + E(R0) = 011000 010001 011110 111010 100001 100110 010100 100111.
S1(B1)S2(B2)S3(B3)S4(B4)S5(B5)S6(B6)S7(B7)S8(B8) = 0101 1100 1000 0010 1011 0101 1001 0111
函数f的最后一步就是对S盒的输出进行一个变换来产生最终值:
f = P(S1(B1)S2(B2)…S8(B8))
变换P由如下表格定义。P输入32位数据,通过下标产生32位输出:
P
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
比如,对于8个S盒的输出:
S1(B1)S2(B2)S3(B3)S4(B4)S5(B5)S6(B6)S7(B7)S8(B8) = 0101 1100 1000 0010 1011 0101 1001 0111
我们得到
f = 0010 0011 0100 1010 1010 1001 1011 1011
那么,
R1 = L0 + f(R0 , K1 )
= 1100 1100 0000 0000 1100 1100 1111 1111
+ 0010 0011 0100 1010 1010 1001 1011 1011
= 1110 1111 0100 1010 0110 0101 0100 0100
在下一轮迭代中,我们的L2 = R1,这就是我们刚刚计算的结果。之后我们必须计算R2 =L1 + f(R1, K2),一直完成16个迭代。在第16个迭代之后,我们有了区块L16 and R16。
接着我们逆转两个区块的顺序得到一个64位的区块:(有的 网上实现的des.cpp算法里面没有这一步)
R16L16
然后对其执行一个最终的变换 IP-1 ,其定义如下表所示:
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
也就是说,该变换的的输出的第1位是输入的第40位,第2位是输入的第8位,一直到将输入的第25位作为输出的最后一位。
比如,如果我们使用了上述方法得到了第16轮的左右两个区块:
L16 = 0100 0011 0100 0010 0011 0010 0011 0100
R16 = 0000 1010 0100 1100 1101 1001 1001 0101
我们将这两个区块调换位置,然后执行最终变换:
R16L16 = 00001010 01001100 11011001 10010101 01000011 01000010 00110010 00110100
IP-1 = 10000101 11101000 00010011 01010100 00001111 00001010 10110100 00000101
写成16进制得到:
85E813540F0AB405
这就是明文M = 0123456789ABCDEF的加密形式C = 85E813540F0AB405。
解密就是加密的反过程,执行上述步骤,只不过在那16轮迭代中,调转左右子秘钥的位置而已。
1000 0101 1110 1000 0001 0011 0101 0100 0000 1111 0000 1010 1011 0100 0000 0101
8 5 E 8 1 3 5 4 0 F 0 A B 4 0 5
a1 17 c8 2a f0 50 2d a0
对应的小端存储的字节为:
0xa1,0x17,0xc8,0x2a,0xf0,0x50 ,0x2d ,0xa0
密钥:133457799BBCDFF1
数据:0123456789ABCDEF
结果:85E813540F0AB405
在算法的实现中,有的实现:
static void ByteToBit(bool *Out, const char *In, int bits)
{
for(int i=0; i>3]>>(7 - i&7)) & 1;
}
static void BitToByte(char *Out, const bool *In, int bits)
{
memset(Out, 0, bits>>3);
for(int i=0; i>3] |= In[i]<<(7 - i&7);
}
有的地方实现是:
void ByteToBit(bool *Out, const char *In, int bits)//字节组转换成位组
{
for(int i = 0; i < bits; i++)
{
Out[i] = (In[i/8] >> (i%8)) & 1;
}
}
void BitToByte(char *Out, const bool *In, int bits)
{
memset(Out, 0, (bits+7)/8);
for(int i = 0; i < bits; i++)
{
Out[i/8] |= In[i] << (i%8);
}
}
本文是用的是第二种方式。
void Des_Run(char Out[8], char In[8], bool Type)
{
static bool M[64], Tmp[32], *Li = &M[0], *Ri = &M[32];
ByteToBit(M, In, 64);// 字节组转换成位组
Transform(M, M, IP_Table, 64);// 变换
if( Type == ENCRYPT ){// 加密
for(int i=0; i<16; i++) {
memcpy(Tmp, Ri, 32);
F_func(Ri, SubKey[i]);
Xor(Ri, Li, 32);// 异或
memcpy(Li, Tmp, 32);
}
}else{//解密
for(int i=15; i>=0; i--) {
memcpy(Tmp, Ri, 32);
F_func(Ri, SubKey[i]);
Xor(Ri, Li, 32);// 异或
memcpy(Li, Tmp, 32);
}
}
memcpy(Tmp, Li, 32);
memcpy(Li, Ri, 32);
memcpy(Ri, Tmp, 32);
Transform(M, M, IPR_Table, 64);// 变换
BitToByte(Out, M, 64);
}
根据网上的文章 ,解密有两种一种是:调转左右子秘钥的位置,
一种是。DES算法的解密过程是一样的,区别仅仅在于第一次迭代时用子密钥K15,第二次K14、……,最后一次用K0,算法本身并没有任何变化。
// DES算法利用一个56+8奇偶校验位(第8,16,24,32,40,48,56,64位)=64位的密钥对以64位为单位的块数据进行加解密
#include "memory.h"
#include "stdio.h"
#include "conio.h"
enum {ENCRYPT,DECRYPT};// ENCRYPT:加密,DECRYPT:解密
void Des_Run(char Out[8], char In[8], bool Type=ENCRYPT);
void Des_SetKey(const char Key[8]);// 设置密钥
static void F_func(bool In[32], const bool Ki[48]);// f 函数
static void S_func(bool Out[32], const bool In[48]);// S 盒代替
static void Transform(bool *Out, bool *In, const char *Table, int len);// 变换
static void Xor(bool *InA, const bool *InB, int len);// 异或
static void RotateL(bool *In, int len, int loop);// 循环左移
static void ByteToBit(bool *Out, const char *In, int bits);// 字节组转换成位组
static void BitToByte(char *Out, const bool *In, int bits);// 位组转换成字节组
static void ByteToBit_Print(bool *Out, const char *In, int bits);
void ByteToBitDD_Print(bool *Out, const char *In, int bits);//字节组转换成位组
/*
* 子密钥生成:
* 1. 密钥先被转化成64比特的二进制;
* 2. 再按照密钥置换函数PC-1(8x7)进行压缩置换,变成56位;
* 3. 将其置换的输出再分为前28位C0和后28位D0两部分;
* 4. 进行16轮数据处理,每一轮中,先将C0和D0 分别 进行循环左移,
* 再将两部分左移结果 一起执行压缩置换PC-2(8X6),最后生成16个48位的子密钥。
*/
//PC1选位表(子密钥生成置换表1) 把密钥中的奇偶校验为去掉(不包含8,16,24,32,40,48,56,64),变成56位;
const static char PC1_Table[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
};
//PC2选位表(子密钥生成置换表2) 生成子密钥 48位
const static char PC2_Table[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
};
//左移位数表
const static char LOOP_Table[16] = {
1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1
};
/*
* 加密:DES算法是对固定大小(64位)的数据块进行加密解密操作的;
* 明文串M经过长度都为48位的16个子密钥K来加密,最后生成长度为64比特的密文E;经过长度都为48位的16个子密钥K来加密,最后生成长度为64比特的密文E;
* 1. 进行IP初始置换,将该明文串M(64bit)被分为32位的左半部分L0和32位的右半部分R0两部分;
* 2. 将数据的右半部分R0通过扩展置换E从32位扩展为48位;
* 3. 扩展后的48位与K1进行异或;
* 4. 异或后的结果通过S盒子转换为32位;
* 5. 输出结果再通过一个P盒置换产生一个32位的输出;
* 6. 最后,P盒置换的结果与左半部分进行异或运算,然后将左右两部分交换,之后进入下一轮迭代。
* 7. 在完成完全相同的16轮运算后,将得到的两部分数据L16和R16合在一起,即L16R16;(L16:指进行了16轮处理得到的32位左半部分数据)
* 8. 将L16与R16的位置交换,得到R16L16,再经过一个末置换函数IP-1,即可得到64位的密文。
*
*/
//置换IP表
const static char IP_Table[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
};
//逆置换IP-1表
const static char IPR_Table[64] = {
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
};
//E位选择表(扩展置换表)
static const char E_Table[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,20,21,22,23,24,25,
24,25,26,27,28,29,28,29,30,31,32,1
};
//P换位表(单纯换位表)
const static char P_Table[32] = {
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
};
// S盒
const static char S_Box[8][4][16] = {
// S1
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,
//S2
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,
//S3
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,
//S4
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,
//S5
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,
//S6
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,
//S7
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,
//S8
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
};
static bool SubKey[16][48];// 16圈子密钥
void printBufFormat(char *s,char * buf, int cnt, int num){
int i;
if (s != 0){
printf("%s\r\n", s);
}
for (i = 0; i < cnt; i++){
printf("%d", (unsigned char)buf[i]);
if ((i + 1) % num == 0){
printf("\r\n");
}
}
printf("\r\n");
}
void Des_Run(char Out[8], char In[8], bool Type)
{
static bool M[64], Tmp[32], *Li = &M[0], *Ri = &M[32];
ByteToBit(M, In, 64);// 字节组转换成位组
Transform(M, M, IP_Table, 64);// 变换
if( Type == ENCRYPT ){
for(int i=0; i<16; i++) {
memcpy(Tmp, Ri, 32);
F_func(Ri, SubKey[i]);
Xor(Ri, Li, 32);// 异或
memcpy(Li, Tmp, 32);
}
}else{
for(int i=15; i>=0; i--) {
memcpy(Tmp, Ri, 32);
F_func(Ri, SubKey[i]);
Xor(Ri, Li, 32);// 异或
memcpy(Li, Tmp, 32);
}
}
memcpy(Tmp, Li, 32);
memcpy(Li, Ri, 32);
memcpy(Ri, Tmp, 32);
Transform(M, M, IPR_Table, 64);// 变换
BitToByte(Out, M, 64);
}
void Des_SetKey(const char Key[8])
{
static bool K[64], *KL = &K[0], *KR = &K[28];
ByteToBit(K, Key, 64);// 字节组转换成位组
Transform(K, K, PC1_Table, 56);// 变换
for(int i=0; i<16; i++)
{
RotateL(KL, 28, LOOP_Table[i]);// 循环左移
RotateL(KR, 28, LOOP_Table[i]);// 循环左移
Transform(SubKey[i], K, PC2_Table, 48);// 变换
}
}
void F_func(bool In[32], const bool Ki[48])// f 函数
{
static bool MR[48];
//printBufFormat("In:", (char*)In, 32, 8);
//printBufFormat("Ki:", (char*)Ki, 48, 8);
Transform(MR, In, E_Table, 48);// 变换
//printBufFormat("MR A:", (char*)MR, 48, 6);
Xor(MR, Ki, 48);// 异或
//printBufFormat("MR B:", (char*)MR, 48, 6);
S_func(In, MR);// S 盒代替
//printBufFormat("In A:", (char*)In, 32, 8);
Transform(In, In, P_Table, 32);// 变换
//printBufFormat("In B:", (char*)In, 32, 8);
//getchar();
}
void S_func(bool Out[32], const bool In[48])// S 盒代替,输入6位的数,输出4位的数,将第一六位对应的十进制数作为行,第二三四五为的对应十进制数作为列
{
for(char i=0,j,k; i<8; i++,In+=6,Out+=4) // 输出数据存放位置out下标不断增进
{
j = (In[0]<<1) + In[5];
k = (In[1]<<3) + (In[2]<<2) + (In[3]<<1) + In[4];
//printf("i=%d,j=%d,k= %d,%d\r\n", i, j, k, S_Box[i][j][k]);
ByteToBitDD_Print(Out, &S_Box[i][j][k], 4); // out每次都会前进4个位置(bool)
//printBufFormat("Out:", (char*)Out, 32, 8);
//getchar();
}
}
void Transform(bool *Out, bool *In, const char *Table, int len)//变换
{
static bool Tmp[256];
for(int i = 0; i < len; i++)
{
Tmp[i] = In[ Table[i] - 1 ];
//printf("%d", Tmp[i]);
//if ((i + 1) % 7 == 0){
// printf("\r\n");
//}
}
//printf("\r\n");
memcpy(Out, Tmp, len);
}
void Xor(bool *InA, const bool *InB, int len)//异或
{
for(int i = 0; i < len; i++)
{
InA[i] ^= InB[i];
}
}
void RotateL(bool *In, int len, int loop)//循环左移
{
static bool Tmp[256];
memcpy(Tmp, In, loop);
memcpy(In, In+loop, len-loop);
memcpy(In+len-loop, Tmp, loop);
}
void ByteToBit(bool *Out, const char *In, int bits)//字节组转换成位组
{
for(int i = 0; i < bits; i++)
{
Out[i] = (In[i/8] >> (i%8)) & 1;
}
}
void ByteToBit_Print(bool *Out, const char *In, int bits)//字节组转换成位组
{
for (int i = 0; i < bits; i++)
{
Out[i] = (In[i / 8] >> (i % 8)) & 1;
printf("%d", Out[i]);
if ((i + 1) % 8 == 0){
printf("\r\n");
}
}
printf("\r\n");
}
void ByteToBitDD_Print(bool *Out, const char *In, int bits)//字节组转换成位组
{
for (int i = 0; i < bits; i++)
{
Out[bits-i-1] = (In[i / 8] >> (i % 8)) & 1;
//printf("%d", Out[i]);
//if ((i + 1) % 8 == 0){
// printf("\r\n");
//}
}
//printf("\r\n");
}
void BitToByte(char *Out, const bool *In, int bits)
{
memset(Out, 0, (bits+7)/8);
for(int i = 0; i < bits; i++)
{
Out[i/8] |= In[i] << (i%8);
}
}
void printBuf(char *strlog,char *buf, int num)
{
int i;
if (strlog != 0){
printf("%s\r\n", strlog);
}
for (i = 0; i < num; i++)
{
printf("%.2x ",(unsigned char ) buf[i]);
}
printf("\r\n");
}
int main()
{
//char key[8] = { 1, 9, 8, 0, 9, 1, 7, 2 };
char key[8] = { 0xC8 ,0x2C ,0xEA ,0x9E ,0xD9 ,0x3D ,0xFB ,0x8F };
char PlainText[8] = { 0x80, 0xC4, 0xA2, 0xE6, 0x91, 0xD5, 0xB3, 0xF7 };
char EncryptMsg[8] = { 0x00 };
char DecryptMsg[8] = { 0x00 };
puts("Before encrypting");
printBuf("PlainText:", PlainText, 8);
Des_SetKey(key);
Des_Run(EncryptMsg, PlainText, ENCRYPT);
printBuf("After Encrypting", EncryptMsg, 8);
Des_Run(DecryptMsg, EncryptMsg, DECRYPT);
printBuf("After Decrypting", DecryptMsg, 8);
return 0;
}
将源码 放到 vs2013中的建立的空的控制台工程,\VC\work\Des_TestA\
运行结果:
Before encrypting
PlainText:
80 c4 a2 e6 91 d5 b3 f7
After Encrypting
a1 17 c8 2a f0 50 2d a0
After Decrypting
80 c4 a2 e6 91 d5 b3 f7
请按任意键继续. . .
该程序是在网上搜来程序的基础上修改的
对应以上的解析流程,另外一种C源码,解析方式,
可以参考如下链接:
https://blog.csdn.net/wowocpp/article/details/80179651
这个链接的算法,实现了:
void Test_Des_B(void)
{
unsigned char key[8] = { 0x13,0x34,0x57,0x79,0x9B,0xBC,0xDF,0xF1};
unsigned char data[8] = { 0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF };
unsigned char EncryptData[8] = { 0 };
des_debug((uint8_t *)&data, (uint8_t *)key, 0, (uint8_t *)EncryptData);
printf("\r\nDes:\r\n");
printBuf("key:", key, 8, 32);
printBuf("plain:", data, 8, 32);
printBuf("EncryptData:", EncryptData, 8, 32);
}
输出:
Des:
key: 13 34 57 79 9b bc df f1
plain: 01 23 45 67 89 ab cd ef
EncryptData: 85 e8 13 54 0f 0a b4 05
得到如下源码:(保留第8节的目的是 对比一下 修改的地方,加深对算法的理解)
// DES算法利用一个56+8奇偶校验位(第8,16,24,32,40,48,56,64位)=64位的密钥对以64位为单位的块数据进行加解密
#include "memory.h"
#include "stdio.h"
#include "conio.h"
enum {ENCRYPT,DECRYPT};// ENCRYPT:加密,DECRYPT:解密
void Des_Run(char Out[8], char In[8], bool Type=ENCRYPT);
void Des_SetKey(const char Key[8]);// 设置密钥
static void F_func(bool In[32], const bool Ki[48]);// f 函数
static void S_func(bool Out[32], const bool In[48]);// S 盒代替
static void Transform(bool *Out, bool *In, const char *Table, int len);// 变换
static void Xor(bool *InA, const bool *InB, int len);// 异或
static void RotateL(bool *In, int len, int loop);// 循环左移
static void ByteToBit(bool *Out, const char *In, int bits);// 字节组转换成位组
static void BitToByte(char *Out, const bool *In, int bits);// 位组转换成字节组
/*
* 子密钥生成:
* 1. 密钥先被转化成64比特的二进制;
* 2. 再按照密钥置换函数PC-1(8x7)进行压缩置换,变成56位;
* 3. 将其置换的输出再分为前28位C0和后28位D0两部分;
* 4. 进行16轮数据处理,每一轮中,先将C0和D0 分别 进行循环左移,
* 再将两部分左移结果 一起执行压缩置换PC-2(8X6),最后生成16个48位的子密钥。
*/
//PC1选位表(子密钥生成置换表1) 把密钥中的奇偶校验为去掉(不包含8,16,24,32,40,48,56,64),变成56位;
const static char PC1_Table[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
};
//PC2选位表(子密钥生成置换表2) 生成子密钥 48位
const static char PC2_Table[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
};
//左移位数表
const static char LOOP_Table[16] = {
1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1
};
/*
* 加密:DES算法是对固定大小(64位)的数据块进行加密解密操作的;
* 明文串M经过长度都为48位的16个子密钥K来加密,最后生成长度为64比特的密文E;经过长度都为48位的16个子密钥K来加密,最后生成长度为64比特的密文E;
* 1. 进行IP初始置换,将该明文串M(64bit)被分为32位的左半部分L0和32位的右半部分R0两部分;
* 2. 将数据的右半部分R0通过扩展置换E从32位扩展为48位;
* 3. 扩展后的48位与K1进行异或;
* 4. 异或后的结果通过S盒子转换为32位;
* 5. 输出结果再通过一个P盒置换产生一个32位的输出;
* 6. 最后,P盒置换的结果与左半部分进行异或运算,然后将左右两部分交换,之后进入下一轮迭代。
* 7. 在完成完全相同的16轮运算后,将得到的两部分数据L16和R16合在一起,即L16R16;(L16:指进行了16轮处理得到的32位左半部分数据)
* 8. 将L16与R16的位置交换,得到R16L16,再经过一个末置换函数IP-1,即可得到64位的密文。
*
*/
//置换IP表
const static char IP_Table[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
};
//逆置换IP-1表
const static char IPR_Table[64] = {
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
};
//E位选择表(扩展置换表)
static const char E_Table[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,20,21,22,23,24,25,
24,25,26,27,28,29,28,29,30,31,32,1
};
//P换位表(单纯换位表)
const static char P_Table[32] = {
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
};
// S盒
const static char S_Box[8][4][16] = {
// S1
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,
//S2
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,
//S3
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,
//S4
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,
//S5
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,
//S6
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,
//S7
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,
//S8
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
};
static bool SubKey[16][48];// 16圈子密钥
void printBufFormat(char *s,char * buf, int cnt, int num){
int i;
if (s != 0){
printf("%s\r\n", s);
}
for (i = 0; i < cnt; i++){
printf("%d", (unsigned char)buf[i]);
if ((i + 1) % num == 0){
printf("\r\n");
}
}
printf("\r\n");
}
void Des_Run(char Out[8], char In[8], bool Type)
{
static bool M[64], Tmp[32], *Li = &M[0], *Ri = &M[32];
ByteToBit(M, In, 64);// 字节组转换成位组
Transform(M, M, IP_Table, 64);// 变换
if( Type == ENCRYPT ){
for(int i=0; i<16; i++) {
memcpy(Tmp, Ri, 32);
F_func(Ri, SubKey[i]);
Xor(Ri, Li, 32);// 异或
memcpy(Li, Tmp, 32);
}
}else{
for(int i=15; i>=0; i--) {
memcpy(Tmp, Ri, 32);
F_func(Ri, SubKey[i]);
Xor(Ri, Li, 32);// 异或
memcpy(Li, Tmp, 32);
}
}
memcpy(Tmp, Li, 32);
memcpy(Li, Ri, 32);
memcpy(Ri, Tmp, 32);
Transform(M, M, IPR_Table, 64);// 变换
BitToByte(Out, M, 64);
}
void Des_SetKey(const char Key[8])
{
static bool K[64], *KL = &K[0], *KR = &K[28];
ByteToBit(K, Key, 64);// 字节组转换成位组
Transform(K, K, PC1_Table, 56);// 变换
for(int i=0; i<16; i++)
{
RotateL(KL, 28, LOOP_Table[i]);// 循环左移
RotateL(KR, 28, LOOP_Table[i]);// 循环左移
Transform(SubKey[i], K, PC2_Table, 48);// 变换
}
}
void F_func(bool In[32], const bool Ki[48])// f 函数
{
static bool MR[48];
//printBufFormat("In:", (char*)In, 32, 8);
//printBufFormat("Ki:", (char*)Ki, 48, 8);
Transform(MR, In, E_Table, 48);// 变换
//printBufFormat("MR A:", (char*)MR, 48, 6);
Xor(MR, Ki, 48);// 异或
//printBufFormat("MR B:", (char*)MR, 48, 6);
S_func(In, MR);// S 盒代替
//printBufFormat("In A:", (char*)In, 32, 8);
Transform(In, In, P_Table, 32);// 变换
//printBufFormat("In B:", (char*)In, 32, 8);
//getchar();
}
void S_func(bool Out[32], const bool In[48])// S 盒代替,输入6位的数,输出4位的数,将第一六位对应的十进制数作为行,第二三四五为的对应十进制数作为列
{
for(char i=0,j,k; i<8; i++,In+=6,Out+=4) // 输出数据存放位置out下标不断增进
{
j = (In[0]<<1) + In[5];
k = (In[1]<<3) + (In[2]<<2) + (In[3]<<1) + In[4];
for (int l = 0; l<4; ++l) //把相应4bit赋值
Out[l] = (S_Box[i][j][k] >> (3 - l)) & 1;
}
}
void Transform(bool *Out, bool *In, const char *Table, int len)//变换
{
static bool Tmp[256];
for(int i = 0; i < len; i++)
{
Tmp[i] = In[ Table[i] - 1 ];
}
memcpy(Out, Tmp, len);
}
void Xor(bool *InA, const bool *InB, int len)//异或
{
for(int i = 0; i < len; i++)
{
InA[i] ^= InB[i];
}
}
void RotateL(bool *In, int len, int loop)//循环左移
{
static bool Tmp[256];
memcpy(Tmp, In, loop);
memcpy(In, In+loop, len-loop);
memcpy(In+len-loop, Tmp, loop);
}
void ByteToBit(bool *Out, const char *In, int bits)
{
for (int i = 0; i> 3] >> (7 - i & 7)) & 1;
}
void BitToByte( char *Out, const bool *In, int bits)
{
memset(Out, 0, (bits + 7) / 8);
for (int i = 0; i> 3] |= In[i] << (7 - i & 7);
}
void printBuf(char *strlog,char *buf, int num)
{
int i;
if (strlog != 0){
printf("%s\r\n", strlog);
}
for (i = 0; i < num; i++)
{
printf("%.2x ",(unsigned char ) buf[i]);
}
printf("\r\n");
}
int main()
{
char key[8] = { 0x13,0x34 ,0x57 ,0x79 ,0x9b ,0xbc ,0xdf ,0xf1 };
char PlainText[8] = { 0x01 ,0x23 ,0x45 ,0x67 ,0x89 ,0xab ,0xcd ,0xef };
char EncryptMsg[8] = { 0x00 };
char DecryptMsg[8] = { 0x00 };
puts("Before encrypting");
printBuf("PlainText:", PlainText, 8);
Des_SetKey(key);
Des_Run(EncryptMsg, PlainText, ENCRYPT);
printBuf("After Encrypting", EncryptMsg, 8);
Des_Run(DecryptMsg, EncryptMsg, DECRYPT);
printBuf("After Decrypting", DecryptMsg, 8);
return 0;
}
运行结果:
Before encrypting
PlainText:
01 23 45 67 89 ab cd ef
After Encrypting
85 e8 13 54 0f 0a b4 05
After Decrypting
01 23 45 67 89 ab cd ef
请按任意键继续. . .
欢迎交流