AES加密 — 详解
轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/120285594
本文出自【趙彥軍的博客】
文章目錄
- 推薦
- AES 簡介
- 對稱加密
- 加密模式
- 填充模式
- 常見填充模式
- PKCS5Padding到底是什么?
- 偏移量
- 字符集
- 實際工作中的加密流程
- AES 加密/解密 注意的問題
- 實戰
- AES加解密
- AES默認實現類
- AES隨機加密
推薦
AES加密 — 詳解
RSA 加密 — 詳解
AES 簡介
- DES 全稱為Data Encryption Standard,即數據加密標準,是一種使用密鑰加密的塊算法,1977年被美國聯邦政府的國家標準局確定為聯邦資料處理標準(FIPS)
- AES 密碼學中的高級加密標準(Advanced Encryption Standard,AES),又稱Rijndael加密法,是美國聯邦政府采用的一種區塊加密標準。這個標準用來替代原先的DES(Data Encryption Standard),已經被多方分析且廣為全世界所使用。
為什么 DES 被廢棄?
我們知道數據加密標準(Data Encryption Standard: DES)的密鑰長度是56比特,因此算法的理論安全強度是2的56次方。但二十世紀中后期正是計算機飛速發展的階段,元器件制造工藝的進步使得計算機的處理能力越來越強,DES將不能提供足夠的安全性。
簡單來說,DES標準的秘鑰長度要求太短,安全性不夠。
為什么AES算法被稱為 Rijndael 算法?
1997年1月2號,美國國家標準技術研究所(National Institute of Standards and Technology: NIST)宣布希望征集高級加密標準(Advanced Encryption Standard: AES)[3],用以取代DES。AES得到了全世界很多密碼工作者的響應,先后有很多人提交了自己設計的算法。最終有5個候選算法進入最后一輪:Rijndael,Serpent,Twofish,RC6和MARS,下圖分別為其中的5位作者。最終經過安全性分析、軟硬件性能評估等嚴格的步驟,Rijndael算法獲勝。
為什么AES算法安全性高?
AES的區塊長度固定為128位,密鑰長度則可以是128 bit,192 bit 或256位 bit 。換算成字節長度,就是密碼必須是 16個字節,24個字節,32個字節。AES密碼的長度更長了,破解難度就增大了,所以就更安全。
對稱加密
- 對稱加密 : 也就是加密秘鑰和解密秘鑰是一樣的。
- 非對稱加密 : 也就是加密秘鑰和解密秘鑰是不一樣的。
AES 是對稱加密算法,優點:加密速度快;缺點:如果秘鑰丟失,就容易解密密文,安全性相對比較差
RSA 是非對稱加密算法 , 優點:安全 ;缺點:加密速度慢
AES加密需要:明文 + 密鑰+ 偏移量(IV)+密碼模式(算法/模式/填充)
AES解密需要:密文 + 密鑰+ 偏移量(IV)+密碼模式(算法/模式/填充)
AES的算法模式一般為 AES/CBC/PKCS5Padding
加密模式
AES的加密模式有以下幾種
- 電碼本模式(ECB)
- 密碼分組鏈接模式(CBC)
- 計算器模式(CTR)
- 密碼反饋模式(CFB)
- 輸出反饋模式(OFB)
密碼分組鏈接模式(CBC):將整段明文切成若干小段,然后每一小段與初始塊或者上一段的密文段進行異或運算后,再與密鑰進行加密。
電碼本模式 ECB (Electronic codebook,ECB):需要加密的消息按照塊密碼的塊大小被分為數個塊,并對每個塊進行獨立加密。
根據圖示,在 CBC 模式下,使用 AES 加解密方式進行分組加解密時,需要用到的兩個參數
-
1、初始化向量,也就是偏移量
-
2、加解密秘鑰
填充模式
如電子密碼本(ECB)和密文塊鏈接(CBC)。 為對稱密鑰加密設計的塊密碼工作模式要求輸入明文長度必須是塊長度的整數倍,因此信息必須填充至滿足要求。
常見填充模式
| AES/CBC/NoPadding | 16 | 不支持 |
| AES/CBC/PKCS5Padding | 32 | 16 |
| AES/CBC/ISO10126Padding | 32 | 16 |
| AES/CFB/NoPadding | 16 | 原始數據長度 |
| AES/CFB/PKCS5Padding | 32 | 16 |
| AES/CFB/ISO10126Padding | 32 | 16 |
| AES/ECB/NoPadding | 16 | 不支持 |
| AES/ECB/PKCS5Padding | 32 | 16 |
| AES/ECB/ISO10126Padding | 32 | 16 |
| AES/OFB/NoPadding | 16 | 不支持 |
| AES/OFB/PKCS5Padding | 32 | 16 |
| AES/OFB/ISO10126Padding | 32 | 16 |
| AES/PCBC/NoPadding | 16 | 不支持 |
| AES/PCBC/PKCS5Padding | 32 | 16 |
| AES/PCBC/ISO10126Padding | 32 | 16 |
PKCS5Padding到底是什么?
為什么 JAVA 里指定算法時,寫的是 AES/CBC/PKCS5Padding,每個都是什么含義,又有什么作用。
-
AES,加解密算法
-
CBC,數據分組模式
-
PKCS5Padding,數據按照一定的大小進行分組,最后分剩下那一組,不夠長度,就需要進行補齊, 也可以叫 補齊模式
簡單的說:拿到一個原始數據以后,首先需要對數據進行分組,分組以后如果長度不滿足分組條件,需要進行補齊,最后形成多個分組,在使用加解密算法,對這多個分組進行加解密。所以這個過程中,AES,CBC,PKCS5Padding 缺一不可。
在對數據進行加解密時,通常將數據按照固定的大小(block size)分成多個組,那么隨之就產生了一個問題,如果分到最后一組,不夠一個 block size 了,要怎么辦?此時就需要進行補齊操作。
補齊規則:The value of each added byte is the number of bytes that are added, i.e. N bytes, each of value N are added.
舉例:
36 位的 UUID,如果按照 block size=16 字節(即 128 比特),那么就需要補齊到 48 位,差 12 個字節。那么最后填充的 12 個字節的內容,都是字節表示的 0x0c(即 12)。
偏移量
偏移量 的添加一般是為了增加 AES 加密的復雜度,增加數據的安全性。一般在 AES_256 中會使用到 偏移量 ,而在 AES_128 加密中不會使用到。
字符集
在 AES 加密中,特別也要注意到字符集的問題。一般用到的字符集是 utf-8 和 gbk 。
實際工作中的加密流程
在實際的工作中,客戶端跟服務器交互一般都是字符串格式,所以一個比較好的加密流程是:
- 加密流程 :明文通過 密鑰 (有時也需要 偏移量 ),利用 AES 加密算法,然后通過 Base64 轉碼,最后生成加密后的字符串。
- 解密流程 :加密后的字符串通過 密鑰 (有時也需要 偏移量 ),利用 AES 解密算法,然后通過 Base64 轉碼,最后生成解密后的字符串。
AES 加密/解密 注意的問題
AES 加密/解密的時候,通常是用在服務端和客戶端通訊的過程中,一端加密傳輸,另一端解密使用。雖然 AES 加密看似簡單,但在使用過程過程中,仍然會出現在一端加密ok,但是另一端解密失敗的情況。一旦出現 AES 解密失敗,我們可以通過以下幾個方面進行排查:
1. AES 加密/解密 使用相同的密鑰 2. 若涉及到偏移量,則AES 加密/解密 使用的偏移量要一樣 3. AES 加密/解密 要使用相同加密數位,如都使用`AES_256` 4. AES 加密/解密 使用相同的字符集 5. AES 加密/解密 使用相同的加密,填充模式,如都使用`AES/CBC/PKCS5Padding`模式 6. 由于不同開發語言(如C 和 Java)及不同開發環境(如 Java 和 Android)的影響,可能相同的加解密算法在實現上出現差異,若你們注意到這個差異,就可能導致加解密出現問題最后,當我們需要驗證自己的 AES 解密算法是否與別人的加密方法為一套的時候??梢宰尲用芊桨l你一份加密后的密文和加密前的明文,然后你用密文解密,看解密結果和加密方發你的是否一致。需要注意的是,加密方給你的明文要盡量簡潔,如就 中國 二字,這樣既能看出加密方和解密方的字符集是否一致,而且能避免復制粘貼等環節出現空格,回車等轉義字符對驗證結果的干擾。
實戰
AES加解密
首先定義加密、解密工具類
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.IOException; import java.security.GeneralSecurityException;/*** AES加解密工具類*/ public class AES {/*** AES加密** @param key* @param iv* @throws GeneralSecurityException* @throws IOException*/public static byte[] encryptAes(byte[] data, byte[] key, byte[] iv)throws GeneralSecurityException, IOException {Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));return cipher.doFinal(data);}/*** AES解密** @param key* @param iv* @return* @throws GeneralSecurityException* @throws IOException*/public static byte[] decryptAesToByteString(byte[] data, byte[] key, byte[] iv)throws GeneralSecurityException, IOException {Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));return cipher.doFinal(data);} }加密和解密的代碼很像,唯一的不同點是,加密 Cipher.ENCRYPT_MODE , 解密用的是 Cipher.DECRYPT_MODE
下面我們寫一個測試代碼:
try {//加密密碼String key = "zhaoyanjunzhaoy1";//偏移量String iv = "1234567890123456";String message = "今天是周二,我好開心";//加密byte[] encryResult = AES.encryptAes(message.getBytes(), key.getBytes(), iv.getBytes());//解密byte[] decryResult = AES.decryptAesToByteString(encryResult, key.getBytes(), iv.getBytes());System.out.println("解密數據 = " + new String(decryResult));} catch (IOException | GeneralSecurityException e) {e.printStackTrace();}輸出結果:
解密數據 = 今天是周二,我好開心可以看到數據已經正常解密了。
AES默認實現類
不帶模式和填充來獲取AES算法的時候,其默認使用 AES/ECB/PKCS5Padding(輸入可以不是16字節,也不需要填充向量), 所以不需要偏移量參數
Cipher cipher = Cipher.getInstance("AES");我下面封裝一個工具類
import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.io.IOException; import java.security.GeneralSecurityException;/*** AES加解密工具類*/ public class AES {/*** AES加密** @param key* @throws GeneralSecurityException*/public static byte[] encryptAes(byte[] data, byte[] key) throws GeneralSecurityException {Cipher cipher = Cipher.getInstance("AES");cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));return cipher.doFinal(data);}/*** AES解密** @param key* @return* @throws GeneralSecurityException* @throws IOException*/public static byte[] decryptAesToByteString(byte[] data, byte[] key)throws GeneralSecurityException, IOException {Cipher cipher = Cipher.getInstance("AES");cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"));return cipher.doFinal(data);} }測試代碼
public class T2 {public static void main(String[] args) {try {//加密密碼String key = "zhaoyanjunzhaoy1";//加密正文String message = "今天是周二,我好開心";//加密byte[] encryResult = AES.encryptAes(message.getBytes(), key.getBytes());//解密byte[] decryResult = AES.decryptAesToByteString(encryResult, key.getBytes());System.out.println("解密數據 = " + new String(decryResult));} catch (IOException | GeneralSecurityException e) {e.printStackTrace();}} }測試結果
解密數據 = 今天是周二,我好開心AES隨機加密
在上面的例子中,我們在 AES 加密中,需要指定規定長度的密碼,偏移量。在 Java 中還給我們提供了 KeyGenerator 類來隨機生成一個密碼和偏移量,解決了我們動腦想密碼的問題。
我們來看看隨機加密怎么用。
/*** AES加密/解密** @throws GeneralSecurityException*/public void encryptAes() throws GeneralSecurityException {//原始數據String message = "今天是周四,好開心哦";byte[] data = message.getBytes();//指定加密類型KeyGenerator keygen = KeyGenerator.getInstance("AES");//指定秘鑰長度keygen.init(256);SecretKey secretKey = keygen.generateKey();Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");cipher.init(Cipher.ENCRYPT_MODE, secretKey);//獲取秘鑰byte[] key = secretKey.getEncoded();//獲取偏移量byte[] iv = cipher.getIV();//解密數據byte[] ciphertext = cipher.doFinal(data);//解密數據cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));byte[] decryMessage = cipher.doFinal(ciphertext);System.out.println("解密數據 = " + new String(decryMessage));}這種加密 key , iv 都是隨機產生的,每次加密后的密文都不一樣,適合一定的特殊場景。
隨機是怎么發生的,我們就看
cipher.init(Cipher.ENCRYPT_MODE, secretKey);在 init 方法的時候,JceSecurity.RANDOM 產生隨機數,源碼如下:
public final void init(int opmode, Key key) throws InvalidKeyException {init(opmode, key, JceSecurity.RANDOM);}具體細節就不看了,知道原理就行。
總結
以上是生活随笔為你收集整理的AES加密 — 详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java Okio-更加高效易用的IO库
- 下一篇: Java CountDownLatch的