Go加密解密之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, 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填充。
转载于:https://www.cnblogs.com/mafeng/p/6208296.html
Go加密解密之DES相关推荐
- C# 加密解密(DES,3DES,MD5,Base64) 类
public sealed class EncryptUtils{#region Base64加密解密/// <summary>/// Base64加密/// </summary&g ...
- react实现RSA加密解密、DES加密
前端实现RSA加密解密.DES解密,主要用到的包有jsencrypt.crypto-js 一.使用场景 需要在本地存储一些标志位,安全问题 用户登录密码的加密 二.加密代码 RSA加密 安装 npm ...
- 各种加密解密函数(URL加密解密、sha1加密解密、des加密解密)
普通hash函数如md5.sha1.base64等都是不可逆函数.虽然我们利用php可以利用这些函数写出可逆函数来.但是跨语言时这类可逆函数非常难搞定.所以这时尽量使用AES DES RC4 Rabb ...
- des vue 加密解密_vue DES 加密
ECB模式 import cryptoJs from 'crypto-js' // DES加密 export const encryptDes = (message, key) => { var ...
- C#之DES加密解密
一.DES简介 在线DES加密/解密工具 DES算法是一种最通用的对称算法,因为算法本身是公开的,所以其安全性在于的安全性.基于的算法通常有两类:对称算法和公开算法.对称算法的对称性体现在加能够从解推 ...
- php ecb加密,PHP之DES加密解密算法类(ECB模式)(实例教程)
PHP中加密解密函数与DES加密解密的应用实例:<?php define('SMS_KEY', 'K0e5293b'); class DesUtil{ public function encry ...
- C#常用加密解密方法(DES加密解密)
在日常开发过程中,总会遇到需要加密解密的需求,这里我整理了C#常用的加密解密方法分享给大家. 先看看加密的基本概念: "加密",是一种限制对网络上传输数据的访问权的技术.原始数据( ...
- C#加密解密(DES,AES,Base64,md5,SHA256,RSA,RC4)
一:异或^简单加解密(数字类型) 1:原理: 异或用于比较两个二进制数的相应位,在执行按位"异或"运算时,如果两个二进制数的相应位都为1或者都为0,则返回0;如果两个二进制数的相应 ...
- Java简单实现DES加密解密算法
Java简单实现DES加密解密算法 文章目录 Java简单实现DES加密解密算法 DES算法介绍 实现相关java类 代码实现 DES算法介绍 DEC加密算法属于对称加密,即利用指定的密钥,按照密码的 ...
最新文章
- 阿里前员工跳槽后曝光薪资截图:新公司月入五万多,很满足!
- java 静态 编译_Java中的动态和静态编译实例详解
- 量化派基于Hadoop、Spark、Storm的大数据风控架构--转
- Spring集成web环境(手动实现)
- boost::mp11::mp_clear相关用法的测试程序
- 微服务Dubbo和SpringCloud架构设计、优劣势比较
- Kubernetes持久化存储Cephfs
- 【AI面试题】什么是数据不平衡,如何解决
- 跨国际日期分界线的解决方法
- [WebAPI] - 使用 Ajax 提交 HTML Form Data 到 WebAPI 的方法
- [Linux] - 网速测试命令
- 双管理人模式下的契约型私募基金实用发行指南
- 【图像处理】基于matlab GUI数字图像处理平台【含Matlab源码 381期】
- 【pandas教程】索引操作
- AR工业应用|企业中使用的7个增强现实创新案例|effiarAR工业云平台
- kmplayer音轨切换(换配音)
- 图像存储的地理坐标位置信息
- STM32CubeMX基于HAL库实现简单串口通信
- 苹果Arm芯片适配开发 (Apple Silicon)
- 直逼 Flash 的流畅感:jQuery 运动特效展示