Go语言 Go加密之DES

前端之家收集整理的这篇文章主要介绍了Go语言 Go加密之DES前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

接着RSA加密解密,我们继续来看看DES的加密解密

一、DES简介

DES(Data Encryption Standard)是对称加密算法,也就是加密和解密用相同的密钥。其入口参数有三个:key、data、mode。key为加密解密使用的密钥,data为加密解密的数据,mode为其工作模式。当模式为加密模式时,明文按照64位进行分组,形成明文组,key用于对数据加密,当模式为解密模式时,key用于对数据解密。实际运用中,密钥只用到了64位中的56位,这样才具有高的安全性。DES 的常见变体是三重 DES,使用 168 位的密钥对资料进行三次加密的一种机制;它通常(但非始终)提供极其强大的安全性。如果三个 56 位的子元素都相同,则三重 DES 向后兼容 DES。

DES加密,涉及到加密模式和填充方式,所以,和其他语言加解密时,应该约定好加密模式和填充方式。(模式定义了Cipher如何应用加密算法。改变模式可以容许一个块加密程序变为流加密程序。)

关于分组加密:分组密码每次加密一个数据分组,这个分组的位数可以是随意的,一般选择64或者128位。另一方面,流加密程序每次可以加密或解密一个字节的数据,这就使它比流加密的应用程序更为有用。

在用DES加密解密时,经常会涉及到一个概念:块(block,也叫分组),模式(比如cbc),初始向量(iv),填充方式(padding,包括none,用’\0′填充,pkcs5padding或pkcs7padding)。多语言加密解密交互时,需要确定好这些。比如这么定:

采用3DES、CBC模式、pkcs5padding,初始向量用key充当;另外,对于zero padding,还得约定好,对于数据长度刚好是block size的整数倍时,是否需要额外填充。

二、Go DES加密解密
1、crypto/des包

Go中crypto/des包实现了 Data Encryption Standard (DES) and the Triple Data Encryption Algorithm (TDEA)。查看该包文档,发现相当简单:
定义了DES块大小(8bytes),定义了一个KeySizeError。另外定义了两个我们需要特别关注的函数,即
1 func NewCipher(key []byte) (cipher.Block,error)
2 func NewTripleDESCipher(key []byte) (cipher.Block,error)

他们都是用来获得一个cipher.Block。从名字可以很容易知道,DES使用NewCipher,3DES使用NewTripleDESCipher。参数都是密钥(key)
2、crypto/cipher包

那么,cipher这个包是干嘛用的呢?它实现了标准的块加密模式。我们看一下cipher.Block
1 type Block interface {
2 // BlockSize returns the cipher's block size.
3 BlockSize() int
4
5 // Encrypt encrypts the first block in src into dst.
6 // Dst and src may point at the same memory.
7 Encrypt(dst,src []byte)
8
9 // Decrypt decrypts the first block in src into dst.
10 // Dst and src may point at the same memory.
11 Decrypt(dst,src []byte)
12 }

这是一个接口

对称加密,按块方式,我们经常见到CBC、ECB之类的,这些是加密模式。可以参考:DES加密模式详解http://linux.bokee.com/6956594.html Go中定义了一个接口BlockMode代表各种模式 1 type BlockMode interface { 2 // BlockSize returns the mode's block size. 3 BlockSize() int 4 5 // CryptBlocks encrypts or decrypts a number of blocks. The length of 6 // src must be a multiple of the block size. Dst and src may point to 7 // the same memory. 8 CryptBlocks(dst,src []byte) 9 } 该包还提供了获取BlockMode实例的两个方法 1 func NewCBCDecrypter(b Block,iv []byte) BlockMode 2 func NewCBCEncrypter(b Block,iv []byte) BlockMode 即一个CBC加密,一个CBC解密 对于按流方式加密的,定义了一个接口: 1 type Stream interface { 2 // XORKeyStream XORs each byte in the given slice with a byte from the 3 // cipher's key stream. Dst and src may point to the same memory. 4 XORKeyStream(dst,src []byte) 5 } 同样也提供了获取实现该接口的实例 这里,我们只讨论CBC模式 3、加密解密 1)DES DES加密代码如下: 1 func DesEncrypt(origData,key []byte) ([]byte,error) { 2 block,err := des.NewCipher(key) 3 if err != nil { 4 return nil,err 5 } 6 origData = PKCS5Padding(origData,block.BlockSize()) 7 // origData = ZeroPadding(origData,block.BlockSize()) 8 blockMode := cipher.NewCBCEncrypter(block,key) 9 crypted := make([]byte,len(origData)) 10 // 根据CryptBlocks方法的说明,如下方式初始化crypted也可以 11 // crypted := origData 12 blockMode.CryptBlocks(crypted,origData) 13 return crypted,nil 14 } 以上代码使用DES加密(des.NewCipher),加密模式为CBC(cipher.NewCBCEncrypter(block,key)),填充方式PKCS5Padding,该函数代码如下: 1 func PKCS5Padding(ciphertext []byte,blockSize int) []byte { 2 padding := blockSize - len(ciphertext)%blockSize 3 padtext := bytes.Repeat([]byte{byte(padding)},padding) 4 return append(ciphertext,padtext...) 5 } 可见,数据长度刚好是block size的整数倍时,也进行了填充,如果不进行填充,unpadding会搞不定。 另外,为了方便,初始向量直接使用key充当了。 DES解密代码如下: 1 func DesDecrypt(crypted,err 5 } 6 blockMode := cipher.NewCBCDecrypter(block,key) 7 origData := make([]byte,len(crypted)) 8 // origData := crypted 9 blockMode.CryptBlocks(origData,crypted) 10 origData = PKCS5UnPadding(origData) 11 // origData = ZeroUnPadding(origData) 12 return origData,nil 13 } 可见,解密无非是调用cipher.NewCBCDecrypter,最后unpadding,其他跟加密几乎一样。相应的PKCS5UnPadding: 1 func ZeroUnPadding(origData []byte) []byte { 2 length := len(origData) 3 // 去掉最后一个字节 unpadding 次 4 unpadding := int(origData[length-1]) 5 return origData[:(length - unpadding)] 6 } 2)、3DES 加密代码: 1 // 3DES加密 2 func TripleDesEncrypt(origData,error) { 3 block,err := des.NewTripleDESCipher(key) 4 if err != nil { 5 return nil,err 6 } 7 origData = PKCS5Padding(origData,block.BlockSize()) 8 // origData = ZeroPadding(origData,block.BlockSize()) 9 blockMode := cipher.NewCBCEncrypter(block,key[:8]) 10 crypted := make([]byte,len(origData)) 11 blockMode.CryptBlocks(crypted,origData) 12 return crypted,nil 13 } 对比DES,发现只是换了NewTripleDESCipher。不过,需要注意的是,密钥长度必须24byte,否则直接返回错误。关于这一点,PHP中却不是这样的,只要是8byte以上就行;而Java中,要求必须是24byte以上,内部会取前24byte(相当于就是24byte)。 另外,初始化向量长度是8byte(目前各个语言都是如此,不是8byte会有问题)。然而,如果你用的Go是1.0.3(或以下),iv可以不等于8byte。其实,在cipher.NewCBCEncrypter方法中有注释: The length of iv must be the same as the Block’s block size. 可是代码中的实现却没有做判断。不过,go tips中修正了这个问题,如果iv不等于block size(des为8),则直接panic。所以,对于加解密,一定要测试,保证iv等于block size,否则可能会panic: 1 func NewCBCDecrypter(b Block,iv []byte) BlockMode { 2 if len(iv) != b.BlockSize() { 3 panic("cipher.NewCBCDecrypter: IV length must equal block size") 4 } 5 return (*cbcDecrypter)(newCBC(b,iv)) 6 } 此处之所有用panic而不是返回error,个人猜测,是由于目前发布的版本,该方法没有返回error,修改方法签名会导致兼容性问题,因此用panic了。 解密代码: 1 // 3DES解密 2 func TripleDesDecrypt(crypted,err 6 } 7 blockMode := cipher.NewCBCDecrypter(block,key[:8]) 8 origData := make([]byte,len(crypted)) 9 // origData := crypted 10 blockMode.CryptBlocks(origData,crypted) 11 origData = PKCS5UnPadding(origData) 12 // origData = ZeroUnPadding(origData) 13 return origData,nil 14 } 三、和其他语言交互:加解密 这次,我写了PHP、Java的版本,具体代码放在github上。这里说明一下,Java中,默认模式是ECB,且没有用”\0″填充的情况,只有NoPadding和PKCS5Padding;而PHP中(mcrypt扩展),默认填充方式是”\0″,而且,当数据长度刚好是block size的整数倍时,默认不会填充”\0″,这样,如果数据刚好是block size的整数倍且结尾字符是”\0″,会有问题。 综上,跨语言加密解密,应该使用PKCS5Padding填充。

猜你在找的Go相关文章