什麼是AES算法

概述

加密算法分爲單向加密和雙向加密。
單向加密包括MD5SHA等摘要算法。單向加密算法是不可逆的,也就是無法將加密後的數據恢復成原始數據,除非採取碰撞攻擊和窮舉的方式。像是銀行賬戶密碼的存儲,一般採用的就是單向加密的方式。

雙向加密是可逆的,存在密文的密鑰,持有密文的一方可以根據密鑰解密得到原始明文,一般用於發送方和接收方都能通過密鑰獲取明文的情況。雙向加密包括對稱加密和非對稱加密。對稱加密包括DES加密,AES加密等,非對稱加密包括RSA加密,ECC加密。
AES算法全稱Advanced Encryption Standard,是DES算法的替代者,也是當今最流行的對稱加密算法之一。

AES基本概念

要想學習AES算法,首先要弄清楚三個基本的概念:密鑰、填充、模式。

密鑰

密鑰是AES算法實現加密和解密的根本。對稱加密算法之所以對稱,是因爲這類算法對明文的加密和解密需要使用同一個密鑰。

AES支持三種長度的密鑰:

128位,192位,256位

平時大家所說的AES128,AES192,AES256,實際上就是指的AES算法對不同長度密鑰的使用。從安全性來看,AES256安全性最高。從性能來看,AES128性能最高。本質原因是它們的加密處理輪數不同。

填充

要想了解填充的概念,我們先要了解AES的分組加密特性。AES算法在對明文加密的時候,並不是把整個明文一股腦加密成一整段密文,而是把明文拆分成一個個獨立的明文塊,每一個明文塊長度128bit。

這些明文塊經過AES加密器的複雜處理,生成一個個獨立的密文塊,這些密文塊拼接在一起,就是最終的AES加密結果。

但是這裏涉及到一個問題:

假如一段明文長度是192bit,如果按每128bit一個明文塊來拆分的話,第二個明文塊只有64bit,不足128bit。這時候怎麼辦呢?就需要對明文塊進行填充(Padding)。AES在不同的語言實現中有許多不同的填充算法,我們只舉出集中典型的填充來介紹一下。

  • NoPadding:

不做任何填充,但是要求明文必須是16字節的整數倍。

  • PKCS5Padding(默認):

如果明文塊少於16個字節(128bit),在明文塊末尾補足相應數量的字符,且每個字節的值等於缺少的字符數。

比如明文:{1,2,3,4,5,a,b,c,d,e},缺少6個字節,則補全爲{1,2,3,4,5,a,b,c,d,e,6,6,6,6,6,6}

  • ISO10126Padding:

如果明文塊少於16個字節(128bit),在明文塊末尾補足相應數量的字節,最後一個字符值等於缺少的字符數,其他字符填充隨機數。

比如明文:{1,2,3,4,5,a,b,c,d,e},缺少6個字節,則可能補全爲{1,2,3,4,5,a,b,c,d,e,5,c,3,G,$,6}

需要注意的是,如果在AES加密的時候使用了某一種填充方式,解密的時候也必須採用同樣的填充方式。

模式

AES的工作模式,體現在把明文塊加密成密文塊的處理過程中。AES加密算法提供了五種不同的工作模式:

ECB、CBC、CTR、CFB、OFB

模式之間的主題思想是近似的,在處理細節上有一些差別。我們這一期只介紹各個模式的基本定義。

  • ECB模式(默認):

電碼本模式 Electronic Codebook Book

  • CBC模式:

密碼分組鏈接模式 CipherBlock Chaining

  • CTR模式:

計算器模式 Counter

  • CFB模式:

密碼反饋模式 CipherFeedBack

  • OFB模式:

輸出反饋模式 OutputFeedBack

如果在AES加密的時候使用了某一種工作模式,解密的時候也必須採用同樣的工作模式。

AES算法原理

                                                      AES加密算法的流程

AES加密主要包括兩個步驟:密鑰擴展明文加密

密鑰擴展:將輸入的密鑰(16字節、24字節和32字節)進行擴展,根據密鑰長度的不同,得到擴展後的密鑰進行加密的輪數也不相同。

                                                    密鑰擴展算法

密鑰擴展過程說明(密鑰爲16字節):

  1. 將初始密鑰以列爲主,轉化爲4個32 bits的字,分別記爲w[0…3];
  2. 按照如下方式,依次求解w[j],其中j是整數並且屬於[4,43];
  3. 若j%4=0,則w[j]=w[j-4]⊕g(w[j-1]),否則w[j]=w[j-4]⊕w[j-1];

函數g的流程說明:

  • 將w循環左移一個字節;
  • 分別對每個字節按S盒進行映射;
  • 與32 bits的輪常量Rcon[j]進行異或。

輪常量(Rcon)是一個字,最右邊三個字節總爲0。因此字與Rcon相異或,其結果只是與該字最左的那個字節相異或。每輪的輪常量不同,定位爲Rcon[j] = (RC[j], 0, 0, 0)。(RC是一維數組)
RC生成函數:RC[1] = 1, RC[j] = 2 * RC[j – 1]。
因爲16字節密鑰的只進行10輪的擴展,所以最後生成的RC[j]的值按16進製表示爲:

                                                                 RC值

十輪的密鑰擴展後,就能生成44個字大小的擴展密鑰。擴展後的密鑰將用於AES對明文的加密過程。

明文加密:無論是AES的加密和解密過程,都涉及到四個主要的步驟:字節代替、行移位、列混淆和輪密鑰加。以下對這四個過程進行詳細說明。

  • 字節代替:將輸入狀態的每個字節使用S盒上對應的字節進行替換.
    輸入狀態:是一個4×4的數組,數組內每個元素由輸入的明文分組組成,按照列進行排序,比如輸入的明文數據爲193de3bea0f4e22b9ac68d2ae9f84808,則輸入狀態爲

                                                                   輸入狀態矩陣

S盒是16×16個字節組成的矩陣,行列的索引值分別從0開始,到十六進制的F結束,每個字節的範圍爲(00-FF)。

                                                                      S盒

進行字節代替的時候,把狀態中的每個字節分爲高4位和低4位。高4位作爲行值,低4位作爲列值,以這些行列值作爲索引從S盒的對應位置取出元素作爲輸出,如下圖所示:

                                                                     S盒替換

S盒的構造方式如下:
(1) 按字節值得升序逐行初始化S盒。在行y列x的字節值是{yx}。
(2) 把S盒中的每個字節映射爲它在有限域GF中的逆;{00}映射爲它自身{00}。
(3) 把S盒中的每個字節的8個構成位記爲(b7, b6, b5, b4, b3, b2, b1)。對S盒的每個字節的每個位做如下的變換:

                                                                S-box盒生成公式

ci指的是值爲{63}的字節c的第i位。
解密過程逆字節代替使用的是逆S盒,構造方式爲

                                                              逆S-box盒生成公式

字節d={05}。

  • 行移位:狀態的第一行保持不變。第二行循環左移一個字節,第三行左移兩個字節,第四行循環左移三個字節。

                                                               正向行移位

逆向行移位將狀態中後三行執行相反方向的移位操作,如第二行向右循環移動一個字節,其他行類似。

  • 列混淆:將每列中的每個字節映射爲一個新值,新值由該列中的4個字節通過函數變換得到。

                                                               正向列混淆

要注意,圖示的矩陣的乘法和加法都是定義在GF(2^8)上的。
逆向列混淆原理如下:

                                                                  逆向列混淆

  • 輪密鑰加(AddRoundKey):將當前分組和擴展密鑰的一部分進行按位異或。
    擴展密鑰通過擴展密鑰算法已經求出來了,所以這一步的步驟就是將分組與一部分擴展密鑰按位異或操作。

                                                            輪密鑰加

輪密鑰加後的分組再進行一次輪密鑰加就能恢復原值.所以,只要經過密鑰擴展和明文加密,就能將明文加密成密文,進行解密的時候,只需要進行逆向變換即可。

圖[AES加密算法的流程]中還需要注意,明文輸入到輸入狀態後,需要進行一輪的輪密鑰加,對輸入狀態進行初始化。前9輪的加密過程,都需要進行字節替代、行移位、列混淆和輪密鑰加,但是第10輪則不再需要進行列混淆。

進行解密的時候,需要進行逆向字節替代,逆向行移位、逆向列混淆和輪密鑰加。

AES算法的Java代碼

  • 加密

 

    /**
     * AES加密字符串
     * 
     * @param content
     *            需要被加密的字符串
     * @param password
     *            加密需要的密碼
     * @return 密文
     */
    public static byte[] encrypt(String content, String password) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");// 創建AES的Key生產者

            kgen.init(128, new SecureRandom(password.getBytes()));// 利用用戶密碼作爲隨機數初始化出
                                                                    // 128位的key生產者
            //加密沒關係,SecureRandom是生成安全隨機數序列,password.getBytes()是種子,只要種子相同,序列就一樣,所以解密只要有password就行

            SecretKey secretKey = kgen.generateKey();// 根據用戶密碼,生成一個密鑰

            byte[] enCodeFormat = secretKey.getEncoded();// 返回基本編碼格式的密鑰,如果此密鑰不支持編碼,則返回
                                                            // null。

            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 轉換爲AES專用密鑰

            Cipher cipher = Cipher.getInstance("AES");// 創建密碼器

            byte[] byteContent = content.getBytes("utf-8");

            cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化爲加密模式的密碼器

            byte[] result = cipher.doFinal(byteContent);// 加密

            return result;

        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return null;
    }
  • 解密

 

 /**
     * 解密AES加密過的字符串
     * 
     * @param content
     *            AES加密過過的內容
     * @param password
     *            加密時的密碼
     * @return 明文
     */
    public static byte[] decrypt(byte[] content, String password) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");// 創建AES的Key生產者
            kgen.init(128, new SecureRandom(password.getBytes()));
            SecretKey secretKey = kgen.generateKey();// 根據用戶密碼,生成一個密鑰
            byte[] enCodeFormat = secretKey.getEncoded();// 返回基本編碼格式的密鑰
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 轉換爲AES專用密鑰
            Cipher cipher = Cipher.getInstance("AES");// 創建密碼器
            cipher.init(Cipher.DECRYPT_MODE, key);// 初始化爲解密模式的密碼器
            byte[] result = cipher.doFinal(content);  
            return result; // 明文   
            
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return null;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章