7

Go加密解密之DES

 3 years ago
source link: http://blog.studygolang.com/2013/01/go%e5%8a%a0%e5%af%86%e8%a7%a3%e5%af%86%e4%b9%8bdes/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

接着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。另外定义了两个我们需要特别关注的函数,即

1func NewCipher(key []byte) (cipher.Block, error)
2func NewTripleDESCipher(key []byte) (cipher.Block, error)

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

2、crypto/cipher包

那么,cipher这个包是干嘛用的呢?它实现了标准的块加密模式。我们看一下cipher.Block

1type Block interface {
2// BlockSize returns the cipher's block size.
3BlockSize() int
4 
5// Encrypt encrypts the first block in src into dst.
6// Dst and src may point at the same memory.
7Encrypt(dst, src []byte)
8 
9// Decrypt decrypts the first block in src into dst.
10// Dst and src may point at the same memory.
11Decrypt(dst, src []byte)
12}

这是一个接口

对称加密,按块方式,我们经常见到CBC、ECB之类的,这些是加密模式。可以参考:DES加密模式详解 http://linux.bokee.com/6956594.html
Go中定义了一个接口BlockMode代表各种模式

1type BlockMode interface {
2// BlockSize returns the mode's block size.
3BlockSize() 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.
8CryptBlocks(dst, src []byte)
9}

该包还提供了获取BlockMode实例的两个方法

1func NewCBCDecrypter(b Block, iv []byte) BlockMode
2func NewCBCEncrypter(b Block, iv []byte) BlockMode

即一个CBC加密,一个CBC解密

对于按流方式加密的,定义了一个接口:

1type 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.
4XORKeyStream(dst, src []byte)
5}

同样也提供了获取实现该接口的实例

这里,我们只讨论CBC模式

3、加密解密

1)DES
DES加密代码如下:

1func DesEncrypt(origData, key []byte) ([]byte, error) {
2block, err := des.NewCipher(key)
3if err != nil {
4return nil, err
5}
6origData = PKCS5Padding(origData, block.BlockSize())
7// origData = ZeroPadding(origData, block.BlockSize())
8blockMode := cipher.NewCBCEncrypter(block, key)
9crypted := make([]byte, len(origData))
10// 根据CryptBlocks方法的说明,如下方式初始化crypted也可以
11// crypted := origData
12blockMode.CryptBlocks(crypted, origData)
13return crypted, nil
14}

以上代码使用DES加密(des.NewCipher),加密模式为CBC(cipher.NewCBCEncrypter(block, key)),填充方式PKCS5Padding,该函数的代码如下:

1func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
2padding := blockSize - len(ciphertext)%blockSize
3padtext := bytes.Repeat([]byte{byte(padding)}, padding)
4return append(ciphertext, padtext...)
5}

可见,数据长度刚好是block size的整数倍时,也进行了填充,如果不进行填充,unpadding会搞不定。
另外,为了方便,初始向量直接使用key充当了(实际项目中,最好别这么做)

DES解密代码如下:

1func DesDecrypt(crypted, key []byte) ([]byte, error) {
2block, err := des.NewCipher(key)
3if err != nil {
4return nil, err
5}
6blockMode := cipher.NewCBCDecrypter(block, key)
7origData := make([]byte, len(crypted))
8// origData := crypted
9blockMode.CryptBlocks(origData, crypted)
10origData = PKCS5UnPadding(origData)
11// origData = ZeroUnPadding(origData)
12return origData, nil
13}

可见,解密无非是调用cipher.NewCBCDecrypter,最后unpadding,其他跟加密几乎一样。相应的PKCS5UnPadding:

1func PKCS5UnPadding(origData []byte) []byte {
2length := len(origData)
3// 去掉最后一个字节 unpadding 次
4unpadding := int(origData[length-1])
5return origData[:(length - unpadding)]
6}

2)、3DES

加密代码:

1// 3DES加密
2func TripleDesEncrypt(origData, key []byte) ([]byte, error) {
3block, err := des.NewTripleDESCipher(key)
4if err != nil {
5return nil, err
6}
7origData = PKCS5Padding(origData, block.BlockSize())
8// origData = ZeroPadding(origData, block.BlockSize())
9blockMode := cipher.NewCBCEncrypter(block, key[:8])
10crypted := make([]byte, len(origData))
11blockMode.CryptBlocks(crypted, origData)
12return 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:

1func NewCBCDecrypter(b Block, iv []byte) BlockMode {
2if len(iv) != b.BlockSize() {
3panic("cipher.NewCBCDecrypter: IV length must equal block size")
4}
5return (*cbcDecrypter)(newCBC(b, iv))
6}

此处之所有用panic而不是返回error,个人猜测,是由于目前发布的版本,该方法没有返回error,修改方法签名会导致兼容性问题,因此用panic了。

解密代码:

1// 3DES解密
2func TripleDesDecrypt(crypted, key []byte) ([]byte, error) {
3block, err := des.NewTripleDESCipher(key)
4if err != nil {
5return nil, err
6}
7blockMode := cipher.NewCBCDecrypter(block, key[:8])
8origData := make([]byte, len(crypted))
9// origData := crypted
10blockMode.CryptBlocks(origData, crypted)
11origData = PKCS5UnPadding(origData)
12// origData = ZeroUnPadding(origData)
13return origData, nil
14}

三、和其他语言交互:加解密

这次,我写了PHP、Java的版本,具体代码放在github上。这里说明一下,Java中,默认模式是ECB,且没有用”\0″填充的情况,只有NoPadding和PKCS5Padding;而PHP中(mcrypt扩展),默认填充方式是”\0″,而且,当数据长度刚好是block size的整数倍时,默认不会填充”\0″,这样,如果数据刚好是block size的整数倍且结尾字符是”\0″,会有问题。

综上,跨语言加密解密,应该使用PKCS5Padding填充。

完整代码 myblog_article_code

欢迎关注我的公众号:

polarisxu-qrcode-soso-s.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK