完整的AES分組與文件的加解密功能程序實現

[在此處輸入文章標題]

 

 

 

 

 

 

 

 

 

 

 

完整的AES分組與文件的加解密功能程序實現

簽名:

1 前言

    本報告論述在論述AES加密/解密算法理論的基礎上,設計一個AES加密/解密軟件系統。AES,密碼學中的高級加密標準(Advanced Encryption Standard,AES),又稱 Rijndael加密法,是美國聯邦政府採用的一種區塊加密標準。這個標準用來替代原先的DES,已經被多方分析且廣爲全世界所使用。AES在軟體及硬件上都能快速地加解密,相對來說較易於實作,且只需要很少的記憶體。作爲一個新的加密標準,目前正被部署應用到更廣大的範圍。AES具有以下可變密鑰長爲128bit、192bit、256bit三種,可變分組長爲 128bit、192bit、256bit,同時它的強度高,能夠抗所有已知的攻擊。

2 程序實現要點(編程語言與開發環境、構件實現核心方法與程序設計技巧)

編寫語言:java

開發環境:windows8,MyEclipse,

用MyEclipse編寫,生產jar文件,進而通過exe4j轉化爲exe程序,該可以在裝有windows 系統的計算機上使用。

 

3 程序實算展示

3.1 字節替換

SubWord()變換是一個基於S盒的非線性置換,它用於將輸入或中間態的每一個字節通過一個簡單的查表操作,將其映射爲另一個字節。映射方法是把輸入字節的高四位作爲S盒的行值,低四位作爲列值,然後取出S盒中對應的行和列的元素作爲輸出。

public    void SubWord(char []A)//字節替代,用於密鑰擴展;

{

    for(int i=0;i<4;i++)

           A[i]=S_BOX[A[i]];

}

//    S盒置換

 public static char [] S_BOX = {

           99,124,119,123,242,107,111,197,48,1,103,43,254,215,171,

           118,202,130,201,125,250,89,71,240,173,212,162,175,156,164,

          114,192,183,253,147,38,54,63,247,204,52,165,229,241,113,216,

          49,21,4,199,35,195,24,150,5,154,7,18,128,226,235,39,178,117,

         9,131,44,26,27,110,90,160,82,59,214,179,41,227,47,132,83,209,

           0,237,32,252,177,91,106,203,190,57,74,76,88,207,208,239,

           170,251,67,77,51,133,69,249,2,127,80,60,159,168,81,163,64,

     143,146,157,56,245,188,182,218,33,16,255,243,210,205,12,19,236,

           95,151,68,23,196,167,126,61,100,93,25,115,96,129,79,220,

          34,42,144,136,70,238,184,20,222,94,11,219,224,50,58,10,73,6,

      36,92,194,211,172,98,145,149,228,121,231,200,55,109,141,213,

            78,169,108,86,244,234,101,122,174,8,186,120,37,46,28,166,

           180,198,232,221,116,31,75,189,139,138,112,62,181,102,72,3,        246,14,97,53,87,185,134,193,29,158,225,248,152,17,105,217,142,

           148,155,30,135,233,206,85,40,223,140,161,137,13,191,230,

            66,104,65,153,45,15,176,84,187,22,};

 

3.2行移位

ShiftRows()完成基於行的循環移位操作,變換方法是第0行不動,第一行循環左移一個字節,第二位循環左移兩個字節,第三行循環左移三個字節。

/*

 * 行移位

 */

public     void  ShiftRows(char []state, intNb,JTextArea showProcess)

{

      String STR="";

      String output="";

      char[]  t = new char [8];

         for( intr=0;r<4;r++)

         {

             for(intc=0;c<Nb;c++)t[c]= state[Nb*r+(r+c)%Nb];

             for(intc=0;c<Nb;c++){

                 state[Nb*r+c]=t[c];

                 STR+=state[Nb*r+c];

                                                   }

         }

        for (inti=0;i<STR.length();i++)  

   {  

    int ch =(int)STR.charAt(i);  

    String s4 = Integer.toHexString(ch);  

    output = output+"  0x" + s4;

   }   //0x表示十六進制

        showProcess.append("\n經過行移位後變爲:"+output);

}

