AES对称加密
前言
對稱加密(Symmetric Cryptography)
非對稱加密(Asymmetric Cryptography)
相關概念
分組:用來執行加密程序的內存空間有限。
密鑰長度:128 bits、192 bits或256 bits。密鑰的長度不同,推薦加密輪數也不同。
初始向量(IV):防止同樣的明文塊,始終加密成同樣的密文塊
填充方式:密鑰只能對確定長度的數據塊進行處理,所以需要對最后一塊做額外處理,常用的模式有PKCS5, PKCS7, NOPADDING
要想學習AES,首先要清楚三個基本的概念:密鑰、填充、模式。
秘鑰
密鑰是AES算法實現加密和解密的根本
- 對稱加密算法之所以對稱,是因為這類算法對明文的加密和解密需要使用同一個密鑰。
AES支持三種長度的密鑰: 128位,192位,256位
- 平時大家所說的AES128,AES192,AES256,實際上就是指AES算法對不同長度密鑰的使用。
三種密鑰的區別:從安全性來看,AES256安全性最高。從性能看,AES128性能最高。本質原因是它們的加密處理輪數不同。
填充
要想了解填充的概念,我們先要了解AES的分組加密特性。
什么是分組加密?
如上圖所示,AES算法在對明文加密的時候,并不是把整個明文一股腦的加密成一整段密文,而是把明文拆分成一個個獨立的明文塊,每一個明文塊長度128bit。
這些明文塊經過AES加密器復雜處理,生成一個個獨立的密文塊,這些密文塊拼接在一起,就是最終的AES加密的結果。
但這里涉及到一個問題,假如一段明文長度是196bit,如果按每128bit一個明文塊來拆分的話,第二個明文塊只有64bit,不足128bit。這時候怎么辦呢?就需要對明文塊進行填充(Padding)。
幾種典型的填充方式:
{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、CFB、OFB、CTR
AES加密算法 - 加密模式
ECB
最簡單的加密模式,明文消息被分成固定大小的塊(分組),并且每個塊被單獨加密。
每個塊的加密和解密都是獨立的,且使用相同的方法進行加密,所以可以進行并行計算,但是這種方法一旦有一個塊被破解,使用相同的方法可以解密所有的明文數據,安全性比較差。
適用于數據較少的情形,加密前需要把明文數據填充到塊大小的整倍數。
優點:
1.簡單
2.有利于并行計算
3.誤差不會被傳送
缺點:
1.不能隱藏明文的模式
2.可能對明文進行主動攻擊
CBC
這種模式是先將明文切分成若干小段,然后每一小段與初始塊或者上一段的密文段進行異或運算后,再與密鑰進行加密。
這樣每個密文塊依賴該塊之前的所有明文塊,為了保持每條消息都具有唯一性,第一個數據塊進行加密之前需要用初始化向量IV進行異或操作。
CBC模式是一種最常用的加密模式,它主要缺點是加密是連續的,不能并行處理,并且與ECB一樣消息塊必須填充到塊大小的整倍數。
優點:
1.不容易主動攻擊,安全性好于ECB,適合傳輸長度長的報文,是SSL、IPSec的標準。
缺點:
1.不利于并行計算
2.誤差傳遞
3.需要初始化向量IV
CFB
和CBC模式比較相似,前一個分組的密文加密后和當前分組的明文XOR異或操作生成當前分組的密文。CFB模式的解密和CBC模式的加密在流程上其實是非常相似的。
優點:
1.隱藏了明文模式
2.分組密碼轉化為流模式
3.可以及時加密傳送小于分組的數據
缺點:
1.不利于并行計算
2.誤差傳送:一個明文單元損壞影響多個單元
3.唯一的IV
OFB
將分組密碼轉換為同步流密碼,也就是說可以根據明文長度先獨立生成相應長度的流密碼。通過流程圖可以看出,OFB和CFB非常相似,CFB是前一個分組的密文加密后XOR當前分組明文,OFB是前一個分組與前一個明文塊異或之前的流密碼XOR當前分組明文。由于異或操作的對稱性,OFB模式的解密和加密完全一樣的流程。
優點:
1.隱藏了明文模式
2.分組密碼轉化為流模式
3.可以及時加密傳送小于分組的數據
缺點:
1.不利于并行計算
2.對明文的主動攻擊是可能的
3.誤差傳送:一個明文單元損壞影響多個單元
CTR
對一系列輸入數據塊(稱為計數)進行加密,產生一系列的輸出塊,輸出塊與明文異或得到密文
加密過程使用了密鑰、Nonce(類似IV)、Counter(一個從0到n的編號)
最大的優勢是可以并行執行,因為所有的塊只依賴于Nonce與Counter,并不會依賴于前一個密文塊,可事先進行加解密準備,適合高速傳輸需求
GCM工作模式
CBC、CFB、OFB 三種模式可以解決 ECB 模式中相同明文生成相同密文的缺陷,CTR 又可以在此基礎上提供多分組并行加密特性,但是它們都不能提供密文消息完整性校驗功能,所有就有了 GCM 模式。
微信支付成功回調通知使用了該模式
完整性
有的人可能會想到,如果將密文的hash值隨密文一起發送,接收者對收到的密文計算hash值,與收到的hash值進行比對,這樣是否就能校驗消息的完整性呢?
但再仔細想想,就能發現這其中的漏洞。
如果篡改者截獲原始密文后,先篡改密文,而后計算篡改后的密文hash,替換掉原始消息中的密文hash。這樣,接收者依舊沒有辦法發現對密文的篡改。
可見,使用單向散列函數計算hash值仍然不能解決消息完整性校驗的問題。
MAC消息驗證碼
MAC:Message Authentication Code, 消息驗證碼
消息驗證碼是一種與密鑰相關的單項散列函數。
發送者使用密鑰和消息得到MAC值,接收者利用密鑰和收到的密文計算MAC值,與隨消息而來的MAC值做 比較,從而判斷消息是否被改過(完整性)。
當篡改者篡改密文后,因為沒有密鑰,就無法計算篡改后的密文的MAC值。
GMAC
GMAC:Galois message authentication code mode, 伽羅瓦消息驗證碼
對應到上圖中的消息認證碼,GMAC就是利用伽羅華域(Galois Field,GF,有限域)乘法運算來計算消息的MAC值。
GCM
GCM中的G 就是值GMAC,C就是指CTR。 所以GCM可以提供對消息的加密和完整性校驗。
就像CTR模式下一樣,先對塊進行順序編號,然后將該塊編號與初始向量(IV)組合,并使用密鑰K,對輸入做AES加密,然后,將加密的結果與明文進行XOR運算來生成密文。像CTR模式下一樣,應該對每次加密使用不同的IV。對于附加消息,會使用密鑰H(由密鑰K得出),運行GMAC,將結果與密文進行XOR運算,從而生成可用于驗證數據完整性的身份驗證標簽。最后,密文接收者會收到一條完整的消息,包含密文、IV(計數器CTR的初始值)、身份驗證標簽(MAC值)。
代碼示例
/*** 密鑰長度:128位、192位、256位* 工作模式:ECB/CBC/PCBC/CTR/CTS/CFB/CFB8 to CFB128/OFB/OBF8 to OFB128* 填充方式:Nopadding/PKCS5Padding/ISO10126Padding/*/ public class AESUtils {private static final String KEY_ALGORITHM = "AES";private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//默認的加密算法public static byte[] initSecretKey() throws NoSuchAlgorithmException {//返回生成指定算法密鑰生成器的 KeyGenerator 對象KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);//初始化此密鑰生成器,使其具有確定的密鑰大小//AES 要求密鑰長度為 256kg.init(256);//生成一個密鑰SecretKey secretKey = kg.generateKey();return secretKey.getEncoded();}private static Key toKey(byte[] key){//生成密鑰return new SecretKeySpec(key, KEY_ALGORITHM);}public static byte[] encrypt(byte[] data, Key key) throws Exception{return encrypt(data, key, DEFAULT_CIPHER_ALGORITHM);}public static byte[] decrypt(byte[] data, Key key) throws Exception{return decrypt(data, key,DEFAULT_CIPHER_ALGORITHM);}public static byte[] encrypt(byte[] data, byte[] key) throws Exception{return encrypt(data, key, DEFAULT_CIPHER_ALGORITHM);}public static byte[] decrypt(byte[] data, byte[] key) throws Exception{return decrypt(data, key,DEFAULT_CIPHER_ALGORITHM);}public static byte[] encrypt(byte[] data, byte[] key, String cipherAlgorithm) throws Exception{//還原密鑰Key k = toKey(key);return encrypt(data, k, cipherAlgorithm);}public static byte[] encrypt(byte[] data, Key key, String cipherAlgorithm) throws Exception{//實例化Cipher cipher = Cipher.getInstance(cipherAlgorithm);//使用密鑰初始化,設置為加密模式cipher.init(Cipher.ENCRYPT_MODE, key);//執行操作return cipher.doFinal(data);}public static byte[] decrypt(byte[] data, byte[] key, String cipherAlgorithm) throws Exception{//還原密鑰Key k = toKey(key);return decrypt(data, k, cipherAlgorithm);}public static byte[] decrypt(byte[] data, Key key, String cipherAlgorithm) throws Exception{//實例化Cipher cipher = Cipher.getInstance(cipherAlgorithm);//使用密鑰初始化,設置為解密模式cipher.init(Cipher.DECRYPT_MODE, key);//執行操作return cipher.doFinal(data);}private static String showByteArray(byte[] data){if(null == data){return null;}StringBuilder sb = new StringBuilder("{");for(byte b:data){sb.append(b).append(",");}sb.deleteCharAt(sb.length()-1);sb.append("}");return sb.toString();}public static void main(String[] args) throws Exception {byte[] key = initSecretKey();System.out.println("key字節數組:"+showByteArray(key));Key k = toKey(key); //生成秘鑰System.out.println("key base64編碼:" + Base64.getEncoder().encodeToString(key));System.out.println("key base64解碼:" + showByteArray(Base64.getDecoder().decode(Base64.getEncoder().encodeToString(key))));System.out.println();String data ="AES數據";System.out.println("加密前數據: string: "+data);System.out.println("加密前數據: byte[]: "+showByteArray(data.getBytes()));System.out.println();byte[] encryptData = encrypt(data.getBytes(), k);//數據加密System.out.println("加密后數據: byte[]: "+showByteArray(encryptData));String base64Data = Base64.getEncoder().encodeToString(encryptData);System.out.println("加密后數據: base64: " + base64Data); // System.out.println("加密后數據: hexStr:"+Hex.encodeHexStr(encryptData));System.out.println();byte[] decryptData = decrypt(Base64.getDecoder().decode(base64Data), k);//數據解密System.out.println("解密后數據: byte[]:"+showByteArray(decryptData));System.out.println("解密后數據: string:"+new String(decryptData));}}總結
- 上一篇: 状态模式(State模式)
- 下一篇: 【历史上的今天】12 月 28 日:冯·