DES加密实现(翻译自书籍《Wiley.Implementing.SSL.TLS.Using.Cryptography.and.PKI》)

Posted qq_28674045

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DES加密实现(翻译自书籍《Wiley.Implementing.SSL.TLS.Using.Cryptography.and.PKI》)相关的知识,希望对你有一定的参考价值。

理解BlockCipher加密算法

  • 凯撒大帝被认为是最古老的对称加密算法。所谓的凯撒加密法(你也许可以从报纸上找到一个作为消遣来玩),它随机的给每一个字母分配一个数字。在这个简单的算法当中,字母到数字的映射就是key。现代加密算法比凯撒算法肯定复杂的多,以便抵御来自计算机的攻击。尽管基本原理是一样,替换一个字母或其它什么东西为另外一个字母或其它什么东西,后续都对替换后的东西进行处理,在几个世纪以来,更多的混淆和扩散(confusion and diffusion)要素被加入,从而创建了现代加密算法。一个很重要的技术就是同时操作一组字符,而不是一个。现在用的最多的对称加密算法类别就是块加密算法,它对固定数目的字节进行操作,而不是一个字符。
  • 在这一部分我们来考察3种最流行的块加密算法,也是我们在工作中最可能碰到的现代加密算法。这些算法也许会在未来几十年还有意义,要修改加密标准,那个过程是非常非常慢的;需要密码专家进行大量的研究和分析。

实现DES加密算法

  • DESData Encryption Standard(数据加密标准)的简称,它是1974年,在NSA的要求下,由IBM实现和提出的;它也是第一个可以公开获取的针对计算机的加密算法。尽管在后文你看可以看到,DES不再认为是足够安全的,但是它还在被广泛地使用着。并且它也是学习对称加密算法的好入口。DES中的绝大多数概念都会在其它加密算法中出现。
  • DES将输入分成8字节的块,然后使用一个8字节的key来混淆它们。这个混淆的过程有一系列的固定置换(将第34位和28位互换,28位和17位互换等等)、轮换以及XORs。尽管DES的核心(也就是DES为什么是安全的)是因为S boxes的操作,经过它输入的每6bit,都会固定地4bit的输出;但是这个过程是不可逆的(除非有key)。
  • 和任何现代对称加密算法一样,DES也非常依赖于XOR操作。【异或操作介绍略】

  • 异或一个非常有趣的属性就是它是可逆的,比如:
                    
  • 在实现DES的时候,我们一般操作的是字节数组,以便充分利用硬件对整数操作的优势。传统上,DES是使用大端来描述的,但是x86等架构都是小端的;为了充分利用硬件性能,我们需要对规范中的一些部分进行反转,不过在这里,我们不需要这么做 
  • 作为一个替代方案,我们操作byte数组。因为我们需要操作位,比如:得到64bit中的第39bit的值;因此我们需要一些宏的支持,从而可以方便的对字节数组进行bit查找和操作。bit操作的宏如下所示:
  // This does not returna 1 for a 1 bit; it just returns non-zero

     #define GET_BIT( array, bit ) \\

                ( array[( int ) ( bit /8 ) ] & ( 0x80 >> (bit % 8 ) ) )

     #define SET_BIT( array, bit ) \\

                ( array[( int ) ( bit /8 ) ] |= ( 0x80 >> (bit % 8 ) ) )

     #define CLEAR_BIT( array, bit ) \\

                ( array[( int ) ( bit /8 ) ] &= ~( 0x80>> ( bit % 8 ) ) )

  • 因为这个例子是对字节数组求异或的,所以需要一个方法来支持异或操作。

       static void xor( unsignedchar *target, constunsigned char *src, int len )

       {

             while ( len-- )

            {

                   *target++^= *src++;

            }

      } 

  • 最后我们还需要一个置换(permute)函数,这个函数根据 permute_table数组对输入进行bit置换,比如:输入的第57bit放到输出的第14bit。正如你下面会看到的,这个函数是DES算法调用最频繁的地方;它被调用了10多次(使用不同的置换表permute_table)

/**

* Implement the initialand final permutation functions. permute_table

* and target must haveexactly len and len * 8 number of entries,

* respectively, but srccan be shorter (expansion function depends on this).

* NOTE: this assumesthat the permutation tables are defined as one-based

* rather than 0-basedarrays, since theyre given that way in the

* specification.

*/

static void permute( unsignedchar target[],

        const unsigned char src[],

        const int permute_table[],

        int len )

{

        int i;

        for ( i = 0; i <len * 8; i++ )

        {

                if ( GET_BIT( src,( permute_table[ i ]- 1 ) ) )

                {

                        SET_BIT(target, i );

                }

                else

                {

                        CLEAR_BIT( target, i );

                }

        }

}

DES初始置换

  • DES规范中要求对输入进行一个初始置换。这个置换的目的是什么还不是很清楚,因为它没有任何加密的效果(置换后的安全效果和置换前是一样的)。加入这个置换的目的,也许是为了对某些硬件类型进行优化。尽管如此,如果你不进行这个操作,你的DES加密结果将会是错误的,也不能够和其它实现进行交互。
  • 在规范中,对这个置换的描述使用术语:input bitsoutput bits。它的操作如下:将input最后一个字节的第2bit拷贝到output第一个字节的第一个bit;然后将input倒数第二个字节的第2bit拷贝到output第一个字节的第2bit等等。因此,output的第一个字节是由input左右字节中的第2bit组成的(反向的)。output的第2个字节是input每个字节的第4bit组成的(也是反向的)。output的第3个字节是input每个字节的第6bit组成的,output的第4个字节由input每个字节的第8bit组成,output的第5个字节由input每个字节的第1个字节组成,等等。
  • 故给定8个字节作为输入,操作的结果如下:



  • 实现这个置换可以使用前面的置换(permute)函数,其中permute_table如下:(注意:DES认为字节序是大端的)

比如:第一个字节是由原字节数组中,每个字节的第2bit组成。

      

原字节数组位数的大小,从左到有为:164,上图字节数组中标识为1的为58250等等

static const int ip_table[] = {

        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 };

  • 在输入被置换之后, 它又被用key进行了16轮的操作组合,每一轮做的操作如下:
    • 扩展输入的第32到第64位到48bits(下面会进行描述)
    • 将扩展后的右边部分和keyXOR
    • 使用上面的输出,查询8s-box表,并使用这些内容来覆盖输入
    • 根据特定的p表来对输出进行置换
    • 将输出和输入的左边部分(1~32)做XOR,然后对左右部分交换;在下一轮,相同的操作被执行,但是左右部分被调换了
        

  • 最终,前后部分做最后一次交换,然后对输出做初始置换的反操作,也就抵消了初始置换。

/**

* This just invertsip_table.

*/

static const int fp_table[] = { 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 }

上面是最后使用的置换表。

DES Key Schedule