3.3列混合

MixColumns()實現逐列混合,方法是s’(x)=c(x)*s(x)mod(x^4+1)

/*

 * 列混合

 */

public    void MixColumns(char[]state, int Nb,JTextArea showProcess)

{

    String STR="";

    String output="";

    int [] t= new int[4];

    for( int c=0;c<Nb;c++)

    {

          for(int r=0;r<4;r++)t[r] = state[Nb*r+c];

       for(int r=0;r<4;r++)

       {

           state[Nb*r+c] = (char)(Ffmul(0x02,t[r])^Ffmul(0x03,t[(r+1)%4])

                      ^t[(r+2)%4]^t[(r+3)%4]);

           STR+=state[Nb*r+c];

       }

    }

    for (int i=0;i<STR.length();i++)  

   {  

    int ch =(int)STR.charAt(i);  

    String s4 = Integer.toHexString(ch);  

    output = output+" 0x" +s4;

   }   //0x表示十六進制

     showProcess.append("\n經過列混合後變爲"+output);

}

 

public    int Ffmul(int A, int B)

{

    

      //查對數表;

        if(A==0||B==0)return 0;

        A = Log[A];

        B = Log[B];

        A =(A+B)%0xff;

      //查反對數表;

        A = Log_1[A];

        return A;

}

 

 

3.4輪密鑰加

AddRoundKey()用於將輸入或中間態S的每一列與一個密鑰字ki進行按位異或,每一個輪密鑰由Nb個字組成。

/*

 * 輪密鑰加

 */

public    void AddRoundKey(char[]state, int Nb,int round,JTextArea showProcess)

{

        String Kkey="";

        String STR="";

        String output="";

          for(int c=0;c<Nb;c++,round++)

       for(int r=0;r<4;r++){

           state[r*Nb+c] = (char)(state[r*Nb+c]^w[round*4+r]);

           Kkey+=w[round*4+r];

           STR+=state[Nb*r+c];

                      }

          for (int i=0;i<Kkey.length();i++)  

   {  

    int ch =(int)Kkey.charAt(i);  

    String s4 = Integer.toHexString(ch);  

    output = output+" 0x" +s4;

   }   //0x表示十六進制

     showProcess.append(output);

     for (int i=0;i<STR.length();i++)  

   {  

    int ch =(int)STR.charAt(i);  

    String s4 = Integer.toHexString(ch);  

    output = output+" 0x" +s4;

   }   //0x表示十六進制

     showProcess.append("\n經過輪密鑰加後變爲:"+output);

 

}

3.5密鑰擴展

通過生成器產生Nr+1個輪密鑰,每個輪密鑰由Nb個字組成,共有Nb(Nr+1)個字。在加密過程中,需要Nr+1個輪密鑰,需要構造4(Nr+1)個32位字。首先將輸入的4個字節直接複製到擴展密鑰數組的前4個字中,然後每次用4個字填充擴展密鑰數餘下的部分。

/*

 * 密鑰擴展

 */

public    byte[] Transform(char[]state, int Nb,int Nr,JTextArea showProcess)

{

            int round=1;

            int l=0;//計算輪數;

            showProcess.append("\n初始密鑰爲:");

            AddRoundKey(state,Nb,0,showProcess);

             for(;round<Nr;round++)

                {

                    l++;

                   SubChar(state,Nb,showProcess);//字節代替

                   ShiftRows(state,Nb,showProcess);//行移位

                   MixColumns(state,Nb,showProcess);//列混合

                    showProcess.append("\n"+l+"輪子密鑰爲:");

                   AddRoundKey(state,Nb,round*Nb,showProcess);//輪密鑰加

                }

                 SubChar(state,Nb,showProcess);

                   ShiftRows(state,Nb,showProcess);

                 AddRoundKey(state,Nb,round*Nb,showProcess);

                 return CharToByte(state);

}

public    byte[] ReTransform(char []state, int Nb,int Nr,JTextArea showProcess)

