Des 加解密 流程解析

http://www.hankcs.com/security/des-algorithm-illustrated.html
http://blog.sina.com.cn/s/blog_7722acb50102x93c.html

Des 加密 流程:

1. 如何取得16个子密钥

举个例子,取十六进制秘钥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

关于子秘钥的话题就到此为止了,接下来我们看看信息本身

2. 对明文数据初始置换(IP=Initial permutation)

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

3.利用16个子密钥 进行16次迭代

我们接着执行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

4. 最终变换

然后对其执行一个最终的变换 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轮迭代中,调转左右子秘钥的位置而已。

5. 其他软件验证

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

6. 算法实现中遇到的问题

在算法的实现中,有的实现:

static void ByteToBit(bool *Out, const char *In, int bits)
{
    for(int i=0; i<bits; ++i)
        Out[i] = (In[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<bits; ++i)
        Out[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);
    }
}

本文是用的是第二种方式。

7. 解密过程:

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,算法本身并没有任何变化。

8. C语言实现源码:

// 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
请按任意键继续. . .

该程序是在网上搜来程序的基础上修改的

9.另外一种 代码 实现方式

对应以上的解析流程,另外一种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

10 . 对第8节的C语言,修改一下:

得到如下源码:(保留第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<bits; ++i)
        Out[i] = (In[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<bits; ++i)
        Out[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
请按任意键继续. . .

欢迎交流

猜你喜欢

转载自blog.csdn.net/wowocpp/article/details/80136245