一、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, key []byte) ([]byte, error) {
2      block, err := des.NewCipher(key)
3      if err != nil {
4           return nil, 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 PKCS5UnPadding(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, key []byte) ([]byte, 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, key []byte) ([]byte, error) {
3      block, err := des.NewTripleDESCipher(key)
4      if err != nil {
5           return nil, 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加密解密之DES的更多相关文章

  1. C# 加密解密(DES,3DES,MD5,Base64) 类

    public sealed class EncryptUtils     {         #region Base64加密解密         /// <summary>        ...

  2. 各种加密解密函数(URL加密解密、sha1加密解密、des加密解密)

    原文:各种加密解密函数(URL加密解密.sha1加密解密.des加密解密) 普通hash函数如md5.sha1.base64等都是不可逆函数.虽然我们利用php可以利用这些函数写出可逆函数来.但是跨语 ...

  3. C#加密解密(DES,AES,Base64,md5,SHA256,RSA,RC4)

    一:异或^简单加解密(数字类型) 1:原理: 异或用于比较两个二进制数的相应位,在执行按位"异或"运算时,如果两个二进制数的相应位都为1或者都为0,则返回0;如果两个二进制数的相应 ...

  4. C# 常用加密解密帮助类

    public static class EncryptUtil { #region MD5加密 /// <summary> /// MD5加密 /// </summary> p ...

  5. C# Java 加密解密

    C# AES加密解密 public static string Encrypt(string key, string clearText) { byte[] clearBytes = Encoding ...

  6. C# 加密(Encrypt) 解密(Decrypt) 操作类 java与 C# 可以相互加密解密

    public sealed class EncryptUtils { #region Base64加密解密 /// <summary> /// Base64加密 /// </summ ...

  7. C#加密解密总览

    C#SHA加密 C#MD5加密 C#RSA加密解密 C#DES加密和解密 C#AES加密和解密

  8. .NET和JAVA中BYTE的区别以及JAVA中“DES/CBC/PKCS5PADDING” 加密解密在.NET中的实现

    场景:java 作为客户端调用已有的一个.net写的server的webservice,输入string,返回字节数组. 问题:返回的值不是自己想要的,跟.net客户端直接调用总是有差距 分析:平台不 ...

  9. php使用openssl进行Rsa长数据加密(117)解密(128) 和 DES 加密解密

    PHP使用openssl进行Rsa加密,如果要加密的明文太长则会出错,解决方法:加密的时候117个字符加密一次,然后把所有的密文拼接成一个密文:解密的时候需要128个字符解密一下,然后拼接成数据. 加 ...

随机推荐

  1. Fatal error: Call-time pass-by-reference has been removed

    下面的代码报错:Fatal error: Call-time pass-by-reference has been removed function myFunc($arg) { do somethi ...

  2. php实现SESSION跨域

    稍微大一点的网站,通常都会有不只一个服务器,每个服务器运行着不同的功能模块或者不同的子系统,他们使用不同的二级域名,比如www.a.com.i.a.com.bbs.a.com.而一个整体性强的网站,用 ...

  3. AngularJS基础概念

    作用域.控制器.指令 作用域 应用的作用域是和应用的数据模型相关联的,同时作用域也是表达式执行的上下文.$scope对象是定义应用业务逻辑.控制器方法和视图属性的地方. 作用域是应用状态的基础.基于动 ...

  4. 基于JAVA的全国天气预报接口调用示例

    step1:选择本文所示例的接口"全国天气预报接口" url:https://www.juhe.cn/docs/api/id/39/aid/87step2:每个接口都需要传入一个参 ...

  5. 准备使用 Office 365 中国版--邮箱迁移

    微软产品一贯的作风是从来不缺文档和教程,Office 365也不例外.无论是最终用户还是企业IT管理员,都可参照Office 365使用指南顺利的开启Office 365之旅.不过比较奇怪的是,貌似很 ...

  6. git 学习使用总结一(本地操作)

    通过几天的学习,熟悉了 git 的一些常用命令,要用熟练和操作更复杂的功能还必须继续学习.不过 git 作为工具,它是用来提高我们的工作效率的工具,系统的学习之后可以暂且放放,等到以后实际项目中用到了 ...

  7. BZOJ1015[JSOI2008]星球大战starwar[并查集]

    1015: [JSOI2008]星球大战starwar Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 5253  Solved: 2395[Submit ...

  8. Unity arm64

    ERROR ITMS-90086 ERROR ITMS-90086:"missing 64-bit support. beginning on february 1, 2015, new i ...

  9. [No00002C]人的寿命应该能达到100至175岁-北大齐教授健康讲座笔录

      人的寿命应该能达到100 至175 岁,为什么都没有达到呢?最主要一个原因就是我们不重视保健,不懂得保健的知识.很多人死于无知,这很冤枉啊! 大家知道怎么保健吗?国际上有个维多利亚宣言,宣言里有三 ...

  10. $.data(data , "")

    今天在二次开发的时候,看到源代码的新闻列表是Aajax获取的,点击新闻内容触发编辑,我没有看到新闻Id却能查到信息. 观看$.ajax遍历赋值过程中,$tr("<a>新闻内容&l ...