Go 實現加密算法系列之對稱加密
加密算法簡介
加密算法就是加密的方法,在密碼學中,加密是將明文信息隱藏起來,使之缺少特殊信息時不可讀。加密算法可以分為兩類:對稱加密和非對稱加密。
1. 對稱加密
將信息使用一個密鑰進行加密,解密時使用同樣的密鑰,同樣的算法進行解密。
2. 非對稱加密
非對稱加密又稱公開密鑰加密,密鑰分為私密密鑰和公開密鑰,通過公鑰加密,私鑰解密的過程,并且加密和解密算法不同。
關于對稱加密
對稱加密是加密和解密雙方持有相同的私鑰,即加密方通過密鑰執(zhí)行加密過程,解密方通過相同的密鑰執(zhí)行解密過程,因為雙方持有相同的密鑰,所以叫做對稱加密。同時,因為密鑰理論上只有加解密雙方持有,所以也叫做私鑰加密。常見的對稱加密算法有:DES,3DES,AES 以及 RC4 等。
對稱加密分為序列密碼和分組密碼兩類,如下圖:

1. 序列密碼
序列密碼單獨加密每個位,它是通過將密鑰序列中的每個位 與每個明文位相加實現的。同步序列密碼的密碼序列僅僅取決于密鑰,而異步序列密碼序列則取決于密鑰和密文,如 RC4。
2. 分組密碼
分組密碼每次使用相同的密鑰加密整個明文位分組。這意味著對給定分組內任何明文位的加密都依賴于與它同在一個分組內的其他所有明文位。絕大多數分組密碼的分組長度要么是 128 位(16 字節(jié)),比如 AES,要么是 64 位(8 字節(jié)),比如 DES 和 3DES。
對稱加密特點
算法可逆,即加密后可以通過解密還原數據。 加密效率高,加密速度快 安全性沒有非對稱加密高,因為密鑰分發(fā)時存在安全隱患
對稱加密算法
在寫對稱加密算法之前,有必要先提一下分組加密的操作模式有哪些,因為分組加密是對稱加密中應用最多的加密方式,并且可以有不同的操作模式。
分組加密模式
1. 電子密碼本模式(ECB)
ECB 模式要求明文長度必須是所使用密碼的分組大小的整數倍,比如在 AES(后面會提到)中,明文長度應該是 16 字節(jié)的整數倍,如果不滿足此長度要求,則必須對其進行填充,填充的方式有很多種,比如:在明文后附加單個"1",然后再根據需要附加足夠多的"0"位,直到明文的長度達到分組長度的整數倍,如果明文的長度剛好是分組長度的整數倍,則填充的所有位剛好形成了一個單獨的額外分組。ECB 加密解密如下圖:


ECB 優(yōu)缺點:
加密和解密之間的分組同步不是必須的,即如果數據丟失,接收方還是可能解密已收到的分組。 加密高度確定,即只要密鑰不變,相同的明文分組總是產生相同的密文分組。 明文分組的加密是完全獨立的,與前面的分組沒有任何關系。如果攻擊者將密文分組重新排序,有可能會得到有效的明文。 密碼分組鏈接模式(CBC)
2. 密碼分組鏈接模式(CBC)
CBC 模式主要基于兩種思想。第一,所有分組的加密都鏈接在一起,是得密文
不僅依賴
,而且還依賴前面所有的明文分組。第二,加密過程使用的初始向量(IV)進行了隨機化。



3. 輸出反饋模式(OFB)
OFB 模式使用分組密碼來構建一個序列密鑰加密方案。注意,在 OFB 模式中,密鑰序列不是按位產生,而是以分組方式產生。密碼的輸出是 b 個密鑰序列位,其中 b 為所使用的分組密碼的寬度;將 b 為的明文與該 b 位的密鑰序列進行異或操作即可實現對明文的加密。如下圖:

OFB 模式首先使用分組密碼加密 IV,得到的密鑰輸出為 b 位密鑰序列的第一個集合;將前一個密鑰輸出反饋給分組密碼進行加密,即可計算出密鑰序列位的下一個分組;不斷重復此過程。因為使用的是同步序列密碼,所以加密與解密操作完全相同,且加密解密位異或操作。OFB 的一個優(yōu)點就是分組密碼的計算與明文無關。因此,可以預計算密鑰序列材料的一個或多個分組 S1。
4. 密碼反饋模式(CFB)
CFB 模式也使用分組密碼作為構建序列密碼的基本元件,與 OFB 模式相同的是,CFB 也使用了反饋;而不同的是,OFB 反饋的是分組密碼的輸出,而 CFB 反饋的是密文。OFB 的基本思想是:要生成第一個密鑰序列分組 S1,必須先加密 IV;而所有后續(xù)密鑰序列分組 S2,S3,。。。都是通過加密前一個密文得到的。如下圖所示:

根據序列密碼加密,CFB 加密解密操作也完全相同,但 CFB 模式是一個異步序列密鑰加密。
5. 計數器模式(CTR)
與 OFB 和 CFB 模式一樣,密鑰序列也是以分組方式計算的。分組密碼的輸入為一個計數器,每當分組密碼計算一個新密鑰序列分組時,該計數器都會產生一個不同的值,如下圖所示:

初始化分組密碼的輸入時必須避免兩次使用相同過的輸入值,如果攻擊者知道了使用相同輸入加密的明文中的任何一個,就可以計算出密鑰序列分組,從而可以解密其他明文。計數器模式最大的特點就是并行化,因為計數器模式不需要任何反饋。加入有兩個分組加密引擎,即可以讓兩個引擎同時使用第一個分組密碼加密計數器值 CTR1 和 CTR2,這兩個分組密碼引擎完成后,一個繼續(xù)加密 CTR3,而另一個則加密 CTR4。
6. 伽羅瓦計數器模式(GCM)
GCM 是一種計算消息驗證碼的加密模式。MAC(消息驗證碼)由發(fā)送者計算密碼校驗和,并附加在消息后面,接收方也會計算此消息的密碼校驗和,并校驗與發(fā)送者發(fā)送過來的是否相同,這樣便可以確認消失是否是發(fā)送者發(fā)送過來的,且密文是否有被修改過。
主流對稱加密算法實現(Go 語言)
下面將逐一介紹上面提到的對稱加密的算法實現。
1. RC4
RC4 為流加密算法,即前面提到的序列加密,由偽隨機數生成器和異或運算組成,密鑰長度可變,范圍在[1,255]。RC4 一個字節(jié)一個字節(jié)地加解密,由于采用的是異或運算,使用的是同一套算法。
func?Encrypt(bytes?[]byte,?key?[]byte)?([]byte,?error)?{
????c,?err?:=?rc4.NewCipher(key)
????if?err?!=?nil?{
????????return?nil,?err
????}
????var?dst?=?make([]byte,?len(bytes))
????c.XORKeyStream(dst,?bytes)
????return?dst,?nil
}
//RC4為序列密碼加密,采用異或運算,所以此處直接調用解密算法
func?Decrypt(encryptData,?key?[]byte)?([]byte,?error)?{
????return?Encrypt(encryptData,?key)
}
2. DES(3DES)
DES 是一種使用 56 位(7 字節(jié))(注:其實 DES 的輸入密鑰是 64 位(8 字節(jié)),但是每個字節(jié)的第 8 位都作為前面 7 位的一個奇校驗位,所以實際密鑰有效位為 56 位。)密鑰對 64 位(8 字節(jié))長分組進行加密的算法,加、解密過程使用相同對密鑰。
DES 算法機制為迭代,一共 16 輪,每輪操作完全相同,但使用不同的子密鑰,子密鑰都是由主密鑰推導而來的,都是從輸入密鑰中選擇 48 個置換位。DES 加密流程如下:

3DES 即為三重 DES 加密,3DES 加密密鑰長度為 192 位(24 字節(jié))。
解密過程主要為密鑰編排逆轉,即解密的第一輪需要子密鑰 16;第二輪需要子密鑰 15;以此類推。
func?Encrypt(plainText?[]byte,?key?[]byte)?([]byte,?error)?{
????//block,?err?:=?des.NewTripleDESCipher(key)為三重DES加密
????block,?err?:=?des.NewCipher(key)
????if?err?!=?nil?{
????????return?nil,?err
????}
????blockSize?:=?block.BlockSize()?????????????????????//分組大小
????plainText?=?paddingWithPKCS5(plainText,?blockSize)?//填充
????var?cipherText?=?make([]byte,?len(plainText))
????//ECB每個分組單獨加密
????var?temp?=?cipherText
????for?len(plainText)?>?0?{
????????block.Encrypt(temp,?plainText[:blockSize])
????????plainText?=?plainText[blockSize:]
????????temp?=?temp[blockSize:]
????}
????return?cipherText,?nil
}
func?Decrypt(cipherText?[]byte,?key?[]byte)?([]byte,?error)?{
????//block,?err?:=?des.NewTripleDESCipher(key)為三重DES加密
????block,?err?:=?des.NewCipher(key)
????if?err?!=?nil?{
????????return?nil,?err
????}
????blockSize?:=?block.BlockSize()?//分組大小
????var?plainText?=?make([]byte,?len(cipherText))
????//ECB每個分組單獨加密
????var?temp?=?plainText
????for?len(cipherText)?>?0?{
????????block.Decrypt(temp,?cipherText[:blockSize])
????????cipherText?=?cipherText[blockSize:]
????????temp?=?temp[blockSize:]
????}
????plainText?=?unPaddingWithPKCS5(plainText)
????return?plainText,?nil
}
func?paddingWithPKCS5(data?[]byte,?blockSize?int)?[]byte?{
????//需要填充的值以及數量
????padding?:=?blockSize?-?len(data)%blockSize
????//組裝填充值([]byte)
????var?paddingData?=?[]byte{byte(padding)}
????paddingData?=?bytes.Repeat(paddingData,?padding)
????//append填充
????data?=?append(data,?paddingData...)
????return?data
}
func?unPaddingWithPKCS5(data?[]byte)?[]byte?{
????padding?:=?int(data[len(data)-1])
????return?data[:len(data)-padding]
}
3. AES
AES 擁有三種不同的密鑰長度抵抗蠻力攻擊,分別為 128 位,192 位和 256 位,分組大小為 128 位。
func?Encrypt(plainText?[]byte,?key?[]byte)?([]byte,?error)?{
????block,?err?:=?aes.NewCipher(key)
????if?err?!=?nil?{
????????return?nil,?err
????}
????blockSize?:=?block.BlockSize()?????????????????????//分組大小
????plainText?=?paddingWithPKCS5(plainText,?blockSize)?//填充
????var?cipherText?=?make([]byte,?len(plainText))
????//CBC模式
????blockMode?:=?cipher.NewCBCDecrypter(block,?key[:blockSize])
????blockMode.CryptBlocks(cipherText,plainText)
????return?cipherText,?nil
}
func?Decrypt(cipherText?[]byte,?key?[]byte)?([]byte,?error)?{
????block,?err?:=?aes.NewCipher(key)
????if?err?!=?nil?{
????????return?nil,?err
????}
????blockSize?:=?block.BlockSize()?//分組大小
????var?plainText?=?make([]byte,?len(cipherText))
????//CBC模式
????blockMode?:=?cipher.NewCBCEncrypter(block,?key[:blockSize])
????blockMode.CryptBlocks(plainText,?cipherText)
????plainText?=?unPaddingWithPKCS5(plainText)
????return?plainText,?nil
}
引用資料
《維基百科》《深入淺出密碼學——常用加密技術原理與應用》
最后
如有錯誤,請不吝賜教!Thanks!
附上代碼鏈接:https://github.com/pyihe/secret。
推薦閱讀
站長 polarisxu
自己的原創(chuàng)文章
不限于 Go 技術
職場和創(chuàng)業(yè)經驗
Go語言中文網
每天為你
分享 Go 知識
Go愛好者值得關注
