咱們在上一篇《詳細解析DES系列加密技術(一)》中提到說DES在1999年1月被破解,而且有分析報告提出DES算法在理論上存在的一些漏洞,另外,2001年,DES做爲一個標準已經被取代了.一旦一種加密技術被破解,那麼,被取代也就是必然的事情了,對於DES來講,取代他的又是誰呢?今天咱們來討論一下DES的後輩,也就是3DES和AES.
3DES(triple-DES)是爲了增長DES的強度,將DES重複3次所獲得的一種密碼算法,也被稱爲TDEA(Triple Data Encryption Algorithm),縮寫爲3DES,其過程以下圖所示:
java
明文通過3次DES處理才能變成最後的密文,因爲DES密鑰的長度實質上是56bit,所以3DES的密鑰長度就是168bit.
雖然3DES是將DES重複了3次,可是透過上圖就可以發現3DES並非進行了三次DES的加密(加密—加密—加密),而是加密—解密—加密.這個是由IMB設計出來的,目的是爲了兼容普通的DES,好比說當3DES的三個密鑰都相同時,3DES也就至關因而普通的DES了.也就是說3DES具備向下兼容性.
在上一篇博文中咱們談到DES時說,DES的加密和解密過程只是改變了子密鑰的順序,而實際上處理都是相同的.因此說,若是全部密鑰都使用相同的比特序列,那麼其結果與普通的DES就是等價的.
咱們看剛剛那幅圖,若是密鑰1和密鑰3使用相同的密鑰,而密鑰2使用不一樣的密鑰(也就是隻使用兩個DES密鑰),這種三重DES就成爲DES-EDE2.EDE表示的是加密(Encryption)—解密(Decryption)—加密(Encryption),以下圖所示:算法
3DES的解密過程與加密過程正好相反,是以密鑰3,密鑰2,密鑰1的順序執行解密—加密—解密的操做.如下是Java實現3DES加/解密的過程:
附帶代碼1.java
package org.shangzeng.cipher;數組
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;安全
public class ThreeDESTest {網絡
public static void main(String[] args) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { String content="Hello,ThreeDES Test!"; String key="shangzengxueyuan2018"; byte[] encryptResult=encryThreeDES(content,key); String result=convertByteToHexString(encryptResult); System.out.println("3DES加密後爲:"+result); byte[] decryptResult=decryptThreeDES(encryptResult,key); System.out.println("3DES解密後的內容爲:"+new String(decryptResult)); } /*** * 將byte數組轉換成16進制輸出 */ public static String convertByteToHexString(byte[] byteArray){ String result=""; for (int i = 0; i < byteArray.length; i++) { int temp=byteArray[i]& 0xff; String tempHex= Integer.toHexString(temp); if(tempHex.length()<2){ result+="0"+tempHex; }else{ result+=tempHex; } } return result; } public static byte[] get3DESKey(String pass){ byte[] key=new byte[24];//3DES裏的密鑰key爲24位 byte[] temp; try { temp=pass.getBytes("UTF-8");//將字符串轉成字節數組 if(key.length>temp.length){ //若是temp不夠24位,則拷貝temp數組整個長度的內容到key數組中 System.arraycopy(temp,0,key,0,temp.length); }else{ //若是temp大於24位,則拷貝temp24個長度的內容到key數組中 System.arraycopy(temp,0,key,0,key.length); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return key; } private static final String ALGORITHM="DESede"; public static byte[] encryThreeDES(String message,String key) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { return threeDES(message.getBytes(),key,Cipher.ENCRYPT_MODE); } public static byte[] decryptThreeDES(byte[] contentArray,String key) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { return threeDES(contentArray,key,Cipher.DECRYPT_MODE); } private static byte[] threeDES(byte[] contentArray,String key,int mode) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { SecretKey desKey=new SecretKeySpec(get3DESKey(key), ALGORITHM); Cipher cipher= null; cipher = Cipher.getInstance(ALGORITHM); cipher.init(mode,desKey); return cipher.doFinal(contentArray); }
}dom
3DES雖然比DES要安全一些,可是他的處理速度不高.因此除了特別重視向下兼容性的狀況之外,不多被用於新的用途.接下來咱們來談談AES.
AES(Advanced Encryption Standard)也就是高級加密標準,他是爲取代DES而成爲新標準的一種對稱加密算法.談到這裏咱們發現,AES和DES同樣,只是加密的標準,而不負責具體的加密算法,DES是由Feistel實現的,而AES的實現,則由NIST(National Institute of Standards and Technology,國家標準技術研究所)組織一個公開競選活動去競選.這個研究所選拔的密碼算法,會成爲美國的國家標準,也就是咱們在上一篇文章中提到的FIPS.由於參加這個競選的條件是:被選擇AES的密碼算法必須無條件地免費供全世界使用,因此AES雖然是美國的標準,卻也和DES同樣,成爲了世界性的標準.
AES競選的參與者還必須提交密碼算法的詳細規格書、以ANSI C和Java編寫的實現代碼以及抗密碼破譯強度的評估等材料.因此,參加者所提交的密碼算法,必須在詳細設計和程序代碼徹底公開的狀況下,依然保證較高的強度,這樣呢,就杜絕了隱蔽式安全性.
AES競選的活動雖然是由NIST組織的,但密碼算法的評審卻不是由NIST完成的,這個評審是由全世界的企業和密碼學家共同完成的.因此參加者都會努力從各個角度尋找其餘密碼算法的弱點,並向其餘參與評審的人進行證實.這種方式就是經過競爭來實現標準化.
在前面咱們提到過,3DES雖然從安全性上來講優於DES,可是,他的處理速度並不高.所以,咱們必需要考慮實現AES的算法的處理速度,除此以外,算法自己是否存在弱點、實現的容易性,密鑰準備的速度,可否在各類平臺,好比智能卡,8位CPU等低性能平臺以及工做站等高性能平臺上有效工做,這些均是AES選拔參與者須要考慮的範圍.
從1997年,NIST開始募集,到1999年,15個算法有5個算法入圍,最終2000年10月2日,Rijndael力壓羣雄,被NIST選定爲AES標準.Rijndael是由比利時密碼學家Joan Daemen和Vincent Rijmen設計的分組密碼算法.不知道Rijndal是否和這兩位密碼學家的名字有關係.
下面咱們來詳細說明一下Rijndael的加密和解密:
首先Rijndael的分組長度和密鑰長度能夠分別以32bit爲單位在128bit到256bit的範圍內進行選擇.不過,在AES的規格中,分組長度固定位128bit,密鑰長度只有128,192和256三種.
和DES同樣,Rijndael算法也是由多個輪構成的,只不過,DES使用Feistel網絡做爲其基本結構,而Rijndael則是使用了SPN結構.其一個單位的加解密過程以下圖所示:
ide
如今咱們對這四種處理進行詳細解析:
經過上圖咱們知道Rijndael的單位輸入分組爲128bit,也就是16字節.首先須要對這16個字節進行SubBytes處理.所謂SubBytes處理能夠簡單理解爲輸入的每一個字節的值都在另外一張替換表中擁有其對應的值,而後按着這種對應關係進行一一替換.以下圖所示:
性能
在SubBytes以後,就是ShiftRows處理,這一步是將以4字節爲一個單位的行按着必定的規則向左平移,而且每一行平移的字節數不相同,以下圖所示:
加密
以後咱們開始進行第三步處理,即MixColumns處理,這一步仍然是對以4字節爲單位的值進行比特運算,將其變爲另一個4字節值,相似於第一步的SubBytes,以下圖所示:
設計
最後咱們要將MixColumns的輸出與輪密鑰進行XOR,即AddRoundKey處理,到這裏,Rijndael的一輪就結束了.
由於在Rijndael中全部輸入的bit都會在一輪中進行加密,所以與Feistel網絡相比,加密所須要的輪數就減小了.而且以上這四種方式能夠並行進行.至於解密過程,除了最後一步AddRoundKey與加密時相同以外,其餘三種處理都是與原步驟相對應的的逆運算.如下是AES的Java實現:
附加代碼2.javapackage org.shangzeng.testsignature;
import javax.crypto.*;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
public class AESTest {
private static String convertByteToHexString(byte[] byteArray){ String result=""; for (int i = 0; i < byteArray.length; i++) { int temp=byteArray[i]& 0xff; String tempHex= Integer.toHexString(temp); if(tempHex.length()<2){ result+="0"+tempHex; }else{ result+=tempHex; } } return result; } static final String ALGORITHM="AES"; static SecretKey secretKey=null; public static SecretKey generateKey() throws NoSuchAlgorithmException { if(null==secretKey){ KeyGenerator keyGenerator=KeyGenerator.getInstance(ALGORITHM); SecureRandom secureRandom=new SecureRandom(); keyGenerator.init(secureRandom); secretKey=keyGenerator.generateKey(); } return secretKey; } public static byte[] encrypt(String content) throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchPaddingException { SecretKey secretKey=generateKey(); return aes(Cipher.ENCRYPT_MODE,secretKey,content.getBytes(Charset.forName("UTF-8"))); } public static String decrypt(byte[] contentArray) throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchPaddingException, UnsupportedEncodingException { SecretKey secretKey=generateKey(); byte[] resultByte= aes(Cipher.DECRYPT_MODE,secretKey,contentArray); return new String(resultByte,"UTF-8"); } private static byte[] aes(int mode,Key key,byte[] contentArray) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher=Cipher.getInstance(ALGORITHM); cipher.init(mode,key); byte[] result=cipher.doFinal(contentArray); return result; } public static void main(String[] args) throws InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, NoSuchPaddingException, UnsupportedEncodingException { String content="你好,我是熵熷學院Jacob老師."; byte[] encryptResult=encrypt(content); String resultByHex=convertByteToHexString(encryptResult); String resultByUnicode=new String(encryptResult,"UTF-8"); System.out.println("加密後的結果16進製表示爲:"+resultByHex+"\n 加密後的結果轉換成字符串爲:"+resultByUnicode); String decryptResult=decrypt(encryptResult); System.out.println("解密後的結果爲:"+decryptResult); }
}
對於Rijndael來講,由於它具備很是嚴謹的數學結構,也就是說從明文到密文的計算過程能夠所有用公式來表達,這是以前其餘算法所不具有的性質,因此,嚴謹的數學結構這個性質也就意味着Rijndael可以經過數學方法進行破譯,固然這目前只是一種假設.目前來看,尚未出現針對Rijndael的有效×××,所以以Rijndael爲實現的AES仍是比較安全的.