密码学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)
}

执行结果:

猜你喜欢

转载自blog.csdn.net/u013792921/article/details/85019361