NTLM Protocol - 2. DES 编码


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 的操作亦同.

所以可以得到:

NTLM Protocol - 2. DES 编码_第1张图片

将 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 个阵列如下表述:

NTLM Protocol - 2. DES 编码_第2张图片

为了方便说明起见, 将这 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 的应用.

你可能感兴趣的:(NTLM Protocol - 2. DES 编码)