原文地址:http://davidleee.com/2016/04/26/about-aes-encryption/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
前世今生
AES 的出現就是為了取代原來的數據加密標準(DES),作為爺爺級的加密算法,DES在風光過后也是到了該退休的年紀了。
關于DES
在繼續了解AES之前,不妨先看看被它取代的DES是什么。
它的全稱為Data Encryption Standard,是一種對稱密鑰加密塊算法,大致的加密流程長這個樣子:
?
在進入到加密流程之前,64位的塊被拆分為兩個32位的子塊,并作為 IP 的兩個輸入。中間的 F 是 Feistel function,算法中的密鑰就是在這個函數中被用到的。
塊加密:Block cipher, 也叫作分組加密,是將明文分成多個等長模塊(block),使用確定的算法和對稱密鑰對每組分別加密解密的方式。
DES 在1976年曾經風光一時,被美國聯邦政府的國家標準局定為聯邦資料處理標準(FIPS)。然而因為它只是用了56位的密鑰,所以在當下已經不是一種安全的加密方法。在1999年1月,已經有組織在22小時15分鐘內公開破解了一個DES密鑰。
后來出現了一種改進的 DES,叫 TDES 或 3DES。它本質上就是把密鑰個數增加到了3個,并沒有算法上的改進。(感覺很兒戲的樣子)
在2001年,DES已經不再是國際標準科技協會(NIST,前 FIPS)的一個標準,而且也開始慢慢被AES所取代。
言歸正傳
AES,全稱為Advanced Encryption Standard,原名叫做Rijndael 加密法。(還是新名字好念)
至于一開始為什么有個這么拗口的名字,因為兩位作者的名字是 Joan Daemen 和 Vincent Rijmen,發現為什么了嗎?這是不是密碼學家約定俗成的某種命名方式呢?
2001年11月26日,美國的 NIST 公布了 AES 這一標準,并開始了長達5年的標準化進程,直到 Rijndael 被選為最適合的方法。
在2002年5月26日,AES 成為了一項聯邦政府標準。它還是聯邦安全局(NSA)批準的唯一一種用來加密頂級機密信息的公開加密方法。
也就是說,如果你想要黑 FBI,也許可以試試看 AES 解密 :)
嚴格來說,AES 和 Rijndael 并不完全一樣。AES 使用的是固定128位大小的塊,密鑰的大小只能是128位、192位或256位;而 Rijndael 使用的塊大小和密鑰長度可以是在128位和256位之間能被32整除的任意值,相對來說靈活性高了很多。
主要過程
AES加密算法的組成可以分成4個主要部分:
AddRoundKey
SubBytes
ShiftRows
MixColumns
簡單來說,就是將上面的幾個部分組合起來形成三種不同的序列,然后把這些過程序列重復執行若干個回合,具體的循環次數由密鑰的長度決定:
128位密鑰:循環10次
192位密鑰:循環12次
256位密鑰:循環14次
這三種序列是:
首次循環:
AddRoundKey
一般循環:
SubBytes
ShiftRows
MixColumns
AddRoundKey
末尾循環:
SubBytes
ShiftRows
AddRoundKey
那么這幾個部分到底是干了些什么呢?
AddRoundKey
在每一次循環中,通過 Rijndael 密鑰生成方案(https://en.wikipedia.org/wiki/Rijndael_key_schedule)從主密鑰中生成一個子密鑰,這個子密鑰的大小應該等同于塊的大小,并且以列優先的方式排列在一個矩陣里(每個塊也是以這樣的方式排列在矩陣里的)。
接下來將這個子密鑰的值與塊上對應位置的值 XOR 起來,形成一個新的矩陣,到這里這一過程就算完成了。?
SubBytes
這一步會使用到一個叫做 Rijndael S-box 的東西,它其實就是一個8位的代換表,每一個字節的數據都可以在表中查到對應的代換結果。只要這個 S-box 在構建的時候足夠好,就可以大大降低這次加密的線性關系。下面是一個6位 S-box 的例子,輸入的值是011011,輸出的值是1001。
?將塊矩陣中的每一個元素通過 S-box 進行代換,組成一個代換后的矩陣,就是 SubBytes 這一步的工作。
ShiftRows
這一步容易理解,就是把塊矩陣中的每一行都進行一個向左循環移位,最后的效果是要讓輸出矩陣的每一列上的元素都屬于輸入矩陣原本不同的列。
這樣做可以保證每一列上的元素都是非線性相關的。?
MixColumns
這個部分會接受4個字節的輸入,并輸出4個字節,而且每一個字節輸入的字節都會對輸出造成影響,所以它跟上面的 ShiftRows 一起為加密算法提供了良好的擴散性.
擴散性(Diffusion):如果改變了任意1位的原文,密文中一半以上的位也應該會跟著改變;反過來,改變了任意1位密文,得到的原文也應該有一半以上的位被改變。—— Stallings, William (2014). Cryptography and Network Security (6th ed.)
簡單來說,這一步就是講輸入矩陣的每一列與一個固定的多項式在一定條件下相乘。最終得到的將會是一個與輸入矩陣完全不一樣的輸出矩陣。
?
更多資料
伽羅華域(Galois Field,GF,有限域)乘法運算(http://blog.csdn.net/mengboy/article/details/1514445)
Confusion and diffusion(https://en.wikipedia.org/wiki/Confusion_and_diffusion)
填充算法
對于塊加密算法來說,如果數據的長度不滿一個塊的大小,我們就需要主動填充一些數據,讓這個塊的大小可以滿足要求,于是,一個合適的填充算法就顯得尤為重要。
經過導師的提醒并且在網上讀了一些博客之后發現,Java端與iOS端使用的AES填充算法是不一樣的(http://my.oschina.net/nicsun/blog/95632),在 Java 端上使用的是 PKCS5Padding ,而在iOS端上使用的是 PKCS7Padding 。所以就會導致在其中一端上加解密沒有問題,但是把密文發到另一端上解密就會得到完全不同的結果。
P.S. 這里說到的 Java 端 應該是指服務器端,Android 端上不知道有沒有這個問題。
PKCS5 相當于是 PKCS7 的一個子集,因為 PKCS7 理論上支持1~255字節的塊大小填充,而 PKCS5 只支持8字節的塊大小填充。其實 PKCS5 更多是應用在 DES/3DES 上。
具體的填充過程也非常好理解,直接舉例子好了:比如說塊大小為8字節的加密算法,現在有一串長度為9的數據:
FF FF FF FF FF FF FF FF FF(9個FF)
使用 PKCS7 算法去填充的話,結果就是這樣的
FF FF FF FF FF FF FF FF FF 07 07 07 07 07 07 07(9個FF和7個07)
填充的目的就是把塊給補滿,所以這里填充的長度為7;而采用 PKCS7 算法的話,填充的每一個字節都是填充長度的十六進制數,那就也是7。
有趣的是,如果采用 PKCS5 去填充,因為它的目標塊大小是8,所以這里會填充一個01。詳情可以參考Can AES use PKCS5 padding(http://crypto.stackexchange.com/a/11274)里的最佳答案。
參考資料
Advanced Encryption Standard(https://en.wikipedia.org/wiki/Advanced_Encryption_Standard)
Rijndael 密鑰生成方案(https://en.wikipedia.org/wiki/Rijndael_key_schedule)
伽羅華域(Galois Field,GF,有限域)乘法運算(http://blog.csdn.net/mengboy/article/details/1514445)
Confusion and diffusion(https://en.wikipedia.org/wiki/Confusion_and_diffusion)
關于AES256算法java端加密,ios端解密出現無法解密問題的解決方案(http://my.oschina.net/nicsun/blog/95632)
Can AES use PKCS5 padding(http://crypto.stackexchange.com/questions/11272/can-aes-use-pkcs5-padding)
Vanilla社區發起?晨讀計劃?,每天堅持積累一點,今天的努力至少讓我們比昨天更進一步。
?晨讀計劃? 期待你的加入... ...
Vanilla:基于OpenResty的高性能Web應用開發框架
我們的微信號:Vanilla-OpenResty
我們的QQ群:205773855、481213820、34782325
晨讀計劃
2016/04/28