{

         int l=0;

         showProcess.append("\n初始密鑰爲:");

         AddRoundKey(state,Nb,Nr*Nb,showProcess);

         for(int round=Nr-1;round>=1;round--)

            {

                l++;

               InvShiftRows(state,Nb,showProcess);

               InvSubint(state,Nb,showProcess);

                showProcess.append("\n初始密鑰爲:");

               AddRoundKey(state,Nb,round*Nb,showProcess);

                showProcess.append("\n"+l+"輪子密鑰爲:");

               InvMixColumns(state,Nb,showProcess);

            }

               InvShiftRows(state,Nb,showProcess);

               InvSubint(state,Nb,showProcess);

               AddRoundKey(state,Nb,0,showProcess);

                return CharToByte(state);

}

 

3.6逆字節替換

與字節代替類似,逆字節代替基於逆S盒實現。

/*

 * 逆字節替換

 */

          

public   void InvSubint(char []state, int Nb,JTextArea showProcess)

{

    for(int i=0;i<4*Nb;i++)

          state[i] = S_BOX_1[state[i]%256];

}

3.7逆行移位

與行移位相反,逆行移位將態state的後三行按相反的方向進行移位操作,即第0行保持不變,第1行循環向右移一個字節,第2行循環向右移動兩個字節,第3行循環向右移動三個字節。

/*

 * 逆行移位

 */

public   void InvShiftRows(char[]state, int Nb,JTextArea showProcess)

{

    char [] t = new char[8];

         for( int r=0;r<4;r++)

         {

             for(int c=0;c<Nb;c++)

                 t[(c+r)%Nb] = state[r*Nb+c];

             for(int c=0;c<Nb;c++)

                 state[r*Nb+c]=t[c];

         }   

}

3.8逆列混合

逆列混淆的處理辦法與InvMixColumns()類似,每一列都通過與一個固定的多項式d(x)相乘進行交換。

/*

 * 逆列混合

 */

public    void  InvMixColumns(char[]state, int Nb,JTextArea showProcess)

{

    char  []t = new char[4];

    for( intc=0;c<Nb;c++)

    {

          for(intr=0;r<4;r++)t[r] = state[Nb*r+c];

       for(intr=0;r<4;r++)

       {

           state[Nb*r+c] = (char)(Ffmul(0x0e,t[r])^Ffmul(0x0b,t[(r+1)%4])

             ^Ffmul(0x0d,t[(r+2)%4])^Ffmul(0x09,t[(r+3)%4]));

       }

     }

}

 

3.9 加密

加密部分分了兩種情況,一種是自動檢查加密程序的正確性,之前在程序裏給明文和密鑰賦上初值,運行程序檢驗結果是否正確;另一種是用戶手動輸入32位的十六進制數,進行加密,我是把每一具體項模塊化,將功能在每個具體模塊中實現,只需要直接調用,視覺效果強,一目瞭然。

    下面是實現加密功能一些關鍵代碼

/*

 * 加密

 */

public longAES_Encrypt(String OpenPath,String SavePath,String m_Key,int Nb,intNk,JTextArea T)

throws IOException

{

     showProcess=T;

     showProcess.append("\n加密文件...");  

     //以二進制讀的方式打開要加密的文件;

     //以二進制寫的方式打開保存密文的文件;

     FileInputStream fp1 = newFileInputStream(OpenPath);

     FileOutputStream fp2 = newFileOutputStream(SavePath,true);

        intLength = fp1.available();//得到要加密的文件的長度,單位bit

     if(Length==0)return 0;

        int  leave = Length%(4*Nb);                         //求剩餘的字塊的字節數;

        longrounds = Length/(4*Nb);                       //得到整塊的加密輪數;

        if(leave!=0)rounds++;    

     longcopy_rounds = rounds;//rounds可以變化不影響copy_rounds的值

       showProcess.append("\n正在加密"+rounds+"個明文組");

     

        byte[]state = new byte[4*8];//作爲加密時存放要加密的明文塊,字節類型數組;

        byte[]copy = new byte[4*8];               //用來進行短塊處理時的緩存區;

        intNr=GetRounds(Nb,Nk);      //得到加密的輪數,Nb爲密鑰矩陣的列數;

       KeyExpansion(m_Key,Nb,Nk,Nr); //生成各輪子密鑰;

 

     if(copy_rounds==1&&rounds==1)

        {

         

         if(leave==0)fp1.read(state,0,4*Nb);//明文的長度恰好等於分組長度;

           else

           {

                fp1.read(state,0,leave);//明文的長度小於八個字符,每個字符一字節;

                 for(inti=leave;i<4*Nb;i++)

                state[i]=0;             //後面用空格補齊;

           }

 

        state = Transform(ByteToChar(state),Nb,Nr,showProcess);                   //加密變換; 

       fp2.write(state,0,4*Nb);//將加密後的密文塊寫入目標文件;

           rounds--;

        }

     else if(copy_rounds>1&&leave!=0)//如果明文的長度大於分組長度且字符數不是分組長度的整數倍             

        {                               //時,需要進行短塊處理;

                fp1.read(state,0,4*Nb);

                 state =Transform(ByteToChar(state),Nb,Nr,showProcess);//先加密最前面的一塊;

                fp2.write(state,0,leave);//將餘數個數那麼多的密文前面部分字符存入文件;

                    int j=0;

                    for(inti=leave;i<4*Nb;i++)

                    copy[j++]=state[i];//將最先加密的密文的後部分放在短塊前面,長度爲分組長度減去leave

                   fp1.read(copy,j,leave);//將剩餘個數那麼多的明文放在後面(是第二組明文塊中的明文),組成一組完整加密明文塊;

                    copy = Transform(ByteToChar(copy),Nb,Nr,showProcess);

                   fp2.write(copy,0,4*Nb);

                   rounds-=2;//這就加密了2組了,實質是一組多;

        }

      while(rounds>0)//以下處理的明文是分組的整數倍的情況;

        {         

                   fp1.read(state,0,4*Nb);

                    state =Transform(ByteToChar(state),Nb,Nr,showProcess);

                   fp2.write(state,0,4*Nb);

                   rounds--;

        }   

        fp1.close();//關閉源文件和目標文件;

        fp2.close();

        return((copy_rounds-1)*4*Nb+leave);//返回文件長度;

    }

3.10 解密

AES解密我也是分成了兩個部分,第一部分是在程序中對密文和密鑰賦初值,通過與標準對照檢查解密過程的正確性;第二部分是用戶手動輸入密文和密鑰,程序對其進行解密,得到最後的明文。

解密過程基本如下:

1)獲取輸入的明文和密鑰    

2)通過密鑰擴展過程獲取各輪密鑰           

3)輪密鑰加變換過程    

4)逆行移位   

5)逆字節替代 

6)輪密鑰加變換  

7)逆列混淆

4—7步共9次循環,最後一輪實現4—6步,完成解密過程。

主要代碼如下:

public     longAES_DeEncrypt(String OpenPath,String SavePath,String  m_Key, int Nb, intNk,JTextArea showProcess)

throws IOException

