AES加密算法(C++)

AES算法是繼DES之后比較快且比較簡單的加密算法??

AES算法

算法原理:

AES密碼與分組密碼Rijndael基本上完全一致,Rijndael分組大小和密鑰大小都可以為128位、192位和256位。然而AES只要求分組大小為128位,因此只有分組長度為128Bit的Rijndael才稱為AES算法。

下面是分組長度為128位的AES算法,而key位數(shù)可以是128/192/256,本次實(shí)驗(yàn)選擇key的大小位128位.

AES

特點(diǎn)

  • 明文分組被描述為一個(gè)字節(jié)方陣并復(fù)制到狀態(tài)數(shù)組,在每輪替換和移位時(shí)都并行處理整個(gè)狀態(tài)分組。
  • 矩陣中字節(jié)的順序是按列排序的,例如128比特的明文分組的前4個(gè)字節(jié)占輸入矩陣的第一列,接下來的4個(gè)字節(jié)占第二列,依次類推。擴(kuò)展子密鑰數(shù)組也類似操作。
  • 假設(shè)AES使用128比特的密鑰,其密鑰被描述為一個(gè)字節(jié)方陣并將擴(kuò)展成為一個(gè)子密鑰數(shù)組w[i](具有44個(gè)32比特字),4個(gè)不同的字(共128比特)用作每輪的輪密鑰。
  • AES在每輪運(yùn)算中將進(jìn)行4個(gè)不同的步驟,1個(gè)是移位,3個(gè)是替換。

數(shù)學(xué)知識(shí)

在AES算法中的MixColumn層中會(huì)用到伽羅瓦域中的乘法運(yùn)算,而伽羅瓦域的運(yùn)算涉及一些數(shù)學(xué)知識(shí)。

素域

有限域有時(shí)也稱伽羅瓦域,它指的是由有限個(gè)元素組成的集合,在這個(gè)集合內(nèi)可以執(zhí)行加、減、乘和逆運(yùn)算。而在密碼編碼學(xué)中,我們只研究擁有有限個(gè)元素的域,也就是有限域。域中包含元素的個(gè)數(shù)稱為域的階。只有當(dāng)m是一個(gè)素?cái)?shù)冪時(shí),即m=p^n(其中n為正整數(shù)是p的次數(shù),p為素?cái)?shù)),階為m的域才存在。p稱為這個(gè)有限域的特征。

例如,有限域中元素的個(gè)數(shù)可以是11(p=11是一個(gè)素?cái)?shù),n=1)、可以是81(p=3是一個(gè)素?cái)?shù),n=4)、也可以是256(p=2是一個(gè)素?cái)?shù),n=8).....但有限域的中不可能擁有12個(gè)元素,因?yàn)?2=2·2·3,因此12也不是一個(gè)素?cái)?shù)冪。因此滿足p是一個(gè)素?cái)?shù)且滿足m = p^n這個(gè)公式,m才是一個(gè)素?cái)?shù)冪。

有限域中最直觀的例子就是階為素?cái)?shù)的域,即n=1的域。域GF(p)的元素可以用整數(shù)0、1、...、p-1l來表示。域的兩種操作就是模整數(shù)加法整數(shù)乘法模p。加上p是一個(gè)素?cái)?shù),整數(shù)環(huán)Z表示為GF(p),也成為擁有素?cái)?shù)個(gè)元素的素?cái)?shù)域或者伽羅瓦域。GF(p)中所有的非零元素都存在逆元,GF(p)內(nèi)所有的運(yùn)算都是模p實(shí)現(xiàn)的。

素域內(nèi)的算數(shù)運(yùn)算規(guī)則如下
  1. 加法和乘法都是通過模p實(shí)現(xiàn)的;
  2. 任何一個(gè)元素a的加法逆元都是由a+(a的逆元)=0 mod p得到的;
  3. 任何一個(gè)非零元素a的乘法逆元定義為a·a的逆元=1。

舉個(gè)例子,在素域GF(5)={0、1、2、3、4}中,2的加法逆元為3,這是因?yàn)?+(3)=5,5mod5=0,所以2+3=5mod5=0。2的乘法逆元為3,這是因?yàn)?·3=6,6mod5=1,所以2·3=6mod5=1。(在很多地方a的加法逆元[1]-a表示,a的乘法逆元[2]1/a表示)

注:GF(2)是一個(gè)非常重要的素域,也是存在的最小的有限域,由于GF(2)的加法,即模2加法與異或(XOR)門等價(jià),GF(2)的乘法與邏輯與(AND)門等價(jià),所以GF(2)對(duì)AES非常重要。

模2加法與異或(XOR)門等價(jià):
(1 + 0) \mod 2 = 1\\\\ (0 + 1) \mod 2 = 1\\\\ (0 + 0) \mod 2 = 0\\\\ (1 + 1) \mod 2 = 0\\\\
乘法與邏輯與(AND)門等價(jià):
(1 \times 0) \mod 2 = 0\\\\ (0 \times 1) \mod 2 = 0\\\\ (0 \times 0) \mod 2 = 0\\\\ (1 \times 1) \mod 2 = 1\\\\

擴(kuò)展域

如果有限域的階不是素?cái)?shù),則這樣的有限域內(nèi)的加法和乘法運(yùn)算就不能用模整數(shù)加法整數(shù)乘法模p表示。而且m>1的域被稱為擴(kuò)展域,為了處理擴(kuò)展域,我們就要使用不同的符號(hào)表示擴(kuò)展域內(nèi)的元素,使用不同的規(guī)則執(zhí)行擴(kuò)展域內(nèi)元素的算術(shù)運(yùn)算。

在擴(kuò)展域GF(2^m)中,元素并不是用整數(shù)表示的,而是用系數(shù)為域GF(2)中元素的多項(xiàng)式表示。這個(gè)多項(xiàng)式最大的度(冪)為m-1?,所以每個(gè)元素共有m個(gè)系數(shù),在AES算法使用的域GF(2^8)中,每個(gè)元素A∈GF(2^8)都可以表示為:
A(x) = a_7x^7 + a_6x^6 + a_5x^5 + a_4x^4 + a_3x^3 + a_2x^2+a_1x + a_0,x_i \in GF(2) = 0,1
注意:在域GF(28)中這樣的多項(xiàng)式共有256個(gè),這256個(gè)多項(xiàng)式組成的集合就是擴(kuò)展域GF(28)。每個(gè)多項(xiàng)式都可以按一個(gè)8位項(xiàng)鏈的數(shù)值形式存儲(chǔ):
A = (a_7,a_6,a_5,a_4,a_3,a_2,a_1,a_0)
x^7、x^6等因子都無需存儲(chǔ),因?yàn)閺奈坏奈恢镁涂梢郧宄嘏袛喑雒總€(gè)系數(shù)對(duì)應(yīng)的冪。

擴(kuò)展域GF(2^m)內(nèi)的加減法

在AES算法中的密鑰加法層中就使用了這部分的知識(shí),但是不是很明顯,因?yàn)槲覀兺ǔ0褦U(kuò)展域中的加法當(dāng)作異或運(yùn)算進(jìn)行處理了,因?yàn)樵跀U(kuò)展域中的加減法處理都是在底層域GF(2)內(nèi)完成的,與按位異或運(yùn)算等價(jià)。假設(shè)A(x)、B(x)∈GF(2^m),計(jì)算兩個(gè)元素之和的方法就是:
C(x) = A(x) + B(x) = \sum_{i=0}^{m-1}C_ix^i , c_i = (a_i + b_i) \mod 2
而兩個(gè)元素之差的計(jì)算公式就是:
C(x) = A(x) - B(x) = \sum_{i=0}^{m-1}C_ix^i , c_i = (a_i - b_i) \mod 2 = (a_i + b_i) \mod 2

注:在減法運(yùn)算中減號(hào)之所以變成加號(hào),這就和二進(jìn)制減法的性質(zhì)有關(guān)了,大家可以試著驗(yàn)算下。從上述兩個(gè)公式中我們發(fā)現(xiàn)在擴(kuò)展域中加法和減法等價(jià),并且與XOR等價(jià)(異或運(yùn)算也被稱作二進(jìn)制加法)。

擴(kuò)展域GF(2^m)內(nèi)的乘法

擴(kuò)展域的乘法主要運(yùn)用在AES算法的列混淆層(Mix Column)中,也是列混淆層中最重要的操作。我們項(xiàng)要將擴(kuò)展域中的兩個(gè)元素用多項(xiàng)式形式展開,然后使用標(biāo)準(zhǔn)的多項(xiàng)式乘法規(guī)則將兩個(gè)多項(xiàng)式相乘:

image

AES步驟詳解

AES算法主要有四種操作處理,分別是密鑰加法層(也叫輪密鑰加,英文Add Round Key)、字節(jié)代換層(SubByte)、行位移層(Shift Rows)、列混淆層(Mix Column)。而明文x和密鑰k都是由16個(gè)字節(jié)組成的數(shù)據(jù)(當(dāng)然密鑰還支持192位和256位的長度),它是按照字節(jié)的先后順序從上到下、從左到右進(jìn)行排列的。而加密出的密文讀取順序也是按照這個(gè)順序讀取的,相當(dāng)于將數(shù)組還原成字符串的模樣了,然后再解密的時(shí)候又是按照4·4數(shù)組處理的。AES算法在處理的輪數(shù)上只有最后一輪操作與前面的輪處理上有些許不同(最后一輪只是少了列混淆處理),在輪處理開始前還單獨(dú)進(jìn)行了一次輪密鑰加的處理。在處理輪數(shù)上,只考慮128位密鑰的10輪處理。

其中字節(jié)排列方式需要按照如下轉(zhuǎn)換:

字節(jié)排列

AES算法流程圖如下:

AES流程

實(shí)現(xiàn)步驟及代碼

按照AES流程圖,對(duì)每一層的代碼進(jìn)行實(shí)現(xiàn).

密鑰加法層

在密鑰加法層中有兩個(gè)輸入的參數(shù),分別是明文子密鑰k[0],而且這兩個(gè)輸入都是128位的。在擴(kuò)展域中加減法操作和異或運(yùn)算等價(jià),所以這里的處理也就異常的簡單了,只需要將兩個(gè)輸入的數(shù)據(jù)進(jìn)行按字節(jié)異或操作就會(huì)得到運(yùn)算的結(jié)果。

如下圖:

image

代碼如下:

//輪密鑰加變換 - 將每一列與擴(kuò)展密鑰進(jìn)行異或
void AddRoundKey(byte mtx[4 * 4], word k[4])
{
    for (int i = 0; i < 4; ++i)
    {
        word k1 = k[i] >> 24;
        word k2 = (k[i] << 8) >> 24;
        word k3 = (k[i] << 16) >> 24;
        word k4 = (k[i] << 24) >> 24;

        mtx[i] = mtx[i] ^ byte(k1.to_ulong());
        mtx[i + 4] = mtx[i + 4] ^ byte(k2.to_ulong());
        mtx[i + 8] = mtx[i + 8] ^ byte(k3.to_ulong());
        mtx[i + 12] = mtx[i + 12] ^ byte(k4.to_ulong());
    }
}

AES密鑰生成

密鑰拓展流程

首先定義位置變換函數(shù)RotWord(),作用是接受一個(gè)字 [a0, a1, a2, a3]作為輸入,循環(huán)左移一個(gè)字節(jié)后輸出[a1, a2, a3, a0],代碼如下:

word RotWord(const word &w)
{
    word result(0x0);
    result = (w << 8) | (w >> 24);
    return result;
}

定義S盒變換函數(shù)SubWord(),接受一個(gè)字 [a0, a1, a2, a3] 作為輸入,然后每一個(gè)byte,例如a0,前四個(gè)字節(jié)為行,后四個(gè)字節(jié)為列,從S_Box中查找并且返回四個(gè)元素。,代碼如下:

word SubWord(const word& sw)
{
    word temp;
    for(int i=0; i<32; i+=8)
    {
        int row = sw[i+7]*8 + sw[i+6]*4 + sw[i+5]*2 + sw[i+4];
        int col = sw[i+3]*8 + sw[i+2]*4 + sw[i+1]*2 + sw[i];
        byte val = S_Box[row][col];
        for(int j=0; j<8; ++j)
            temp[i+j] = val[j];
    }
    return temp;
}

輪常數(shù)Rcon[]作為一個(gè)常量數(shù)組,每一輪生成密鑰的時(shí)候需要作為參數(shù)異或

// 輪常數(shù),密鑰擴(kuò)展中用到。(AES-128只需要10輪)
word Rcon[10] = {0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
                 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000};

密鑰拓展函數(shù)KeyExpansion(),接受一個(gè)參數(shù)為外部密鑰,另外一個(gè)為需要拓展的輪密鑰數(shù)組

//密鑰擴(kuò)展函數(shù) - 對(duì)128位密鑰進(jìn)行擴(kuò)展得到 w[4*(Nr+1),Nr為輪數(shù)
void KeyExpansion(byte key[4 * N_key], word w[4 * (N_round + 1)])
{
    word temp;
    int i = 0;
    while (i < N_key)   //前四個(gè)word就是輸入的key
    {
        w[i] = ToWord(key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]);
        ++i;
    }
    i = N_key;
    while (i < 4 * (N_round + 1))
    {
        temp = w[i - 1]; //記錄前一個(gè)word
        if (i % N_key == 0)
        { //temp先位置表換RotWord,再S盒變換,然后與輪常數(shù)異或,最后w[i-N_key] 異或
            w[i] = w[i - N_key] ^ SubWord(RotWord(temp)) ^ Rcon[i / N_key - 1];
        }
        else
        {
            w[i] = w[i - N_key] ^ temp;
        }
        i++;
    }
}

字節(jié)替換層

S盒字節(jié)替換,主要功能就是讓輸入的數(shù)據(jù)通過S_box表完成從一個(gè)字節(jié)到另一個(gè)字節(jié)的映射,讀取S_box數(shù)據(jù)的方法就是要將輸入數(shù)據(jù)的每個(gè)字節(jié)的高四位作為第一個(gè)下標(biāo),第四位作為第二個(gè)下標(biāo)。然后返回?cái)?shù)據(jù),字節(jié)替換主要是為了擾亂數(shù)據(jù)。

S盒:

image

逆S盒:

image

圖解如下:

字節(jié)替換圖解

正向S盒變換代碼如下:

//S盒變換 - 前4位為行號(hào),后4位為列號(hào)
void SubBytes(byte mtx[4 * 4])
{
    for (int i = 0; i < 16; ++i)
    {
        int row = mtx[i][7] * 8 + mtx[i][6] * 4 + mtx[i][5] * 2 + mtx[i][4];
        int col = mtx[i][3] * 8 + mtx[i][2] * 4 + mtx[i][1] * 2 + mtx[i][0];
        mtx[i] = S_Box[row][col];
    }
}

反向S盒變換代碼如下:

//  逆S盒變換
void InvSubBytes(byte mtx[4*4])
{
    for(int i=0; i<16; ++i)
    {
        int row = mtx[i][7]*8 + mtx[i][6]*4 + mtx[i][5]*2 + mtx[i][4];
        int col = mtx[i][3]*8 + mtx[i][2]*4 + mtx[i][1]*2 + mtx[i][0];
        mtx[i] = Inv_S_Box[row][col];
    }
}

行移位層

將輸入數(shù)據(jù)作為一個(gè)4·4的字節(jié)矩陣進(jìn)行處理,然后將這個(gè)矩陣的字節(jié)進(jìn)行位置上的置換。在加密時(shí)行位移處理與解密時(shí)的處理相反,我們這里將解密時(shí)的處理稱作逆行位移。它之所以稱作行位移,是因?yàn)樗辉?img class="math-inline" src="https://math.jianshu.com/math?formula=4%C2%B74" alt="4·4" mathimg="1">矩陣的行間進(jìn)行操作,每行4字節(jié)的數(shù)據(jù)。在加密時(shí),保持矩陣的第一行不變,第二行向左移動(dòng)8Bit(一個(gè)字節(jié))、第三行向左移動(dòng)2個(gè)字節(jié)、第四行向左移動(dòng)3個(gè)字節(jié)。而在解密時(shí)恰恰相反,依然保持第一行不變,將第二行向右移動(dòng)一個(gè)字節(jié)、第三行右移2個(gè)字節(jié)、第四行右移3個(gè)字節(jié)。最終結(jié)束。

正向行移位圖解:

image

代碼如下:

//正向行變換 - 按字節(jié)循環(huán)移位
void ShiftRows(byte mtx[4 * 4])
{
    // 第二行循環(huán)左移一位
    byte temp = mtx[4];
    for (int i = 0; i < 3; ++i)
        mtx[i + 4] = mtx[i + 5];
    mtx[7] = temp;
    // 第三行循環(huán)左移兩位
    for (int i = 0; i < 2; ++i)
    {
        temp = mtx[i + 8];
        mtx[i + 8] = mtx[i + 10];
        mtx[i + 10] = temp;
    }
    // 第四行循環(huán)左移三位
    temp = mtx[15];
    for (int i = 3; i > 0; --i)
        mtx[i + 12] = mtx[i + 11];
    mtx[12] = temp;
}

反向行移位圖解:

image

代碼如下:

// 逆行變換 - 以字節(jié)為單位循環(huán)右移
void InvShiftRows(byte mtx[4*4])
{
    // 第二行循環(huán)右移一位
    byte temp = mtx[7];
    for(int i=3; i>0; --i)
        mtx[i+4] = mtx[i+3];
    mtx[4] = temp;
    // 第三行循環(huán)右移兩位
    for(int i=0; i<2; ++i)
    {
        temp = mtx[i+8];
        mtx[i+8] = mtx[i+10];
        mtx[i+10] = temp;
    }
    // 第四行循環(huán)右移三位
    temp = mtx[12];
    for(int i=0; i<3; ++i)
        mtx[i+12] = mtx[i+13];
    mtx[15] = temp;
}

列混淆層

列混淆子層是AES算法中最為復(fù)雜的部分,屬于擴(kuò)散層,列混淆操作是AES算法中主要的擴(kuò)散元素,它混淆了輸入矩陣的每一列,使輸入的每個(gè)字節(jié)都會(huì)影響到4個(gè)輸出字節(jié)。行位移子層和列混淆子層的組合使得經(jīng)過三輪處理以后,矩陣的每個(gè)字節(jié)都依賴于16個(gè)明文字節(jié)成可能。

在加密的正向列混淆中,我們要將輸入的4·4矩陣左乘一個(gè)給定的4·4矩陣。而它們之間的加法、乘法都在擴(kuò)展域GF(2^8)中進(jìn)行,,在矩陣相乘計(jì)算中,出現(xiàn)了加法和乘法,而前面提到了在拓展域中加法等同于異或運(yùn)算,而對(duì)于乘法,需要特殊的方式進(jìn)行處理,于是將+號(hào)換成^號(hào),然后將伽羅瓦域的乘法定義成一個(gè)有兩個(gè)參數(shù)的函數(shù),并讓他返回最后計(jì)算結(jié)果,最后列混淆代碼如下:

//正向列變換
void MixColumns(byte mtx[4*4])
{
    byte arr[4];
    for(int i=0; i<4; ++i)
    {
        for(int j=0; j<4; ++j)
            arr[j] = mtx[i+j*4];
 
        mtx[i] = GFMul(0x02, arr[0]) ^ GFMul(0x03, arr[1]) ^ arr[2] ^ arr[3];
        mtx[i+4] = arr[0] ^ GFMul(0x02, arr[1]) ^ GFMul(0x03, arr[2]) ^ arr[3];
        mtx[i+8] = arr[0] ^ arr[1] ^ GFMul(0x02, arr[2]) ^ GFMul(0x03, arr[3]);
        mtx[i+12] = GFMul(0x03, arr[0]) ^ arr[1] ^ arr[2] ^ GFMul(0x02, arr[3]);
    }
}

在解密的逆向列混淆中與正向列混淆的不同之處在于使用的左乘矩陣不同,它與正向列混淆的左乘矩陣互為逆矩陣,也就是說,數(shù)據(jù)矩陣同時(shí)左乘這兩個(gè)矩陣后,數(shù)據(jù)矩陣不會(huì)發(fā)生任何變化。下面是圖解:

正向混淆處理:

正向列混淆

逆向混淆處理:

逆向列混淆

反向列變換代碼如下:

//反向列混淆
void InvMixColumns(byte mtx[4*4])
{
    byte arr[4];
    for(int i=0; i<4; ++i)
    {
        for(int j=0; j<4; ++j)
            arr[j] = mtx[i+j*4];
        mtx[i] = GFMul(0x0e, arr[0]) ^ GFMul(0x0b, arr[1]) ^ GFMul(0x0d, arr[2]) ^ GFMul(0x09, arr[3]);
        mtx[i+4] = GFMul(0x09, arr[0]) ^ GFMul(0x0e, arr[1]) ^ GFMul(0x0b, arr[2]) ^ GFMul(0x0d, arr[3]);
        mtx[i+8] = GFMul(0x0d, arr[0]) ^ GFMul(0x09, arr[1]) ^ GFMul(0x0e, arr[2]) ^ GFMul(0x0b, arr[3]);
        mtx[i+12] = GFMul(0x0b, arr[0]) ^ GFMul(0x0d, arr[1]) ^ GFMul(0x09, arr[2]) ^ GFMul(0x0e, arr[3]);
    }
}

密鑰加法層

這一層主要是明文矩陣盒子密鑰矩陣進(jìn)行異或操作,在密鑰加法層中有兩個(gè)輸入的參數(shù),分別是明文和子密鑰,而且這兩個(gè)輸入都是128位的。只需要將兩個(gè)輸入的數(shù)據(jù)進(jìn)行按字節(jié)異或操作就會(huì)得到運(yùn)算的結(jié)果。

圖解:

image

代碼如下:

//輪密鑰加變換 - 將每一列與擴(kuò)展密鑰進(jìn)行異或
void AddRoundKey(byte mtx[4*4], word k[4])
{
    for(int i=0; i<4; ++i)
    {
        word k1 = k[i] >> 24;
        word k2 = (k[i] << 8) >> 24;
        word k3 = (k[i] << 16) >> 24;
        word k4 = (k[i] << 24) >> 24;
        
        mtx[i] = mtx[i] ^ byte(k1.to_ulong());
        mtx[i+4] = mtx[i+4] ^ byte(k2.to_ulong());
        mtx[i+8] = mtx[i+8] ^ byte(k3.to_ulong());
        mtx[i+12] = mtx[i+12] ^ byte(k4.to_ulong());
    }
}

實(shí)現(xiàn)加密函數(shù)

加密函數(shù)按照流程圖,首先開始是先進(jìn)行一次輪密鑰加,然后開始9輪的字節(jié)替換+行移位+列混淆+輪密鑰加的操作,循環(huán)之后再做一次字節(jié)替換+行移位+輪密鑰加就完成加密操作了.

void encrypt(byte in[4*4], word w[4*(N_round+1)])
{
    word key[4];
    for(int i=0; i<4; ++i)
        key[i] = w[i];
    AddRoundKey(in, key);
 
    for(int round=1; round<N_round; ++round)
    {
        SubBytes(in);
        ShiftRows(in);
        MixColumns(in);
        for(int i=0; i<4; ++i)
            key[i] = w[4*round+i];
        AddRoundKey(in, key);
    }
 
    SubBytes(in);
    ShiftRows(in);
    for(int i=0; i<4; ++i)
        key[i] = w[4*N_round+i];
    AddRoundKey(in, key);
}

實(shí)現(xiàn)解密函數(shù)

解密函數(shù)與加密差不多,只不過將行移位變成反向行移位,列混淆變成反向列混淆,字節(jié)替換變成逆字節(jié)替換即可.

代碼如下:

void decrypt(byte in[4*4], word w[4*(N_round+1)])
{
    word key[4];
    for(int i=0; i<4; ++i)
        key[i] = w[4*N_round+i];
    AddRoundKey(in, key);
 
    for(int round=N_round-1; round>0; --round)
    {
        InvShiftRows(in);
        InvSubBytes(in);
        for(int i=0; i<4; ++i)
            key[i] = w[4*round+i];
        AddRoundKey(in, key);
        InvMixColumns(in);
    }
 
    InvShiftRows(in);
    InvSubBytes(in);
    for(int i=0; i<4; ++i)
        key[i] = w[i];
    AddRoundKey(in, key);
}

測(cè)試加密解密函數(shù)

image

可以發(fā)現(xiàn)上面面的測(cè)試中明文與解密之后的明文是完全正確的,說明加密函數(shù)與解密函數(shù)正確!

測(cè)試代碼如下:

void Aes_test()
{
    byte key[16] = {0x2b, 0x7e, 0x15, 0x16,
                    0x28, 0xae, 0xd2, 0xa6,
                    0xab, 0xf7, 0x15, 0x88,
                    0x09, 0xcf, 0x4f, 0x3c};

    byte plain[16] = {0x32, 0x88, 0x31, 0xe0,
                      0x43, 0x5a, 0x31, 0x37,
                      0xf6, 0x30, 0x98, 0x07,
                      0xa8, 0x8d, 0xa2, 0x34};
    // 輸出密鑰
    cout << "Key is : ";
    for (int i = 0; i < 16; ++i)
        cout << hex << key[i].to_ulong() << " ";
    cout << endl;
    word w[4 * (N_round + 1)];
    KeyExpansion(key, w);
    // 輸出待加密的明文
    cout << endl
         << "the plaintext to encrypy:" << endl;
    for (int i = 0; i < 16; ++i)
    {
        cout << hex << plain[i].to_ulong() << " ";
        if ((i + 1) % 4 == 0)
            cout << endl;
    }
    cout << endl;
    // 加密,輸出密文
    encrypt(plain, w);
    cout << "cipher : " << endl;
    for (int i = 0; i < 16; ++i)
    {
        cout << hex << plain[i].to_ulong() << " ";
        if ((i + 1) % 4 == 0)
            cout << endl;
    }
    cout << endl;
    // 解密,輸出明文
    decrypt(plain, w);
    cout << "plain arter decrypt:" << endl;
    for (int i = 0; i < 16; ++i)
    {
        cout << hex << plain[i].to_ulong() << " ";
        if ((i + 1) % 4 == 0)
            cout << endl;
    }
    cout << endl;
}

實(shí)現(xiàn)加解密文件

加密文件函數(shù),返回加密后的文件名:

string encryptFile(string oname, string suffix, word w[4 * (N_round + 1)])
{
    string outputfilename = oname + "_cipher.bin";
    bitset<128> data;
    byte plain[16];
    cout << "begining encrypy..........." << endl;
    clock_t start = clock();
    // 將文件加密到 oname + cipher.bin 中
    ifstream in;
    ofstream out;
    in.open(oname + suffix, ios::binary);  //輸入文件
    out.open(outputfilename, ios::binary); //輸出加密文件

    while (in.read((char *)&data, sizeof(data)))
    {
        divideToByte(plain, data);
        encrypt(plain, w);
        data = mergeByte(plain);
        out.write((char *)&data, sizeof(data));
        data.reset(); // 置0
    }
    in.close();
    out.close();
    clock_t end = clock();
    cout << "encrypy finish!" << endl;
    cout << "encrypy cost time : " << (end - start) << " ms" << endl;
    return outputfilename; //返回加密之后的文件
}

解密文件函數(shù),返回解密后的文件名:

string decryptFile(string filename, string oname, string suffix, word w[4 * (N_round + 1)])
{
    ifstream in;
    ofstream out;
    in.open(filename, ios::binary);
    string outputfilename = oname + "_decrypt" + suffix;
    out.open(outputfilename, ios::binary);
    bitset<128> data;
    byte plain[16];
    cout << "begining decrypt............" << endl;
    clock_t start = clock();

    while (in.read((char *)&data, sizeof(data)))
    {
        divideToByte(plain, data);
        decrypt(plain, w);
        data = mergeByte(plain);
        out.write((char *)&data, sizeof(data));
        data.reset(); // 置0
    }
    in.close();
    out.close();
    clock_t end = clock();
    cout << "decrypt finish!" << endl;
    cout << "decrypt cost time : " << end - start << " ms" << endl;
    return outputfilename;
}

實(shí)現(xiàn)效果:

加密txt文件:

image
image

加密jpg文件:

image
image

加密mp3文件:

image
image

加密doc文件:

image
image

AES五種加密模式

實(shí)現(xiàn)五種加密方式的密鑰是一個(gè)置換表unsigned char Table[4] = {0x12, 0xb1, 0x53, 0x28};,加密函數(shù)是原文與密鑰的異或.

ECB模式(電子密碼本模式)

加密前根據(jù)加密塊大?。ㄈ鏏ES為128位)分成若干塊,之后將每塊使用相同的密鑰單獨(dú)加密,解密同理。

ECB模式由于每塊數(shù)據(jù)的加密是獨(dú)立的因此加密和解密都可以并行計(jì)算,ECB模式最大的缺點(diǎn)是相同的明文塊會(huì)被加密成相同的密文塊,這種方法在某些環(huán)境下不能提供嚴(yán)格的數(shù)據(jù)保密性。

流程圖如下:

image

實(shí)現(xiàn)代碼:

//電子密碼本模式,分組大小為4
unsigned char* ECB(unsigned char *plain, int N)
{
    int gNum = N / groupSize; //分組數(shù)量
    //密文
    unsigned char *cipher = new unsigned char[N];
    int count = 0;
    for (int i = 0; i < gNum; ++i)
    {   unsigned char temp[groupSize];
        for(int j = 0;j < groupSize;++j)
            temp[j] = plain[count++];
        //加密
        encrypt(temp,groupSize);
        for(int j = i*4;j < i*4 + 4;++j)
            cipher[j] = temp[j - i * 4];
    }
    return cipher;//返回密文
}

解密方法也是讓密文與密鑰進(jìn)行異或即可,實(shí)現(xiàn)效果如下:

image
CBC模式(分組鏈接模式)

CBC模式對(duì)于每個(gè)待加密的密碼塊在加密前會(huì)先與前一個(gè)密碼塊的密文異或然后再用加密器加密。第一個(gè)明文塊與一個(gè)叫初始化向量的數(shù)據(jù)塊異或。

