zoukankan      html  css  js  c++  java
  • AES涉及的有限域乘法及字节填充方法

       非常值得参考的是官方文档,它详细介绍了AES及其实验过程。博文AES加密算法的C++实现就是基于该文档的介绍及实现,是难得的一篇好文,故在本文最后会附上该文,以作备份。

      还有很值得推荐的就是AES的动画演示,做的很形象,非常有助于理解!

        对AES而言,它采用了“代换-置换网络”结构(Substitution-Permutation Network, SPN)。其最复杂的计算在于列混淆,而列混淆的复杂又来自有限域的乘法;另外,一方面,我们还要考虑加密过程中需要考虑的字节填充。下边将进行介绍。

    1. 有限域乘法

      这部分主要参考自《密码编码学与网络安全——原理与实践》(第五版)(P. 96-97)。

      在该书中,作者提到“本质上说,域就是一个集合,我们可以在其上进行加法、减法、乘法和除法而不脱离该集合”。有限域是域的一种,它指的是阶(元素个数,记为p)有限的域,记为GF(p),其中有限域的阶必须是一个素数的幂p'^n(p'为素数,n为正整数)。GF(2^n)是在密码学中用得很多的有限域,它表示该域总共只有2^n个元素。特别地,GF(2^8)被用于AES的加解密。GF(2^8)是一个包含256个元素的域,它的每一个元素被赋值为0~2^8-1中的唯一整数(注意这里提到的,它意味着GF(2^8)的每一个元素同样可以取其他范围的值(不在0~2^8-1),但就是要满足域的条件)。在AES中,用到的有限域是GF(2^8),它的每一个元素被赋值为0~2^8-1中的唯一整数。

      同时,在该书中,作者还提出GF(2^n)的乘法计算公式如下:

      

      详细推导请参考该书P. 96。

      下边参考博文有限域GF(2^8)内乘法代码实现以及原理(这篇博文开头对有限域的说明有点问题,作者貌似是把有限域理解成因为该域内的元素的值是有范围的,所以才叫有限域)的例子来说明如何进行GF(2^8)有限域乘法。

      在二进制中,所有的数都能用0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80异或得到,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80的二进制表示如下:

      

          后一个分别是前一个的2倍。假设任意一个数a,他的二进制表示为10101101,可以由以下组合组成:

      

           而任何一个数x和a相乘都可以表示为

      

      所以只要计算出

      

    最后再对这些结果进行异或就可以求出最终的乘法结果。那如何求0x3a*0x24?

          首先0x3a=00111010,分别求

      

      0x24=00100100,所以0x3a*0x24=0x3a*00100100=0x04*0x3a^0x20*0x3a=0xe8^0x01=0xe9.

      作者还附带了一个C/C++程序来计算GF(2^8)有限域乘法:

    unsigned char XTIME(unsigned char x) 
    {
        return ((x << 1) ^ ((x & 0x80) ? 0x1b : 0x00));
    }
    unsigned char multiply(unsigned char a, unsigned char b) 
    {
        unsigned char temp[8] = { a };
        unsigned char tempmultiply = 0x00;
        int i = 0;
        for (i = 1; i < 8; i++) 
        {
            temp[i] = XTIME(temp[i - 1]);
        }
        tempmultiply = (b & 0x01) * a;
        for (i = 1; i <= 7; i++) 
        {
            tempmultiply ^= (((b >> i) & 0x01) * temp[i]);
        }
        return tempmultiply;
    }

      关于程序的解释可以参考该博文。

    2. 字节填充

      AES是分块计算,当数据内容不足,16字节(128 bit AES),24字节(192 bit AES),32字节(256 bit AES),不足部分就需要填充。维基百科翻译)上面列举填充方式有如下几种:
      1)ANSI X.923
      不足部分填充0,最后一字节为填充字节数。如下面8字节的块,需要填充4字节时:
      … | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 04 |
      2)ISO 10126
      不足部分填充随机数字,最后一字节为填充字节数。如下面8字节的块,需要填充4字节时:
      … | DD DD DD DD DD DD DD DD | DD DD DD DD BC DA EF 04 |
      3)PKCS7与PKCS5
      不足部分填充为需要填充字节数。若数据大小是分块大小N的倍数时,则增加一个全为N的分块。如下面8字节的块,需要填充4字节时:
      … | DD DD DD DD DD DD DD DD | DD DD DD DD 04 04 04 04 |
      4)ISO/IEC 7816-4
      不足的部分,首先填充一个0×80,剩余部分全为0。如下面8字节的块,需要填充4字节时:
      … | DD DD DD DD DD DD DD DD | DD DD DD DD 80 00 00 00 |
      要求数据内容本身不包含0×80
      5)Zero padding
      不足部分全部填充0。如下面8字节的块,需要填充4字节时:
      … | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 00 |
      这种方法不能区分数据内容本身末尾包含0的情况,因而也不是标准的填充方式。

      本人在实现的时候采用的是ANSI X.923标准。

    3. 个人实现

       代码请见Github.

    4. 博文AES加密算法的C++实现摘录

      摘要:作为新一代的加密标准,AES 旨在取代 DES(请看《DES加密算法的C++实现》),以适应当今分布式开放网络对数据加密安全性的要求。本文在分析了 AES 加密原理的基础上着重说明了算法实现的具体步骤,并用 C++ 实现了对文件的加密和解密。

      一、AES 介绍

      AES(高级加密标准,Advanced Encryption Standard),在密码学中又称 Rijndael 加密法,是美国联邦政府采用的一种分组加密标准。这个标准用来替代原先的 DES,目前已经广为全世界所使用,成为对称密钥算法中最流行的算法之一。

      在 AES 出现之前,最常用的对称密钥算法是 DES 加密算法,它在 1977 年被公布成为美国政府的商用加密标准。DES 的主要问题是密钥长度较短,渐渐不适合于分布式开放网络对数据加密安全性的要求。因此,1998年美国政府决定不再继续延用 DES 作为联邦加密标准,并发起了征集 AES 候选算法的活动。征集活动对 AES 的基本要求是: 比三重DES快、至少与三重DES一样安全、数据分组长度为128比特、密钥长度为128/192/256比特。

      经过三年多的甄选,比利时的密码学家所设计的 Rijndael 算法最终脱颖而出,成为新一代的高级加密标准,并于 2001 年由美国国家标准与技术研究院(NIST)发布于 FIPS PUB 197

      二、AES 算法原理

      AES算法(即 Rijndael 算法)是一个对称分组密码算法。数据分组长度必须是 128 bits,使用的密钥长度为 128,192 或 256 bits。对于三种不同密钥长度的 AES 算法,分别称为“AES-128”、“AES-192”、“AES-256”。(Rijndael 的设计还可以处理其它的分组长度和密钥长度,但 AES 标准中没有采用)

      下图是 AES 加密解密的整体流程图:

      

      这里我们需要知道3个符号:Nb—— 状态 State 包含的列(32-bit 字)的个数,也就是说 Nb=4;Nk—— 密钥包含的 32-bit 字的个数,也就是说 Nk=4,6 或 8;Nr—— 加密的轮数,对于不同密钥长度,轮数不一样,具体如下图所示:

      

      下面分为密钥扩展、分组加密、分组解密三个部分来讲 AES 算法,我会尽可能地简明扼要,若还有不懂的,请自行 Google。

      1)密钥扩展

      AES 算法通过密钥扩展程序(Key Expansion)将用户输入的密钥 K 扩展生成 Nb(Nr+1)个字,存放在一个线性数组w[Nb*(Nr+1)]中。具体如下:

    1. 位置变换函数RotWord(),接受一个字 [a0, a1, a2, a3] 作为输入,循环左移一个字节后输出 [a1, a2, a3, a0]。

    2. S盒变换函数SubWord(),接受一个字 [a0, a1, a2, a3] 作为输入。S盒是一个16x16的表,其中每一个元素是一个字节。对于输入的每一个字节,前四位组成十六进制数 x 作为行号,后四位组成的十六进制数 y 作为列号,查找表中对应的值。最后函数输出 4 个新字节组成的 32-bit 字。

    3. 轮常数Rcon[],如何计算的就不说了,直接把它当做常量数组。

    4. 扩展密钥数组w[]的前 Nk 个元素就是外部密钥 K,以后的元素w[i]等于它前一个元素w[i-1]与前第 Nk 个元素w[i-Nk]的异或,即w[i] = w[i-1] XOR w[i-Nk];但若 i 为 Nk 的倍数,则w[i] = w[i-Nk] XOR SubWord(RotWord(w[i-1])) XOR Rcon[i/Nk-1]

      注意,上面的第四步说明适合于 AES-128 和 AES-192,详细的伪代码如下:

      

      密钥扩展程序的 C++ 代码(AES-128):

      1 #include <iostream>
      2 #include <bitset>
      3 using namespace std;
      4 typedef bitset<8> byte;
      5 typedef bitset<32> word;
      6 
      7 const int Nr = 10;  // AES-128需要 10 轮加密
      8 const int Nk = 4;   // Nk 表示输入密钥的 word 个数
      9 
     10 byte S_Box[16][16] = {
     11     { 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76 },
     12     { 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0 },
     13     { 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15 },
     14     { 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75 },
     15     { 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84 },
     16     { 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF },
     17     { 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8 },
     18     { 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2 },
     19     { 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73 },
     20     { 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB },
     21     { 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79 },
     22     { 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08 },
     23     { 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A },
     24     { 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E },
     25     { 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF },
     26     { 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 }
     27 };
     28 
     29 // 轮常数,密钥扩展中用到。(AES-128只需要10轮)
     30 word Rcon[10] = { 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
     31 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000 };
     32 
     33 /**
     34 * 将4个 byte 转换为一个 word.
     35 */
     36 word Word(byte& k1, byte& k2, byte& k3, byte& k4)
     37 {
     38     word result(0x00000000);
     39     word temp;
     40     temp = k1.to_ulong();  // K1
     41     temp <<= 24;
     42     result |= temp;
     43     temp = k2.to_ulong();  // K2
     44     temp <<= 16;
     45     result |= temp;
     46     temp = k3.to_ulong();  // K3
     47     temp <<= 8;
     48     result |= temp;
     49     temp = k4.to_ulong();  // K4
     50     result |= temp;
     51     return result;
     52 }
     53 
     54 /**
     55 *  按字节 循环左移一位
     56 *  即把[a0, a1, a2, a3]变成[a1, a2, a3, a0]
     57 */
     58 word RotWord(word& rw)
     59 {
     60     word high = rw << 8;
     61     word low = rw >> 24;
     62     return high | low;
     63 }
     64 
     65 /**
     66 *  对输入word中的每一个字节进行S-盒变换
     67 */
     68 word SubWord(word& sw)
     69 {
     70     word temp;
     71     for (int i = 0; i<32; i += 8)
     72     {
     73         int row = sw[i + 7] * 8 + sw[i + 6] * 4 + sw[i + 5] * 2 + sw[i + 4];
     74         int col = sw[i + 3] * 8 + sw[i + 2] * 4 + sw[i + 1] * 2 + sw[i];
     75         byte val = S_Box[row][col];
     76         for (int j = 0; j<8; ++j)
     77             temp[i + j] = val[j];
     78     }
     79     return temp;
     80 }
     81 
     82 /**
     83 *  密钥扩展函数 - 对128位密钥进行扩展得到 w[4*(Nr+1)]
     84 */
     85 void KeyExpansion(byte key[4 * Nk], word w[4 * (Nr + 1)])
     86 {
     87     word temp;
     88     int i = 0;
     89     // w[]的前4个就是输入的key
     90     while (i < Nk)
     91     {
     92         w[i] = Word(key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]);
     93         ++i;
     94     }
     95 
     96     i = Nk;
     97 
     98     while (i < 4 * (Nr + 1))
     99     {
    100         temp = w[i - 1]; // 记录前一个word
    101         if (i % Nk == 0)
    102             w[i] = w[i - Nk] ^ SubWord(RotWord(temp)) ^ Rcon[i / Nk - 1];
    103         else
    104             w[i] = w[i - Nk] ^ temp;
    105         ++i;
    106     }
    107 }
    108 
    109 int main()
    110 {
    111     byte key[16] = { 0x2b, 0x7e, 0x15, 0x16,
    112         0x28, 0xae, 0xd2, 0xa6,
    113         0xab, 0xf7, 0x15, 0x88,
    114         0x09, 0xcf, 0x4f, 0x3c };
    115 
    116     word w[4 * (Nr + 1)];
    117 
    118     cout << "KEY IS: ";
    119     for (int i = 0; i<16; ++i)
    120         cout << hex << key[i].to_ulong() << " ";
    121     cout << endl;
    122 
    123     KeyExpansion(key, w);
    124     // 测试
    125     for (int i = 0; i<4 * (Nr + 1); ++i)
    126         cout << "w[" << dec << i << "] = " << hex << w[i].to_ulong() << endl;
    127 
    128     return 0;
    129 }
    KeyExpansion

      测试输出结果:

      

      2)加密

      根据 AES 加密的整体流程图(本文开头),伪代码如下:

      

      从伪代码描述中可以看出,AES 加密时涉及到的子程序有SubBytes()ShiftRows()MixColumns()AddRoundKey()。下面我们一个一个进行介绍:

      ① S盒变换-SubBytes()

      在密钥扩展部分已经讲过了,S盒是一个 16 行 16 列的表,表中每个元素都是一个字节。S盒变换很简单:函数SubBytes()接受一个 4x4 的字节矩阵作为输入,对其中的每个字节,前四位组成十六进制数 x 作为行号,后四位组成的十六进制数 y 作为列号,查找表中对应的值替换原来位置上的字节。

      ② 行变换-ShiftRows()

    行变换也很简单,它仅仅是将矩阵的每一行以字节为单位循环移位:第一行不变,第二行左移一位,第三行左移两位,第四行左移三位。如下图所示:

      

      ③ 列变换-MixColumns()

      函数MixColumns()同样接受一个 4x4 的字节矩阵作为输入,并对矩阵进行逐列变换,变换方式如下:

      

      注意公式中用到的乘法是伽罗华域(GF,有限域)上的乘法,高级加密标准文档 fips-197 上有讲,如果还是不懂,请自行Google。

      

      ④ 与扩展密钥的异或-AddRoundKey()

      扩展密钥只参与了这一步。根据当前加密的轮数,用w[]中的 4 个扩展密钥与矩阵的 4 个列进行按位异或。如下图:

      

      好了,到这里 AES 加密的各个部分就讲完了。算法实现的 C++ 源码在文章后面第三部分。

       3)解密

      根据 AES 解密的整体流程图(本文开头),伪代码如下:

      

      从伪代码可以看出,我们需要分别实现 S 盒变换、行变换和列变换的逆变换InvShiftRows()InvSubBytes()InvMixColumns()。下面就简单的讲一下这三个逆变换:

      ① 逆行变换-InvShiftRows()

      上面讲到ShiftRows()是对矩阵的每一行进行循环左移,所以InvShiftRows()是对矩阵每一行进行循环右移。

      

      ② 逆 S 盒变换-InvSubBytes()

      与 S 盒变换一样,也是查表,查表的方式也一样,只不过查的是另外一个置换表(S-Box的逆表)。

      ③ 逆列变换-InvMixColumns()

      与列变换的方式一样,只不过计算公式的系数矩阵发生了变化。如下图:

      

      好了,AES 解密到这里也讲完了。只要写出三个逆变换的函数,然后根据伪代码就很容易实现 AES 解密算法了。

      三、C++实现

      下面我用 C++ 实现 AES 的加密和解密算法,并实现了对文件的加密和解密。这里我使用 C++ STL 的bitset定义了两个类型:byteword。需要提到的是,对于有限域上的乘法,我们既可以通过查表(6个结果表),也可以写一个函数来实现。当然,查表的效率会更高,但考虑到贴代码,这里我就用一个函数来实现的。

      下面是 AES-128 对一个 128 位数据加密和解密的源代码:

      1 /*************************************************************************
      2 > File Name: AES.cpp
      3 > Author: SongLee
      4 > E-mail: lisong.shine@qq.com
      5 > Created Time: 2014年12月12日 星期五 20时15分50秒
      6 > Personal Blog: http://songlee24.github.com
      7 ************************************************************************/
      8 #include <iostream>
      9 #include <bitset>
     10 #include <string>
     11 using namespace std;
     12 typedef bitset<8> byte;
     13 typedef bitset<32> word;
     14 
     15 const int Nr = 10;  // AES-128需要 10 轮加密
     16 const int Nk = 4;   // Nk 表示输入密钥的 word 个数
     17 
     18 byte S_Box[16][16] = {
     19     { 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76 },
     20     { 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0 },
     21     { 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15 },
     22     { 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75 },
     23     { 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84 },
     24     { 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF },
     25     { 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8 },
     26     { 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2 },
     27     { 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73 },
     28     { 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB },
     29     { 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79 },
     30     { 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08 },
     31     { 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A },
     32     { 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E },
     33     { 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF },
     34     { 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 }
     35 };
     36 
     37 byte Inv_S_Box[16][16] = {
     38     { 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB },
     39     { 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB },
     40     { 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E },
     41     { 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25 },
     42     { 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92 },
     43     { 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84 },
     44     { 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06 },
     45     { 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B },
     46     { 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73 },
     47     { 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E },
     48     { 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B },
     49     { 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4 },
     50     { 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F },
     51     { 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF },
     52     { 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61 },
     53     { 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D }
     54 };
     55 
     56 // 轮常数,密钥扩展中用到。(AES-128只需要10轮)
     57 word Rcon[10] = { 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
     58 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000 };
     59 
     60 /**********************************************************************/
     61 /*                                                                    */
     62 /*                              AES算法实现                           */
     63 /*                                                                    */
     64 /**********************************************************************/
     65 
     66 /******************************下面是加密的变换函数**********************/
     67 /**
     68 *  S盒变换 - 前4位为行号,后4位为列号
     69 */
     70 void SubBytes(byte mtx[4 * 4])
     71 {
     72     for (int i = 0; i<16; ++i)
     73     {
     74         int row = mtx[i][7] * 8 + mtx[i][6] * 4 + mtx[i][5] * 2 + mtx[i][4];
     75         int col = mtx[i][3] * 8 + mtx[i][2] * 4 + mtx[i][1] * 2 + mtx[i][0];
     76         mtx[i] = S_Box[row][col];
     77     }
     78 }
     79 
     80 /**
     81 *  行变换 - 按字节循环移位
     82 */
     83 void ShiftRows(byte mtx[4 * 4])
     84 {
     85     // 第二行循环左移一位
     86     byte temp = mtx[4];
     87     for (int i = 0; i<3; ++i)
     88         mtx[i + 4] = mtx[i + 5];
     89     mtx[7] = temp;
     90     // 第三行循环左移两位
     91     for (int i = 0; i<2; ++i)
     92     {
     93         temp = mtx[i + 8];
     94         mtx[i + 8] = mtx[i + 10];
     95         mtx[i + 10] = temp;
     96     }
     97     // 第四行循环左移三位
     98     temp = mtx[15];
     99     for (int i = 3; i>0; --i)
    100         mtx[i + 12] = mtx[i + 11];
    101     mtx[12] = temp;
    102 }
    103 
    104 /**
    105 *  有限域上的乘法 GF(2^8)
    106 */
    107 byte GFMul(byte a, byte b) {
    108     byte p = 0;
    109     byte hi_bit_set;
    110     for (int counter = 0; counter < 8; counter++) {
    111         if ((b & byte(1)) != 0) {
    112             p ^= a;
    113         }
    114         hi_bit_set = (byte)(a & byte(0x80));
    115         a <<= 1;
    116         if (hi_bit_set != 0) {
    117             a ^= 0x1b; /* x^8 + x^4 + x^3 + x + 1 */
    118         }
    119         b >>= 1;
    120     }
    121     return p;
    122 }
    123 
    124 /**
    125 *  列变换
    126 */
    127 void MixColumns(byte mtx[4 * 4])
    128 {
    129     byte arr[4];
    130     for (int i = 0; i<4; ++i)
    131     {
    132         for (int j = 0; j<4; ++j)
    133             arr[j] = mtx[i + j * 4];
    134 
    135         mtx[i] = GFMul(0x02, arr[0]) ^ GFMul(0x03, arr[1]) ^ arr[2] ^ arr[3];
    136         mtx[i + 4] = arr[0] ^ GFMul(0x02, arr[1]) ^ GFMul(0x03, arr[2]) ^ arr[3];
    137         mtx[i + 8] = arr[0] ^ arr[1] ^ GFMul(0x02, arr[2]) ^ GFMul(0x03, arr[3]);
    138         mtx[i + 12] = GFMul(0x03, arr[0]) ^ arr[1] ^ arr[2] ^ GFMul(0x02, arr[3]);
    139     }
    140 }
    141 
    142 /**
    143 *  轮密钥加变换 - 将每一列与扩展密钥进行异或
    144 */
    145 void AddRoundKey(byte mtx[4 * 4], word k[4])
    146 {
    147     for (int i = 0; i<4; ++i)
    148     {
    149         word k1 = k[i] >> 24;
    150         word k2 = (k[i] << 8) >> 24;
    151         word k3 = (k[i] << 16) >> 24;
    152         word k4 = (k[i] << 24) >> 24;
    153 
    154         mtx[i] = mtx[i] ^ byte(k1.to_ulong());
    155         mtx[i + 4] = mtx[i + 4] ^ byte(k2.to_ulong());
    156         mtx[i + 8] = mtx[i + 8] ^ byte(k3.to_ulong());
    157         mtx[i + 12] = mtx[i + 12] ^ byte(k4.to_ulong());
    158     }
    159 }
    160 
    161 /**************************下面是解密的逆变换函数***********************/
    162 /**
    163 *  逆S盒变换
    164 */
    165 void InvSubBytes(byte mtx[4 * 4])
    166 {
    167     for (int i = 0; i<16; ++i)
    168     {
    169         int row = mtx[i][7] * 8 + mtx[i][6] * 4 + mtx[i][5] * 2 + mtx[i][4];
    170         int col = mtx[i][3] * 8 + mtx[i][2] * 4 + mtx[i][1] * 2 + mtx[i][0];
    171         mtx[i] = Inv_S_Box[row][col];
    172     }
    173 }
    174 
    175 /**
    176 *  逆行变换 - 以字节为单位循环右移
    177 */
    178 void InvShiftRows(byte mtx[4 * 4])
    179 {
    180     // 第二行循环右移一位
    181     byte temp = mtx[7];
    182     for (int i = 3; i>0; --i)
    183         mtx[i + 4] = mtx[i + 3];
    184     mtx[4] = temp;
    185     // 第三行循环右移两位
    186     for (int i = 0; i<2; ++i)
    187     {
    188         temp = mtx[i + 8];
    189         mtx[i + 8] = mtx[i + 10];
    190         mtx[i + 10] = temp;
    191     }
    192     // 第四行循环右移三位
    193     temp = mtx[12];
    194     for (int i = 0; i<3; ++i)
    195         mtx[i + 12] = mtx[i + 13];
    196     mtx[15] = temp;
    197 }
    198 
    199 void InvMixColumns(byte mtx[4 * 4])
    200 {
    201     byte arr[4];
    202     for (int i = 0; i<4; ++i)
    203     {
    204         for (int j = 0; j<4; ++j)
    205             arr[j] = mtx[i + j * 4];
    206 
    207         mtx[i] = GFMul(0x0e, arr[0]) ^ GFMul(0x0b, arr[1]) ^ GFMul(0x0d, arr[2]) ^ GFMul(0x09, arr[3]);
    208         mtx[i + 4] = GFMul(0x09, arr[0]) ^ GFMul(0x0e, arr[1]) ^ GFMul(0x0b, arr[2]) ^ GFMul(0x0d, arr[3]);
    209         mtx[i + 8] = GFMul(0x0d, arr[0]) ^ GFMul(0x09, arr[1]) ^ GFMul(0x0e, arr[2]) ^ GFMul(0x0b, arr[3]);
    210         mtx[i + 12] = GFMul(0x0b, arr[0]) ^ GFMul(0x0d, arr[1]) ^ GFMul(0x09, arr[2]) ^ GFMul(0x0e, arr[3]);
    211     }
    212 }
    213 
    214 /******************************下面是密钥扩展部分***********************/
    215 /**
    216 * 将4个 byte 转换为一个 word.
    217 */
    218 word Word(byte& k1, byte& k2, byte& k3, byte& k4)
    219 {
    220     word result(0x00000000);
    221     word temp;
    222     temp = k1.to_ulong();  // K1
    223     temp <<= 24;
    224     result |= temp;
    225     temp = k2.to_ulong();  // K2
    226     temp <<= 16;
    227     result |= temp;
    228     temp = k3.to_ulong();  // K3
    229     temp <<= 8;
    230     result |= temp;
    231     temp = k4.to_ulong();  // K4
    232     result |= temp;
    233     return result;
    234 }
    235 
    236 /**
    237 *  按字节 循环左移一位
    238 *  即把[a0, a1, a2, a3]变成[a1, a2, a3, a0]
    239 */
    240 word RotWord(word& rw)
    241 {
    242     word high = rw << 8;
    243     word low = rw >> 24;
    244     return high | low;
    245 }
    246 
    247 /**
    248 *  对输入word中的每一个字节进行S-盒变换
    249 */
    250 word SubWord(word& sw)
    251 {
    252     word temp;
    253     for (int i = 0; i<32; i += 8)
    254     {
    255         int row = sw[i + 7] * 8 + sw[i + 6] * 4 + sw[i + 5] * 2 + sw[i + 4];
    256         int col = sw[i + 3] * 8 + sw[i + 2] * 4 + sw[i + 1] * 2 + sw[i];
    257         byte val = S_Box[row][col];
    258         for (int j = 0; j<8; ++j)
    259             temp[i + j] = val[j];
    260     }
    261     return temp;
    262 }
    263 
    264 /**
    265 *  密钥扩展函数 - 对128位密钥进行扩展得到 w[4*(Nr+1)]
    266 */
    267 void KeyExpansion(byte key[4 * Nk], word w[4 * (Nr + 1)])
    268 {
    269     word temp;
    270     int i = 0;
    271     // w[]的前4个就是输入的key
    272     while (i < Nk)
    273     {
    274         w[i] = Word(key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]);
    275         ++i;
    276     }
    277 
    278     i = Nk;
    279 
    280     while (i < 4 * (Nr + 1))
    281     {
    282         temp = w[i - 1]; // 记录前一个word
    283         if (i % Nk == 0)
    284             w[i] = w[i - Nk] ^ SubWord(RotWord(temp)) ^ Rcon[i / Nk - 1];
    285         else
    286             w[i] = w[i - Nk] ^ temp;
    287         ++i;
    288     }
    289 }
    290 
    291 /******************************下面是加密和解密函数**************************/
    292 /**
    293 *  加密
    294 */
    295 void encrypt(byte in[4 * 4], word w[4 * (Nr + 1)])
    296 {
    297     word key[4];
    298     for (int i = 0; i<4; ++i)
    299         key[i] = w[i];
    300     AddRoundKey(in, key);
    301 
    302     for (int round = 1; round<Nr; ++round)
    303     {
    304         SubBytes(in);
    305         ShiftRows(in);
    306         MixColumns(in);
    307         for (int i = 0; i<4; ++i)
    308             key[i] = w[4 * round + i];
    309         AddRoundKey(in, key);
    310     }
    311 
    312     SubBytes(in);
    313     ShiftRows(in);
    314     for (int i = 0; i<4; ++i)
    315         key[i] = w[4 * Nr + i];
    316     AddRoundKey(in, key);
    317 }
    318 
    319 /**
    320 *  解密
    321 */
    322 void decrypt(byte in[4 * 4], word w[4 * (Nr + 1)])
    323 {
    324     word key[4];
    325     for (int i = 0; i<4; ++i)
    326         key[i] = w[4 * Nr + i];
    327     AddRoundKey(in, key);
    328 
    329     for (int round = Nr - 1; round>0; --round)
    330     {
    331         InvShiftRows(in);
    332         InvSubBytes(in);
    333         for (int i = 0; i<4; ++i)
    334             key[i] = w[4 * round + i];
    335         AddRoundKey(in, key);
    336         InvMixColumns(in);
    337     }
    338 
    339     InvShiftRows(in);
    340     InvSubBytes(in);
    341     for (int i = 0; i<4; ++i)
    342         key[i] = w[i];
    343     AddRoundKey(in, key);
    344 }
    345 
    346 /**********************************************************************/
    347 /*                                                                    */
    348 /*                              测试                                  */
    349 /*                                                                    */
    350 /**********************************************************************/
    351 int main()
    352 {
    353     byte key[16] = { 0x2b, 0x7e, 0x15, 0x16,
    354         0x28, 0xae, 0xd2, 0xa6,
    355         0xab, 0xf7, 0x15, 0x88,
    356         0x09, 0xcf, 0x4f, 0x3c };
    357 
    358     byte plain[16] = { 0x32, 0x88, 0x31, 0xe0,
    359         0x43, 0x5a, 0x31, 0x37,
    360         0xf6, 0x30, 0x98, 0x07,
    361         0xa8, 0x8d, 0xa2, 0x34 };
    362     // 输出密钥
    363     cout << "密钥是:";
    364     for (int i = 0; i<16; ++i)
    365         cout << hex << key[i].to_ulong() << " ";
    366     cout << endl;
    367 
    368     word w[4 * (Nr + 1)];
    369     KeyExpansion(key, w);
    370 
    371     // 输出待加密的明文
    372     cout << endl << "待加密的明文:" << endl;
    373     for (int i = 0; i<16; ++i)
    374     {
    375         cout << hex << plain[i].to_ulong() << " ";
    376         if ((i + 1) % 4 == 0)
    377             cout << endl;
    378     }
    379     cout << endl;
    380 
    381     // 加密,输出密文
    382     encrypt(plain, w);
    383     cout << "加密后的密文:" << endl;
    384     for (int i = 0; i<16; ++i)
    385     {
    386         cout << hex << plain[i].to_ulong() << " ";
    387         if ((i + 1) % 4 == 0)
    388             cout << endl;
    389     }
    390     cout << endl;
    391 
    392     // 解密,输出明文
    393     decrypt(plain, w);
    394     cout << "解密后的明文:" << endl;
    395     for (int i = 0; i<16; ++i)
    396     {
    397         cout << hex << plain[i].to_ulong() << " ";
    398         if ((i + 1) % 4 == 0)
    399             cout << endl;
    400     }
    401     cout << endl;
    402     return 0;
    403 }
    View Code

      测试用例如下图:

      

      测试结果截图:

      

      可见,测试结果和预期输出相同,表明对数据的加密和解密成功!!!

      下面我们来写 AES 对文件的加密和解密,在对 128 位的数据加解密成功以后,对文件的加解密就很简单了!只需要每次读 128 位,加密以后,将 128 位的密文写入另外一个文件…..如此循环,直到文件尾。下面是对一张图片进行 AES 加密和解密的测试代码(效率先不管了,有时间我再优化):

     1 //#include <fstream>
     2 typedef bitset<8> byte;
     3 typedef bitset<32> word;
     4 /**
     5 *  将一个char字符数组转化为二进制
     6 *  存到一个 byte 数组中
     7 */
     8 void charToByte(byte out[16], const char s[16])
     9 {
    10     for (int i = 0; i<16; ++i)
    11         for (int j = 0; j<8; ++j)
    12             out[i][j] = ((s[i] >> j) & 1);
    13 }
    14 
    15 /**
    16 *  将连续的128位分成16组,存到一个 byte 数组中
    17 */
    18 void divideToByte(byte out[16], bitset<128>& data)
    19 {
    20     bitset<128> temp;
    21     for (int i = 0; i<16; ++i)
    22     {
    23         temp = (data << 8 * i) >> 120;
    24         out[i] = temp.to_ulong();
    25     }
    26 }
    27 
    28 /**
    29 *  将16个 byte 合并成连续的128位
    30 */
    31 bitset<128> mergeByte(byte in[16])
    32 {
    33     bitset<128> res;
    34     res.reset();  // 置0
    35     bitset<128> temp;
    36     for (int i = 0; i<16; ++i)
    37     {
    38         temp = in[i].to_ulong();
    39         temp <<= 8 * (15 - i);
    40         res |= temp;
    41     }
    42     return res;
    43 }
    44 
    45 int main()
    46 {
    47     string keyStr = "abcdefghijklmnop";
    48     byte key[16];
    49     charToByte(key, keyStr.c_str());
    50     // 密钥扩展
    51     word w[4 * (Nr + 1)];
    52     KeyExpansion(key, w);
    53 
    54     bitset<128> data;
    55     byte plain[16];
    56     // 将文件 flower.jpg 加密到 cipher.txt 中
    57     ifstream in;
    58     ofstream out;
    59     in.open("D://flower.jpg", ios::binary);
    60     out.open("D://cipher.txt", ios::binary);
    61     while (in.read((char*)&data, sizeof(data)))
    62     {
    63         divideToByte(plain, data);
    64         encrypt(plain, w);
    65         data = mergeByte(plain);
    66         out.write((char*)&data, sizeof(data));
    67         data.reset();  // 置0
    68     }
    69     in.close();
    70     out.close();
    71 
    72     // 解密 cipher.txt,并写入图片 flower1.jpg
    73     in.open("D://cipher.txt", ios::binary);
    74     out.open("D://flower1.jpg", ios::binary);
    75     while (in.read((char*)&data, sizeof(data)))
    76     {
    77         divideToByte(plain, data);
    78         decrypt(plain, w);
    79         data = mergeByte(plain);
    80         out.write((char*)&data, sizeof(data));
    81         data.reset();  // 置0
    82     }
    83     in.close();
    84     out.close();
    85 
    86     return 0;
    87 }
    View Code

      (全文完)

      更新 —— 2014.12.21

      有限域 GF(28) 上的乘法改用查表的方式实现,AES的加密速度马上提升 80% 以上,所以建议最好使用查表的方式。下面是 AES 算法中用到的 6 个乘法结果表:

      1 byte Mul_02[256] = {
      2     0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
      3     0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
      4     0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
      5     0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
      6     0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
      7     0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
      8     0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
      9     0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
     10     0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05,
     11     0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25,
     12     0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45,
     13     0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65,
     14     0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85,
     15     0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5,
     16     0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5,
     17     0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5
     18 };
     19 
     20 byte Mul_03[256] = {
     21     0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11,
     22     0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21,
     23     0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71,
     24     0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41,
     25     0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1,
     26     0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1,
     27     0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1,
     28     0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81,
     29     0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a,
     30     0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba,
     31     0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea,
     32     0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda,
     33     0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a,
     34     0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a,
     35     0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a,
     36     0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a
     37 };
     38 
     39 byte Mul_09[256] = {
     40     0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
     41     0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
     42     0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
     43     0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc,
     44     0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01,
     45     0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91,
     46     0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a,
     47     0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa,
     48     0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b,
     49     0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b,
     50     0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0,
     51     0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30,
     52     0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed,
     53     0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d,
     54     0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6,
     55     0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46
     56 };
     57 
     58 byte Mul_0b[256] = {
     59     0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69,
     60     0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9,
     61     0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12,
     62     0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2,
     63     0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f,
     64     0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f,
     65     0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4,
     66     0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54,
     67     0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e,
     68     0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e,
     69     0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5,
     70     0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55,
     71     0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68,
     72     0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8,
     73     0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13,
     74     0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3
     75 };
     76 
     77 byte Mul_0d[256] = {
     78     0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b,
     79     0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b,
     80     0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0,
     81     0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20,
     82     0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26,
     83     0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6,
     84     0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d,
     85     0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d,
     86     0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91,
     87     0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41,
     88     0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a,
     89     0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa,
     90     0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc,
     91     0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c,
     92     0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47,
     93     0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97
     94 };
     95 
     96 byte Mul_0e[256] = {
     97     0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a,
     98     0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba,
     99     0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81,
    100     0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61,
    101     0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7,
    102     0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17,
    103     0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c,
    104     0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc,
    105     0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b,
    106     0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb,
    107     0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0,
    108     0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20,
    109     0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6,
    110     0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56,
    111     0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d,
    112     0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d
    113 };
    View Code
  • 相关阅读:
    理解AJAX工作原理 构建网站的有效方法()
    吃大亏的SQL
    Global
    jQuery插件jTemplates(模板
    distinct吃亏记
    事件和委托的编译代码
    星期划分
    理解AJAX工作原理 构建网站的有效方法(5)
    理解AJAX工作原理 构建网站的有效方法(2)
    我比较笨系列2.《委托》将方法绑定到委托
  • 原文地址:https://www.cnblogs.com/xiehongfeng100/p/4315395.html
Copyright © 2011-2022 走看看