{                                                                          

       showProcess.append("\n解密文件...");

     //以二進制讀的方式打開要解密密的文件;

     //以二進制寫的方式打開保存解開的密文文件;  

     FileInputStream fp1= new FileInputStream(OpenPath);

     FileOutputStream fp2= new FileOutputStream(SavePath,true);

        intLength = fp1.available();//得到要解密的文件的長度;

     if(Length==0)return 0;

        int  leave=Length%(4*Nb);//求剩餘的字塊的字節數;

        longrounds=Length/(4*Nb);//得到整塊的解密輪數;

        if(leave!=0)rounds++;    

     longcopy_rounds=rounds;

     showProcess.append("\n正在解密"+rounds+"個密文組");

 

       

        byte[]state = new byte[4*8];//解密時存放密文塊;

        int Nr =GetRounds(Nb,Nk);      //得到解密時循環輪數;

       KeyExpansion(m_Key,Nb,Nk,Nr); //生成各輪子密鑰

        byte[]copy = new byte[32];

 

    if(leave!=0)//需要進行短塊處理

        {

        fp1.read(copy,0,leave);//先把餘數個密文字符保存進數組;

        fp1.read(state,0,4*Nb);//讀取緊接着的下一個密文塊;

        state = ReTransform(ByteToChar(state),Nb,Nr,showProcess);          //解密;

           int j=0;

           for(inti=leave;i<4*Nb;i++)        //把解密後的明文前部分和前面的餘數個合在一起組成一塊,

              copy[i]=state[j++];            //一起解密;

           copy =ReTransform(ByteToChar(copy),Nb,Nr,showProcess);

        //將解密後的明文寫入目標文件;

           fp2.write(copy,0,4*Nb);

           fp2.write(state,j,leave);//將餘數個明文寫入目標文件;

           rounds-=2;               //已經完成了兩輪解密所以減二;

        }

        while(rounds>0)//對後面是分組長度的整數倍的密文塊解密;

        {

           fp1.read(state,0,4*Nb);//讀取密文塊;

           copy =ReTransform(ByteToChar(state),Nb,Nr,showProcess);   //解密變換;

           fp2.write(copy,0,4*Nb);//將解密後的明文寫入目標文件;

           rounds--;                           //輪數減一;

        }

        fp1.close();//關閉源文件和目標文件;

        fp2.close();

     return((copy_rounds-1)*4*Nb+leave);//返回文件長度

    }

3.11效果截圖

 

                          圖1 軟件軟件效果圖

 

圖2 加密前的源文件數據:

 

 

圖3 加密過程顯示結果圖

圖4 加密過程顯示結果圖

 

 

圖 5 加密後效果圖:

 

 

4 結語

經過這次《信息安全技術》的課程學習和項目實踐設計,我個人得到了不少的收穫,一方面加深了我對課本理論的認識,另一方面也提高了實踐操作能力。

我的此次課程作業中我主要關於編寫一個加密解密程序,這也使得我更加紮實的掌握了有關於信息安全密碼學的相關方面的知識。在設計過程中雖然遇到了一些困難,但經過一次又一次的思考,一遍又一遍的檢查終於找出了問題所在,也暴露出了前期我在這方面的知識欠缺和經驗不足。實踐出真知,通過親自動手編寫開發,使我們掌握的知識得以實踐。

在課程作業過程中,我們不斷髮現錯誤,不斷改正,不斷領悟,不斷獲取。在設計過程中遇到了很多問題,最後在不懈的努力下,終於迎刃而解。在今後社會的發展和學習實踐過程中,一定要不懈努力,不能遇到問題就想到要退縮,一定要不厭其煩的發現問題所在,然後一一進行解決,只有這樣,才能成功的做成想做的事,才能在今後的道路上劈荊斬棘,而不是知難而退,那樣永遠不可能收穫成功,收穫喜悅,也永遠不可能得到社會及他人對你的認可!

個人認爲,在這本次的的課程作業中,不僅培養了獨立思考、動手操作的能力,在各種其它能力上也都有了提高。更重要的是,在課程作業過程中,我們學會了很多學習的方法。而這是日後最實用的,真的是受益匪淺。要面對社會的挑戰,只有不斷的學習、實踐,再學習、再實踐。這對於我們的將來也有很大的幫助。以後,不管有多苦,我想我們都能變苦爲樂,找尋有趣的事情,發現其中珍貴的事情。回顧起此課程作業,從理論到實踐,可以說得是苦多於甜,但是可以學到很多很多的東西,同時不僅可以鞏固了以前所學過的知識,而且學到了很多在書本上所沒有學到過的知識。通過這次課程作業使我懂得了理論與實際相結合是很重要的,只有理論知識是遠遠不夠的,只有把所學的理論知識與實踐相結合起來,從理論中得出結論,才能真正爲社會服務,從而提高自己的實際動手能力和獨立思考的能力。

 總之,認真對待每一個學習的機會,珍惜過程中的每一分一秒,學到最多的知識和方法,鍛鍊自己的能力,這個是我在本次信息安全技術課程作業中學到的最重要的、受用終身的知識!

 

 代碼附件:http://download.csdn.net/detail/zhoujn90/8369289 下載

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章