1. 程式人生 > >密碼學02--go語言與對稱加密演算法的實現

密碼學02--go語言與對稱加密演算法的實現

1.致謝

非常感謝Go語言中文網這個開源社群所提供的有關Go語言各種介面的文件說明:https://studygolang.com/pkgdoc

2.對稱加密演算法在go語言中的實現分析

2.1 演算法選擇

這裡僅針對DES和AES加密演算法來做了測試,因為3DES的結構邏輯和DES幾乎完全相同

2.2 分組模式

這裡選擇了CBC和CTR兩種安全性比較高的分組模式,而沒有選擇ECB、CFB和OFB。當然ECB和CBC結構類似,而CFB和OFB與CTR類似,所以經驗可以完全照搬即可。

2.3 go語言中對稱加密演算法的【固有模式】

由於go語言對與對稱加密演算法的包提供的“太過於詳細”,所以在go語言中進行對稱加密演算法幾本可以按照幾個固有的模式來進行就OK

//一、加密過程
//--------------------------------------------------------------------------------
//(1)選擇一種演算法,建立一個底層使用的des/3des/aes的密碼介面
//a.建立並返回一個使用DES演算法的cipher.Block介面。key引數指的是金鑰,長度是8bit。返回的是一個cipher.Block
func NewCipher(key []byte) (cipher.Block, error)
//b.建立並返回一個使用TDEA演算法的cipher.Block介面。
func NewTripleDESCipher(key []byte) (cipher.Block, error)
//c.建立一個cipher.Block介面。引數key為金鑰,長度只能是16、24、32位元組,用以選擇AES-128、AES-192、AES-256。
func NewCipher(key []byte) (cipher.Block, error)

//返回值Block介面代表一個使用特定金鑰的底層塊加/解密器。它提供了加密和解密獨立資料塊的能力。
/*
type Block interface {
    // 返回加密位元組塊的大小
    BlockSize() int
    // 加密src的第一塊資料並寫入dst,src和dst可指向同一記憶體地址
    Encrypt(dst, src []byte)
    // 解密src的第一塊資料並寫入dst,src和dst可指向同一記憶體地址
    Decrypt(dst, src []byte)
}
*/
//--------------------------------------------------------------------------------
//(2)選擇密碼分組模式:如果cbc/ecb分組模式需要自主編輯對明文分組進行填充操作,如果ctr/cfb/ofb則不需要本步驟
//--------------------------------------------------------------------------------
//(3)建立一個分組密碼模式的介面物件(選擇CBC還是CTR)
//返回一個密碼分組連結模式的、底層用b加密的BlockMode介面,初始向量iv的長度必須等於b的塊尺寸。		
func NewCBCEncrypter(b Block, iv []byte) BlockMode
//返回一個密碼分組連結模式的、底層用b解密的BlockMode介面,初始向量iv必須和加密時使用的iv相同。
func NewCBCDecrypter(b Block, iv []byte) BlockMode
//BlockMode介面代表一個工作在塊模式(如CBC、ECB等)的加/解密器。
/*
type BlockMode interface {
    // 返回加密位元組塊的大小
    BlockSize() int
    // 加密或解密連續的資料塊,src的尺寸必須是塊大小的整數倍,src和dst可指向同一記憶體地址
    CryptBlocks(dst, src []byte)
}
*/

//返回一個計數器模式的、底層採用block生成key流的Stream介面,初始向量iv的長度必須等於block的塊尺寸。
func NewCTR(block Block, iv []byte) Stream
//Stream介面代表一個流模式的加/解密器。
/*
type Stream interface {
    // 從加密器的key流和src中依次取出位元組二者xor後寫入dst,src和dst可指向同一記憶體地址
    XORKeyStream(dst, src []byte)
}
*/
//--------------------------------------------------------------------------------
/*(4)通過不同分組密碼模式的介面物件,執行分組加密方法。
    有blockMode的執行CryptBlocks加密
    有stream的執行xorkeystream加密
*/
解密過程:
(1)選擇一種演算法:建立一個底層使用的des/3des/aes的密碼介面
(2)建立一個分組密碼模式的介面物件(選擇CBC還是CTR)
(3)通過分組密碼模式的介面物件,執行分組解密方法。得到明文(但是帶有填充內容)
(4)去除填充內容,得到原始明文

3.對稱加密演算法在go語言中的實現模板

3.1 使用【DES加密演算法】+【CBC分組密碼模式】加密--解密模板

/*
工具方法:
(1)工具函式,實現對最後一個分組資料進行填充
	supplementLastGroup
	plainText []byte : 明文切片
	blockSize int : 根據演算法決定分組的長度 (des、3des是8,aes是16)
(2)工具函式,實現對最後一個分組資料進行填充取消
	unsupplementLastGroup
	plainText []byte : 明文切片
*/
func supplementLastGroup(plainText []byte, blockSize int) []byte {
	//獲取填充長度
	supNum := blockSize - len(plainText)%blockSize
	//建立一個新的字元切片,長度和填充長度相同,用填充長度填充(卻幾補幾)
	supText := bytes.Repeat([]byte{byte(supNum)},supNum)
	//把supText拼接到plainText內部,然後返回
	//...可以監測到切片,並對切片進行擴容
	return append(plainText, supText...)
}
func unsupplementLastGroup(plainText []byte) []byte{
	//獲取最後一個字元,並還原回整數
	lastCharNum := int(plainText[len(plainText)-1])
	//將原內容從起始位置擷取到【長度-補充】位置。
	return plainText[:len(plainText)-lastCharNum]
}

//使用【DES加密演算法】+【CBC分組密碼模式】加密--解密
func desCbcEncryption(plainText,key []byte)[]byte{
	//1.建立一個底層加密介面物件(des,3des還是aes)
	block,err := des.NewCipher(key)
	if err!=nil{
		panic(err)
	}
	//2.如果選擇ecb或者cbc密碼分組模式,則需要對明文最後一個分組內容進行填充
	newText := supplementLastGroup(plainText,block.BlockSize())//supplementLastGroup(plainText,des.BlockSize)
	//3.建立一個密碼分組模式的介面物件(cbc和ctr)
	iv := []byte{1,2,3,4,5,6,7,8}
	blcokMode := cipher.NewCBCEncrypter(block, iv)
	//4.實現加密
	blcokMode.CryptBlocks(newText, newText)
	//5.返回加密密文
	return newText
}
func desCbcDecryption(cipherText,key []byte)[]byte{
	//1.建立一個底層加密介面物件(des,3des還是aes)
	block,err := des.NewCipher(key)
	if err!=nil{
		panic(err)
	}
	//2.建立一個密碼分組模式的介面物件(cbc和ctr)
	iv := []byte{1,2,3,4,5,6,7,8}
	blcokMode := cipher.NewCBCDecrypter(block, iv)
	//3.實現解密
	blcokMode.CryptBlocks(cipherText, cipherText)
	//4.對最後一個分組資料進行消除填充,並返回
	return unsupplementLastGroup(cipherText)
}
func main() {
	key1:=[]byte("1234abcd")
	result1 := desCbcEncryption([]byte("helloworld今天天氣好晴朗處處好風光"),key1)
	fmt.Printf("%s\n",result1)
	reward1 := desCbcDecryption(result1,key1)
	fmt.Printf("%s\n",reward1)
}

執行結果:

3.2 使用【AES加密演算法】+【CTR分組密碼模式】加密--解密模板

func supplementLastGroup(plainText []byte, blockSize int) []byte {
	supNum := blockSize - len(plainText)%blockSize
	supText := bytes.Repeat([]byte{byte(supNum)},supNum)
	return append(plainText, supText...)
}
func unsupplementLastGroup(plainText []byte) []byte{
	lastCharNum := int(plainText[len(plainText)-1])
	return plainText[:len(plainText)-lastCharNum]
}
func aesCtrEncryption(plainText,key []byte)[]byte{
	//1.建立一個底層加密介面物件(des,3des還是aes)
	block,err := aes.NewCipher(key)
	if err!=nil{
		panic(err)
	}
	//2.ctr不需要內容填充
	//3.建立一個密碼分組模式的介面物件
	iv := []byte("abcdefgh12345678")	//由於演算法是aes,所以隨機數種子長度應該是16長度
	stream := cipher.NewCTR(block, iv)
	//4.使用流物件呼叫異或加密
	stream.XORKeyStream(plainText, plainText)
	//5.返回
	return plainText
}
func aesCtrDecryption(cipherText,key []byte)[]byte{
	//1.建立一個底層加密介面物件(des,3des還是aes)
	block,err := aes.NewCipher(key)
	if err!=nil{
		panic(err)
	}
	//2.建立一個密碼分組模式的介面物件
	iv := []byte("abcdefgh12345678")	//由於演算法是aes,所以隨機數種子長度應該是16長度
	stream := cipher.NewCTR(block, iv)
	//3.使用流物件呼叫異或解密(一次異或加密,在異或一次解密。都是這個方法)
	stream.XORKeyStream(cipherText, cipherText)
	//4.不需要對內容消除填充
	//5.直接將解密內容返回
	return cipherText
}

func main() {
	key2:=[]byte("12345678abcdefgh")
	result2 := aesCtrEncryption([]byte("helloworld今天天氣好晴朗處處好風光"),key2)
	fmt.Printf("%s\n",result2)
	reward2 := aesCtrDecryption(result2,key2)
	fmt.Printf("%s\n",reward2)
}

執行結果: