DES(Data Encryption Standard), 出自于IBM的研究项目, 1977 年被美国政府正式采用. 整个编码的过程都是对 BIT 作运算, DES 编码是用一组 8 bytes 的密钥来对 8 bytes 的资料进行加密, 下面我们将伴随着案例来说明 DEA (Data Encryption Algorithm).
假设我们以 "133457799BBCDFF1" 为密钥, 需加密的资料为 "0123456789ABCDEF", 由以下算法可得知加密后的资料为 "85E813540F0AB405".
1. 首先以上述密钥为基础算出 16 个 subkeys, 每一个 subkey 是 48 bits.
先将 "133457799BBCDFF1" 转换成 binary string, 转换表如下:
%codemap = ('0' => '0000', '1' => '0001', '2' => '0010', '3' => '0011',
'4' => '0100', '5' => '0101', '6' => '0110', '7' => '0111',
'8' => '1000', '9' => '1001', 'A' => '1010', 'B' => '1011',
'C' => '1100', 'D' => '1101', 'E' => '1110', 'F' => '1111');
于是经过转换表可以得出 MK = "0001001100110100010101110111100110011011101111001101111111110001"
再来需要一个置换表将其转换成 56 bits 的 binary string, 置换表如下:
@pc1map = (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);
要注意这一个阵列的所表述的数值 X 就是表示 MK[X-1], 按此阵列派序可得到 K0 = "11110000110011001010101011110101010101100110011110001111"
将 K0 分成左右两个部分, 每个部分是 28 bits binary string
C0: 1111000011001100101010101111
D0: 0101010101100110011110001111
由 C0, D0 可算出 Ci, Di (i=1..16), C1, D1 是 C0, D0 分别往左位移 1 单元, 而 C0, D0 的第一个单元分别为 C1, D1 的最后一个单元.下位移表则可算出 Ci 与 Di:
%iterations = ( 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 单元, 则 C2 的第一单元为 C3 的倒数第二单元, C2 的第二单元为 C3 的最后一个单元. D3 的操作亦同.
所以可以得到:
将 Ci, Di 组合起来, Li = Ci + Di, 再经过依下的置换阵列运算可得 16 个 subkeys Ki (i=1..16)
@pc2map = (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);
由上阵列置换既可得以下 16 个 48 bits 的 subkeys:
K1 = 000110110000001011101111111111000111000001110010
K2 = 011110011010111011011001110110111100100111100101
K3 = 010101011111110010001010010000101100111110011001
K4 = 011100101010110111010110110110110011010100011101
K5 = 011111001110110000000111111010110101001110101000
K6 = 011000111010010100111110010100000111101100101111
K7 = 111011001000010010110111111101100001100010111100
K8 = 111101111000101000111010110000010011101111111011
K9 = 111000001101101111101011111011011110011110000001
K10 = 101100011111001101000111101110100100011001001111
K11 = 001000010101111111010011110111101101001110000110
K12 = 011101010111000111110101100101000110011111101001
K13 = 100101111100010111010001111110101011101001000001
K14 = 010111110100001110110111111100101110011100111010
K15 = 101111111001000110001101001111010011111100001010
K16 = 110010110011110110001011000011100001011111110101
2. 处理完密钥的部分, 接下来开始运用这 16 个 subkeys 对资料进行编码, 首先一样先对 64 bits 的资料转换成 binary string
"0123456789ABCDEF" => M = "0000000100100011010001010110011110001001101010111100110111101111"
再对 M 进行第一次的置换, 置换规则如下阵列:
@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 转换成 IP = '1100110000000000110011001111111111110000101010101111000010101010'
就象第一步骤一样将 IP 分成左右两个部分 L0 及 R0, 每个部分长度为 32 bits binary string
L[0] = 11001100000000001100110011111111
R[0] = 11110000101010101111000010101010
再来根据 L[0], R[0] , 依下迭代规这可以计算出 L[i] 及 R[i] (i=1..16)
L[i] = R[i-1]
R[i] = L[i-1] + F(R[i-1], K[i])
上式中的 F 是一个函数, 而加法的处理是一个 XOR ON BIT 的运算, 其算法可以下表达式描述:
sub subxor { my ($a, $b) = @_; my @as = split(//, $a); my @bs = split(//, $b); my @targets; for ($z=0; $z<@as; $z++) { if ($as[$z] eq '0' && $bs[$z] eq '0') { $targets[$z] = '0'; } elsif ($as[$z] eq '1' && $bs[$z] eq '1') { $targets[$z] = '0'; } else { $targets[$z] = '1'; } } return join '', @targets; }现在来说明下 F 这一个函数的作用, F 接受两个参数 A, B, 但 A 所代表的 R[i-1] 长度为 32, 而 B 所代表的 K[i] 是一个长度为 48 的 subkey, 因此我们需将 A 扩充到长度为 48 的 G, 而扩充的算法如下:
@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);
以计算 R[1] 为例, 要对 F(R[0], K[1]) 计算, 扩充后的 R[0], 也就是函数中的 G = '011110100001010101010101011110100001010101010101', 得到 G 后, 将 G 与 B 作 XOR ON BIT, 可得到 H, 也就是 H = subxor(G, B) = '011000010001011110111010100001100110010100100111', 将 H 分成 8 段, 每一段的长度为 6, 既可得以下阵列:
I[8] = ('011000', '010001', '011110', '111010', '100001', '100110', '010100', '100111')
在此每一个字串都分别代表这一个阵列的索引值, 所以可想而知会有 8 个阵列, 这 8 个阵列如下表述:
为了方便说明起见, 将这 8 个阵列视为二维阵列, 并用一个三维阵列 sboxes 将其攘括起来, 所以 sboxes 是一个三维阵列, 其逻辑表达式为 @sboxes = (@sb1, @sb2, @sb3, @sb4, @sb5, @sb6, @sb7, @sb8), 先回过头来回顾刚刚所得到的I[8] = ('011000', '010001', '011110', '111010', '100001', '100110', '010100', '100111'), 将 I[8] 的每一个元素单独拉出来, 如I[0] = '011000', 头尾两个数字组合得 '00', 中见四个数字组合得 '1100', 将其转为十进位得 0, 12, 这对应到的是 @sb1 的第 0 列 (ROW) 及第 12 行 (COL) 所代表的值, 列与行数从 0 开始, 所以I[0] 其实就是代表 @sb1[0][12], 查上表可知 sb1[0][12] = 5, 按照这规则依序把 I[1]..I[7] 都计算出来如下所示:
I[0] = '011000' => ('00', '1100') => (0, 12) => sb1[0][12] = 5
I[1] = '010001' => ('01', '1000') => (1, 8) => sb2[1][8] = 12
I[2] = '011110' => ('00', '1111') => (0, 15) => sb3[0][15] = 8
I[3] = '111010' => ('10', '1101') => (2, 13) => sb4[2][13] = 2
I[4] = '100001' => ('11', '0000') => (3, 0) => sb5[3][0] = 11
I[5] = '100110' => ('10', '0011') => (2, 3) => sb6[2][3] = 5
I[6] = '010100' => ('00', '1010') => (0, 10) => sb7[0][11] = 9
I[7] = '100111' => ('11', '0011') => (3, 3) => sb8[3][3] = 7
将这些得到的值依序丢入阵列 @SBS, 既可得 @SBS = (5, 12, 8, 2, 11, 5, 9, 7), 再将 @SBS 中的每一个元素都分别拉出来, 如 SBS[0] = 5 转换成二进位字串为 '0101', 则 SBS[1] = 12 = '1100', 依序往后计算则可得到:
SBS[0] = 5 => '0101' SBS[1] = 12 => '1100'
SBS[2] = 8 => '1000' SBS[3] = 2 => '0010'
SBS[4] = 11 => '1011' SBS[5] = 5 => '0101'
SBS[6] = 9 => '1001' SBS[7] = 7 => '0111'
再将这些得到的字串全都依序组合起来就可以得到 J = '01011100100000101011010110010111', 此时 J 的长度为 32, 但还需经过一个置换, 置换阵列如下:
@sp = (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);
经过置换后将可以得到 F 函数的结果 H = '00100011010010101010100110111011', 再回到计算 L1, R1 的式子上
L[1] = R[0] = '11110000101010101111000010101010'
R[1] = L[0] + F(R[0], K[1]) = L[0] + H = subxor(L[0], H) = '11101111010010100110010101000100'
按照这样的规律, 我们就可以依序算出 L[16] 及 R[16], 下面是最终算出 L[16] 与 R[16] 途经递徊的过程:
L1 = 11110000101010101111000010101010
R1 = 11101111010010100110010101000100
L2 = 11101111010010100110010101000100
R2 = 11001100000000010111011100001001
L3 = 11001100000000010111011100001001
R3 = 10100010010111000000101111110100
L4 = 10100010010111000000101111110100
R4 = 01110111001000100000000001000101
L5 = 01110111001000100000000001000101
R5 = 10001010010011111010011000110111
L6 = 10001010010011111010011000110111
R6 = 11101001011001111100110101101001
L7 = 11101001011001111100110101101001
R7 = 00000110010010101011101000010000
L8 = 00000110010010101011101000010000
R8 = 11010101011010010100101110010000
L9 = 11010101011010010100101110010000
R9 = 00100100011111001100011001111010
L10 = 00100100011111001100011001111010
R10 = 10110111110101011101011110110010
L11 = 10110111110101011101011110110010
R11 = 11000101011110000011110001111000
L12 = 11000101011110000011110001111000
R12 = 01110101101111010001100001011000
L13 = 01110101101111010001100001011000
R13 = 00011000110000110001010101011010
L14 = 00011000110000110001010101011010
R14 = 11000010100011001001011000001101
L15 = 11000010100011001001011000001101
R15 = 01000011010000100011001000110100
L16 = 01000011010000100011001000110100
R16 = 00001010010011001101100110010101
将 L16, R16 组合成 V = R16L16 = '0000101001001100110110011001010101000011010000100011001000110100' 再经过最后一个置换, 既可得到加密后的资料, 置换阵列如下:
@fp = (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);
经此置换后既可得加密后的资料 FINAL = '85E813540F0AB405'
我们了解了 DES 的编码原理就可以实现出 DES 编码的程序: des.pl
而执行的结果如下:
到此, 我们已讲述了 DES 编码的原理及算法, 下一章将介绍 MD4 的使用以及在 NTLM 中 DES 与 MD4 的应用.