可用公式總結(jié)為:
C_i = E_K(P_i XOR C_{i-1}) \\ C_{-1} = IV
流程圖如下:

image

CBC模式相比ECB有更高的保密性,但由于對(duì)每個(gè)數(shù)據(jù)塊的加密依賴與前一個(gè)數(shù)據(jù)塊的加密所以加密無法并行。與ECB一樣在加密前需要對(duì)數(shù)據(jù)進(jìn)行填充,不是很適合對(duì)流數(shù)據(jù)進(jìn)行加密。

代碼如下:

加密函數(shù):

//CCB加密函數(shù)
unsigned char *CCB(unsigned char *plain, int N)
{
    int gNum = N / groupSize; //分組數(shù)量
    //密文
    unsigned char *cipher = new unsigned char[N];
    //設(shè)置初始向量
    unsigned char C[groupSize] = {0xe4, 0xa9, 0x5d, 0x99};
    int count = 0;
    for (int i = 0; i < gNum; ++i)
    {
        unsigned char temp[groupSize];
        for (int j = 0; j < groupSize; ++j)
            temp[j] = plain[count++];
        //加密
        for (int j = 0; j < groupSize; ++j) //先與初始向量異或
            temp[i] ^= C[i];
        encrypt(temp, groupSize); //加密
        for (int j = i * 4; j < i * 4 + 4; ++j)
        {
            cipher[j] = temp[j - i * 4];
            C[j - i * 4] = temp[j - i * 4];//設(shè)置新向量
        }    
    }
    return cipher;
}

解密函數(shù):

//CCB解密函數(shù)
unsigned char *dCCB(unsigned char *cipher, int N)
{
    int gNum = N / groupSize; //分組數(shù)量
    //明文
    unsigned char *plain = new unsigned char[N];
    //設(shè)置初始向量
    unsigned char C[groupSize] = {0xe4, 0xa9, 0x5d, 0x99};
    int count = 0;
    for (int i = 0; i < gNum; ++i)
    {
        unsigned char temp[groupSize];
        for (int j = 0; j < groupSize; ++j)
            temp[j] = cipher[count++];
        //解密
        encrypt(temp, groupSize); //先解密
        for (int j = 0; j < groupSize; ++j) //然后與初始向量異或
            temp[i] ^= C[i];
        for (int j = i * 4; j < i * 4 + 4; ++j)
        {
            plain[j] = temp[j - i * 4];
            C[j - i * 4] = cipher[j];//設(shè)置新向量
        }    
    }
    return plain;
}

實(shí)現(xiàn)效果:

image
CFB模式(密文反饋模式)

與前面的模式不同,CFB模式可以將消息被當(dāng)成是比特流.可以總結(jié)為如下的公式:
C_i = P_i XOR E_K(C_{i-1})\\ C_{-1} = IV
流程圖如下:

image

加密代碼:

//密文反饋模式,加密函數(shù)
unsigned char *CFB(unsigned char *plain, int N)
{
    int gsize = 2;
    int gNum = N / gsize; //分組數(shù)量,分成8組,每組大小為2
    //密文
    unsigned char *cipher = new unsigned char[N];
    //設(shè)置初始向量
    unsigned char C[4] = {0xe4, 0xa9, 0x5d, 0x99};
    unsigned char S[2]; //前2個(gè)字節(jié)
    int count = 0;
    for (int i = 0; i < gNum; ++i)
    {
        unsigned char temp[gsize];  //分組明文,大小為2
        for (int j = 0; j < gsize; ++j)
            temp[j] = plain[count++]; 
        //加密
        //先對(duì)初始向量進(jìn)行加密
        encrypt(C,4);
        //獲取結(jié)果C的前兩個(gè)bit,然后前2個(gè)bit S與明文進(jìn)行異或
        for(int j = 0;j < gsize;++j){
            temp[j] ^= C[j];
            S[j] = temp[j]; //獲取密文的2bit
        }

        //設(shè)置密文
        for (int j = i * gsize; j < i * gsize + gsize; ++j)
        {
            cipher[j] = temp[j - i * gsize];
        }
        //設(shè)置新向量,新向量左移
        for(int j = 0;j < gsize;++j)
            {
                C[j] = C[j + gsize];
                C[j + gsize] = S[j];
            }
    }
    return cipher;
}

解密代碼:

//密文反饋解密
unsigned char *dCFB(unsigned char *cipher, int N)
{
    int gsize = 2;
    int gNum = N / gsize; //分組數(shù)量,分成8組,每組大小為2
    //明文
    unsigned char *plain = new unsigned char[N];
    //設(shè)置初始向量
    unsigned char C[4] = {0xe4, 0xa9, 0x5d, 0x99};
    unsigned char S[2]; //前2個(gè)字節(jié)
    int count = 0;
    for (int i = 0; i < gNum; ++i)
    {
        unsigned char temp[gsize];  //分組密文
        for (int j = 0; j < gsize; ++j)
            temp[j] = cipher[count++]; 
        //解密
        //先對(duì)初始向量進(jìn)行加密
        encrypt(C,4);
        //獲取結(jié)果C的前兩個(gè)bit,然后前2個(gè)bit S與明文進(jìn)行異或
        for(int j = 0;j < 2;++j){
            S[j] = temp[j];
            temp[j] = C[j] ^ temp[j];
        }
        //設(shè)置明文
        for (int j = i * gsize; j < i * gsize + gsize; ++j)
        {
            plain[j] = temp[j - i * gsize];
        }
        //設(shè)置新向量,新向量左移
        for(int j = 0;j < gsize;++j)
            {
                C[j] = C[j + gsize];
                C[j+gsize] = S[j];
            }
    }
    return plain;
}

實(shí)現(xiàn)效果:

image
OFB模式(輸出反饋模式)

OFB是先用塊加密器生成密鑰流(Keystream),然后再將密鑰流與明文流異或得到密文流,解密是先用塊加密器生成密鑰流,再將密鑰流與密文流異或得到明文,由于異或操作的對(duì)稱性所以加密和解密的流程是完全一樣的。

OFB與CFB一樣都非常適合對(duì)流數(shù)據(jù)的加密,OFB由于加密和解密都依賴與前一段數(shù)據(jù),所以加密和解密都不能并行。

流程圖如下:

image

加密解密代碼:

//輸出反饋模式,加密解密函數(shù)相同
unsigned char *OFB(unsigned char *plain, int N)
{
    int gsize = 2;
    int gNum = N / gsize; //分組數(shù)量,分成8組,每組大小為2
    //密文
    unsigned char *cipher = new unsigned char[N];
    //設(shè)置初始向量
    unsigned char C[4] = {0xee, 0xa9, 0x5d, 0x99};
    unsigned char S[2]; //前2個(gè)字節(jié)
    int count = 0;
    for (int i = 0; i < gNum; ++i)
    {
        unsigned char temp[gsize];  //分組明文
        for (int j = 0; j < gsize; ++j)
            temp[j] = plain[count++]; 
        //加密
        //先對(duì)初始向量進(jìn)行加密
        encrypt(C,4);
        //獲取結(jié)果C的前兩個(gè)bit,然后前2個(gè)bit S與明文進(jìn)行異或
        for(int j = 0;j < 2;++j){
            S[j] = C[j];    //取向量加密后的前兩位
            temp[j] ^= C[j];
        }

        //設(shè)置密文
        for (int j = i * gsize; j < i * gsize + gsize; ++j)
        {
            cipher[j] = temp[j - i * gsize];
        }
        //設(shè)置新向量,新向量左移
        for(int j = 0;j < gsize;++j)
            {
                C[j] = C[j + gsize];
                C[j + gsize] = S[j];
            }
    }
    return cipher;
}

實(shí)現(xiàn)效果:

image
CTR模式(計(jì)數(shù)器模式)

類型于CFB,但是加密每個(gè)計(jì)數(shù)值,而不是任何反饋值,對(duì)每個(gè)明文分組,必須有不同的密鑰和計(jì)數(shù)值 (從不重復(fù)使用),,可以用如下公式表示:
O_i = E_K(i)\\ C_i = P_i XOR O_i
計(jì)數(shù)器模式流程圖如下:

image

計(jì)數(shù)器模式加密函數(shù)與解密函數(shù)一樣,代碼如下:

//計(jì)數(shù)器模式,加密函數(shù)
unsigned char *CTR(unsigned char *plain, int N)
{
    int gNum = N / groupSize; //分組數(shù)量
    //密文
    unsigned char *cipher = new unsigned char[N];
    //設(shè)置隨機(jī)值
    unsigned char Counter[groupSize*groupSize] = {0x44, 0xa9, 0x5d, 0x99,
                                                  0xe5, 0xf1, 0x3d, 0x91,
                                                  0x16, 0xa6, 0xe1, 0x33,
                                                  0x22, 0xdd, 0xab, 0x1f};
    int count = 0;
    for (int i = 0; i < gNum; ++i)
    {
        unsigned char temp[groupSize];  //明文分組
        unsigned char C[groupSize];     //分組隨機(jī)值
        for (int j = 0; j < groupSize; ++j)
            {
                temp[j] = plain[count++];
                C[j] = Counter[i*4+j];
            }
        //加迷
        //首先加密隨機(jī)值C
        encrypt(C, groupSize);
        //然后與明文進(jìn)行異或
        for(int j = 0;j < groupSize;++j)
            temp[j] ^= C[j];
        //設(shè)置密文
        for(int j = i*groupSize;j < i*groupSize+groupSize;j++)
            cipher[j] = temp[j-i*groupSize]; 
    }
    return cipher;
}

實(shí)現(xiàn)效果如下:

image

參考:


  1. 設(shè)“+”為一個(gè)交換性的二元運(yùn)算,即對(duì)于所有x,y,x+y=y+x。若該集內(nèi)存在一個(gè)元素0,使得對(duì)于所有x,x+0=0+x=x,則此元素是唯一的。如果對(duì)于一個(gè)給定的x,存在一個(gè)x'使得x+x'=x'+x=0,則稱x'是x的加法逆元。 ?

  2. 乘法逆元,是指數(shù)學(xué)領(lǐng)域群G中任意一個(gè)元素a,都在G中有唯一的逆元a‘,具有性質(zhì)a×a'=a'×a=e,其中e為該群的單位元。 ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容