AES涉及的有限域乘法及字节填充方法

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

    对AES而言,最复杂的计算在于列混淆,而列混淆的复杂又来自有限域的乘法;另外,一方面,我们还要考虑加密过程中需要考虑的字节填充。下边将进行介绍。

  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: [email protected]
  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 }

  测试用例如下图:

  

  测试结果截图:

  

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

  下面我们来写 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 }

  (全文完)

  更新 —— 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 };

时间: 2024-10-28 14:25:25

AES涉及的有限域乘法及字节填充方法的相关文章

RandomAccessFileDemo3~4 向文件中读写出一组字节的方法

RandomAccessFileDemo3  向文件中写出一组字节的方法 /** *    文件操作 --- RandomAccessFile * *    RandomAccessFile提供了一个可以向文件中写出一组字节的方法: *    void write(byte[] bytesArr) 将给定的字节数组中所有字节一次性写出 与该方法相似的还有一个常用方法: void write(byte[] bytesArr,int offset,int len) 该方法会根据当前指针所在位置处连续

ProgressBar样式总结与自动填充方法(代码)

有时候开发的时候需要用一个进度条告知用户目前正在运行一个耗时操作,但是并不需要明确知道某个value来setProgress,所以就可以自定义一个时间和进度让进度条自动运行了. 下面是代码: Handler mHandler=new Handler(){ @Override public void handleMessage(Message msg) { p.setProgress(msg.what); mHandler.post(updateThread);//实现连续过程 } }; Runn

EXCEL快速自动填充方法集锦

EXCEL快速自动填充方法集锦 原文地址,转载请注明:http://www.cnblogs.com/croso/p/5396841.html 方法一: 名称框输入a1:a1000回车,1, ctrl+回车,单击A1,双击填充柄,“自动填充选项”--“填充序列” 方法二: 先在A1输入需填充的内容,然后点右上角的填充按钮,点击“系列”选项,输入终止值 方法三: 编辑 定位 输入需要输入数据的最后一个单元格的位置ctrl ↑(方向键的上箭头) 编辑栏输入公式=MOD(ROW()-1,2)+1按ctr

AES加密,CBC模式,0填充

java的aes加密的CBC模式只有NoPadding,PKCS5Padding,PKCS7Padding,因此0填充需要手动更改 public static byte[] encrypt(byte[] data, byte[] iv, byte[] key) { if(key.length != 16) { throw new RuntimeException("Invalid AES key length (must be 16 bytes)"); } else { try { S

JSP写出九九乘法表(表达式方法 和 脚本的方法)

<%@ page language="java" import="java.util.*" pageEncoding = "UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.ge

js获取字符串字节数方法小结

js获取字符串字节数的方法.分享给大家供大家参考.具体如下: 大家都知道,获取字符串的长度可用length来获取,那么获取这段字符串的字节数呢? 英文字母肯定lenght和字节数都一样:都是1而中文lenght=1,字节数=2因此,需要作的就是把中文字符的字节数计算出来. 方法一: alert('a'.replace(/[^\u0000-\u00ff]/g,"aaa").length); //原理:把中文字符替换成2个英文字母,那么字节数就是2, //示例中改成替换成3个英文字母了.

字节对齐方法

使用伪指令 #pragma pack (n), c编译器将按照n个字节对齐,使用伪指令#pragma pack(), 取消自定义字节对齐方式. __attribute__((aligned(n))),让所作用的结构体成员对齐在n字节自然边界上,如果结构体中有成员的长度大于n,则按照最大成员的长度来对齐. __attribute__((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,即对变量是一字节对齐.

Java字节码方法表与属性表详解

继续跟着上一次[https://www.cnblogs.com/webor2006/p/9502507.html]的那10个代表code的字节分析,如下: 而这些字节其实对应的信息是它: 所以问题就来了,aload_0不是助记符信息么,怎么就能够跟字节码文件中的字节对应上呢?所谓助记符其实也就是帮忙我们去记忆的符合,在底层其实也是对应的一个个十六进制的数字的,其它aload_0对应的就是2A这个十六进制数字,凭什么这么说?因为有jclasslib这么好的工具能帮我们对应上,将鼠标放在助记符上发现

比较C#中几种常见的复制字节数组方法的效率[转]

[原文链接] 在日常编程过程中,我们可能经常需要Copy各种数组,一般来说有以下几种常见的方法:Array.Copy,IList<T>.Copy,BinaryReader.ReadBytes,Buffer.BlockCopy,以及System.Buffer.memcpyimpl,由于最后一种需要使用指针,所以本文不引入该方法. 本次测试,使用以上前4种方法,各运行1000万次,观察结果. using System; using System.Collections.Generic; using