Go加密解密之DES

Wesley13
• 阅读 758

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

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

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

2、crypto/cipher包

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

type Block interface { // BlockSize returns the cipher's block size. BlockSize() int // Encrypt encrypts the first block in src into dst. // Dst and src may point at the same memory. Encrypt(dst, src []byte) // Decrypt decrypts the first block in src into dst. // Dst and src may point at the same memory. Decrypt(dst, src []byte) }

这是一个接口

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

type BlockMode interface { // BlockSize returns the mode's block size. BlockSize() int // CryptBlocks encrypts or decrypts a number of blocks. The length of // src must be a multiple of the block size. Dst and src may point to // the same memory. CryptBlocks(dst, src []byte) }

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

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

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

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

type Stream interface { // XORKeyStream XORs each byte in the given slice with a byte from the // cipher's key stream. Dst and src may point to the same memory. XORKeyStream(dst, src []byte) }

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

这里,我们只讨论CBC模式

3、加密解密

1)DES
DES加密代码如下:

func DesEncrypt(origData, key []byte) ([]byte, error) { block, err := des.NewCipher(key) if err != nil { return nil, err } origData = PKCS5Padding(origData, block.BlockSize()) // origData = ZeroPadding(origData, block.BlockSize()) blockMode := cipher.NewCBCEncrypter(block, key) crypted := make([]byte, len(origData)) // 根据CryptBlocks方法的说明,如下方式初始化crypted也可以 // crypted := origData blockMode.CryptBlocks(crypted, origData) return crypted, nil }

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

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

可见,数据长度刚好是block size的整数倍时,也进行了填充,如果不进行填充,unpadding会搞不定。
另外,为了方便,初始向量直接使用key充当了。

DES解密代码如下:

func DesDecrypt(crypted, key []byte) ([]byte, error) { block, err := des.NewCipher(key) if err != nil { return nil, err } blockMode := cipher.NewCBCDecrypter(block, key) origData := make([]byte, len(crypted)) // origData := crypted blockMode.CryptBlocks(origData, crypted) origData = PKCS5UnPadding(origData) // origData = ZeroUnPadding(origData) return origData, nil }

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

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

2)、3DES

加密代码:

// 3DES加密 func TripleDesEncrypt(origData, key []byte) ([]byte, error) { block, err := des.NewTripleDESCipher(key) if err != nil { return nil, err } origData = PKCS5Padding(origData, block.BlockSize()) // origData = ZeroPadding(origData, block.BlockSize()) blockMode := cipher.NewCBCEncrypter(block, key[:8]) crypted := make([]byte, len(origData)) blockMode.CryptBlocks(crypted, origData) return crypted, nil }

对比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:

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

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

解密代码:

// 3DES解密 func TripleDesDecrypt(crypted, key []byte) ([]byte, error) { block, err := des.NewTripleDESCipher(key) if err != nil { return nil, err } blockMode := cipher.NewCBCDecrypter(block, key[:8]) origData := make([]byte, len(crypted)) // origData := crypted blockMode.CryptBlocks(origData, crypted) origData = PKCS5UnPadding(origData) // origData = ZeroUnPadding(origData) return origData, nil }

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

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

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

点赞
收藏
评论区
推荐文章
科工人 科工人
3年前
golang - DES加密ECB(模式)
Java默认DES算法使用DES/ECB/PKCS5Padding,而golang认为这种方式是不安全的,所以故意没有提供这种加密方式,那如果我们还是要用到怎么办?下面贴上golang版的DESECB加密解密代码(默认对密文做了base64处理)。
Wesley13 Wesley13
2年前
java实现非对称加密
对称加密:加密和解密的过程使用的是相同的密钥!这里写图片描述(https://oscimg.oschina.net/oscnet/42e81282a912d5abcf561e846c2b997914e.png)非对称加密与对称加密不同,非对称加密算法的加密和解密使用不同的两个密钥.这两个密钥就是我们经常听到的”公开密钥”(公钥
Wesley13 Wesley13
2年前
DES与RSA加解密
加密,是以某种特殊的算法改变原有的信息数据,使得未授权的用户即使获得了已加密的信息,但因不知解密的方法,仍然无法了解信息的内容。大体上分为双向加密 和单向加密 ,而双向加密又分为对称加密 和非对称加密(有些资料将加密直接分为对称加密和非对称加密)。 双向加密大体意思就是明文加密后形成密文,可以通过算法还原成明文。而
Wesley13 Wesley13
2年前
DES加密解密
DES加密解密涉及到的JAVA类Cipher此类为加密和解密提供密码功能。它构成了JavaCryptographicExtension(JCE)框架的核心。为创建Cipher对象,应用程序调用Cipher的getInstance方法并将所请求转换的名称传递给它。还可
Wesley13 Wesley13
2年前
JAVA加密算法(1)
密码学综述密码学基本功能机密性、鉴别、报文完整性、不可否认性基本模型sender加密算法密文解密算法receiver密钥源密码学算法分类:消息编码:Base64消息摘要:MD类,SHA类,MAC对称加密:DES,3DES,AES
Wesley13 Wesley13
2年前
JAVA_RSA_的加解密
RSA为非对称加密算法。数字签名的过程:1、对明文数据进行HASH加密,不可逆;2、对加密后的数据再用RSA的私钥进行二次加密。数字签名的验证过程:1、对明文数据进行HASH加密,不可逆;2、用RSA的公钥对数字签名后的数据进行解密;3、把1的结果和2的结果进行比较是否相等。RSA加密的过程和解密的过程都需要三步:加/解密、分组、填充。这三部分每
Stella981 Stella981
2年前
DESEncrypt 对称可逆加密
usingSystem;usingSystem.Security.Cryptography;usingSystem.Text;usingSystem.Web.Security;namespaceDESEncrypt{///<summary///DES加密/解密类。///</summary
Wesley13 Wesley13
2年前
99. 加密 解密 脱敏
1.加密解密1.1  DES加密解密工具类1.1.1效果!(https://oscimg.oschina.net/oscnet/upa4f71ba60f1d1d22042e819f112cb0a228c.png)1.1.2代码packagecn.ma.life.
Stella981 Stella981
2年前
Linux下DES安全通信编程
des.h///文件名:des.h/功能: 实现DES加密算法的加密解密功能    for linux/
Wesley13 Wesley13
2年前
VC++网络安全编程范例(2)
数字证书采用公钥体制,即利用一对互相匹配的密钥进行加密、解密。每个用户自己设定一把特定的仅为本人所知的私有密钥(私钥),用它进行解密和签名;同时设定一把公共密钥(公钥)并由本人公开,为一组用户所共享,用于加密和验证签名。当发送一份保密文件时,发送方使用接收方的公钥对数据加密,而接收方则使用自己的私钥解密,这样信息就可以安全无误地到达目的地了。通过数字的手段