一、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
2 |
// BlockSize returns the cipher's block size. |
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) |
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) |
这是一个接口
对称加密,按块方式,我们经常见到CBC、ECB之类的,这些是加密模式。可以参考:DES加密模式详解 http://linux.bokee.com/6956594.html
Go中定义了一个接口BlockMode代表各种模式
1 |
type BlockMode interface { |
2 |
// BlockSize returns the mode's block size. |
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 |
8 |
CryptBlocks(dst, src []byte) |
该包还提供了获取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) |
同样也提供了获取实现该接口的实例
这里,我们只讨论CBC模式
3、加密解密
1)DES
DES加密代码如下:
1 |
func DesEncrypt(origData, key []byte) ([]byte, error) { |
2 |
block, err := des.NewCipher(key) |
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) |
以上代码使用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...) |
可见,数据长度刚好是block size的整数倍时,也进行了填充,如果不进行填充,unpadding会搞不定。
另外,为了方便,初始向量直接使用key充当了(实际项目中,最好别这么做)。
DES解密代码如下:
1 |
func DesDecrypt(crypted, key []byte) ([]byte, error) { |
2 |
block, err := des.NewCipher(key) |
6 |
blockMode := cipher.NewCBCDecrypter(block, key) |
7 |
origData := make([]byte, len(crypted)) |
9 |
blockMode.CryptBlocks(origData, crypted) |
10 |
origData = PKCS5UnPadding(origData) |
11 |
// origData = ZeroUnPadding(origData) |
可见,解密无非是调用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)] |
2)、3DES
加密代码:
2 |
func TripleDesEncrypt(origData, key []byte) ([]byte, error) { |
3 |
block, err := des.NewTripleDESCipher(key) |
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) |
对比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" ) |
5 |
return (*cbcDecrypter)(newCBC(b, iv)) |
此处之所有用panic而不是返回error,个人猜测,是由于目前发布的版本,该方法没有返回error,修改方法签名会导致兼容性问题,因此用panic了。
解密代码:
2 |
func TripleDesDecrypt(crypted, key []byte) ([]byte, error) { |
3 |
block, err := des.NewTripleDESCipher(key) |
7 |
blockMode := cipher.NewCBCDecrypter(block, key[:8]) |
8 |
origData := make([]byte, len(crypted)) |
10 |
blockMode.CryptBlocks(origData, crypted) |
11 |
origData = PKCS5UnPadding(origData) |
12 |
// origData = ZeroUnPadding(origData) |
三、和其他语言交互:加解密
这次,我写了PHP、Java的版本,具体代码放在github上。这里说明一下,Java中,默认模式是ECB,且没有用”\0″填充的情况,只有NoPadding和PKCS5Padding;而PHP中(mcrypt扩展),默认填充方式是”\0″,而且,当数据长度刚好是block size的整数倍时,默认不会填充”\0″,这样,如果数据刚好是block size的整数倍且结尾字符是”\0″,会有问题。
综上,跨语言加密解密,应该使用PKCS5Padding填充。
- C# 加密解密(DES,3DES,MD5,Base64) 类
public sealed class EncryptUtils { #region Base64加密解密 /// <summary> ...
- 各种加密解密函数(URL加密解密、sha1加密解密、des加密解密)
原文:各种加密解密函数(URL加密解密.sha1加密解密.des加密解密) 普通hash函数如md5.sha1.base64等都是不可逆函数.虽然我们利用php可以利用这些函数写出可逆函数来.但是跨语 ...
- C#加密解密(DES,AES,Base64,md5,SHA256,RSA,RC4)
一:异或^简单加解密(数字类型) 1:原理: 异或用于比较两个二进制数的相应位,在执行按位"异或"运算时,如果两个二进制数的相应位都为1或者都为0,则返回0;如果两个二进制数的相应 ...
- C# 常用加密解密帮助类
public static class EncryptUtil { #region MD5加密 /// <summary> /// MD5加密 /// </summary> p ...
- C# Java 加密解密
C# AES加密解密 public static string Encrypt(string key, string clearText) { byte[] clearBytes = Encoding ...
- C# 加密(Encrypt) 解密(Decrypt) 操作类 java与 C# 可以相互加密解密
public sealed class EncryptUtils { #region Base64加密解密 /// <summary> /// Base64加密 /// </summ ...
- C#加密解密总览
C#SHA加密 C#MD5加密 C#RSA加密解密 C#DES加密和解密 C#AES加密和解密
- .NET和JAVA中BYTE的区别以及JAVA中“DES/CBC/PKCS5PADDING” 加密解密在.NET中的实现
场景:java 作为客户端调用已有的一个.net写的server的webservice,输入string,返回字节数组. 问题:返回的值不是自己想要的,跟.net客户端直接调用总是有差距 分析:平台不 ...
- php使用openssl进行Rsa长数据加密(117)解密(128) 和 DES 加密解密
PHP使用openssl进行Rsa加密,如果要加密的明文太长则会出错,解决方法:加密的时候117个字符加密一次,然后把所有的密文拼接成一个密文:解密的时候需要128个字符解密一下,然后拼接成数据. 加 ...
随机推荐
- ELF Format 笔记(七)—— 符号表
最是那一低头的温柔,像一朵水莲花不胜凉风的娇羞,道一声珍重,道一声珍重,那一声珍重里有蜜甜的忧愁 —— 徐志摩 ilocker:关注 Android 安全(新手) QQ: 2597294287 符号表 ...
- Redis-benchmark测试Redis性能
Redis-benchmark是官方自带的Redis性能测试工具,可以有效的测试Redis服务的性能. 使用说明如下: Usage: redis-benchmark [-h <host>] ...
- html5语法
html5语法沿用html语法,但更简单,更人性化. 一.DOCTYPE及字符编码 DOCTYPE:<!doctype html>或者<!DOCTYPE html>因为html ...
- HDU 2516 取石子游戏
Problem Description 1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍.取完者胜.先取者负输出"Secon ...
- Objective-C声明在头文件和实现文件中的区别
Objective-C声明在头文件和实现文件中的区别 转自codecloud(有整理) 调试程序的时候,突然想到这个问题,百度一下发现有不少这方面的问答,粗略总结一下: 属性写在.h文件中和在.m文件 ...
- HDU 4630 No Pain No Game 树状数组+离线操作
题意:给一串数字,每次查询[L,R]中两个数的gcd的最大值. 解法:容易知道,要使取两个数让gcd最大,这两个数最好是倍数关系,所以处理出每个数的所有倍数,两两间根据倍数关系形成一条线段,值为该数. ...
- Codeforces 549C. The Game Of Parity[博弈论]
C. The Game Of Parity time limit per test 1 second memory limit per test 256 megabytes input standar ...
- HDU1502/Luogu1352/UVa1220 party[树形DP]
题目描述 某大学有N个职员,编号为1~N.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司.现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri, ...
- 嵌入式Linux驱动学习之路(六)u-boot启动内核
内核启动是需要必要的启动参数.不能开机自动完全从0开始启动,需要uboot帮助内核实现重定位并提供参数. 首先,uboo会从Kernel分区中读取bootcmd环境变量,根据环境变量可自动启动. 分区 ...
- java 27 - 8 反射之 通过反射来设置某个对象的某个属性为指定值
标题的意思是: 通过反射写一个类,类中有个方法.该方法可以设置某个类中的某个属性(构造方法,成员变量,成员方法)为特定的值 代码: // 该方法的参数列表是一个类的 类名.成员变量.给变量的赋值 pu ...