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)
}
执行结果: