GO的DES/3DES加密算法的实现及通信透彻解析
前位语:
各位老铁万福金安,相信各位之所以找到这篇文章,肯定是因为项目遇到了关于des/des3的相关问题.不要着急,建议耐下性子花上半个小时,理解完本篇文章,你的问题将迎刃而解.
前景提要:
最近因工作需要,接触到des加密算法; 但是因为des加密容易被暴力破解,所以项目中用到的是3des加密算法,也就是des三重加密.
这里还有要提的是, 因为之前的项目是c++服务, 所以这里的情况就是加密方用的c++, 解密方用的是GO;所以这里我们也会讲到des在各个语言的通信.
一、DES介绍
DES(Data Encryption Standard)是对称加密算法, DES三个入口参数: key、data、mode.
key: 加密/解密的密钥,项目中自己设计的一串字符串. 因为des加密为对称加密, 所以加解密用的密钥key是同一个.
data: 加密时为明文数据(源数据). 解密时为密文数据, 需要将其还原.
mode: 工作模式. 是加密算法底层实现的模式, 按64位进行明文分组.这里大致介绍下两种工作模式.
MODE: ECB
-
概念
ECB(电子密本方式)就是将8个字节一段, 进行DES加密/解密得到密文/明文. 最后一段不足8个字节, 按照需求补足8个字节计算.之后按照顺序将计算所得的数据连在一起即可,各段数据之间互不影响.
这里值得一提的就是, 你加密/解密的数据可能 (总字节长度/8字节), 不能整除, 按8字节分段处理过程中, 会有一段剩余. 这个时候会有填充方式的概念(padding, 包括none,用’\0’填充,pkcs5padding或pkcs7padding), 下面介绍des多语言通信时会再介绍. -
特点
1 简单, 有利于并行计算,误差不会被传送.
2 不能隐藏明文的模式;在密文中出现明文消息的重复.
3 可能对明文进行主动攻击;加密消息块相互独立成为被攻击的弱点.
4 GO的DES默认隐藏了ECB模式,因为认为其不安全. 但是3des三重加密算法下安全性也是可靠的.小编这里就是老项目用c++做的3des的ECB模式加密, 所以用go的3des-ECB解密.
MODE: CBC
-
概念
CBC(密文分组链接方式)有向量的概念,它的实现机制使加密的各段数据有了联系.- 加密步骤
1 首先跟ecb方式一样, 按8字节进行数据分组, 最后一段不足的用padding方式补齐补位.
2 第一组数据与初始化向量异运算/后运算的结果进行DES加密得到第一组密文
3 第二组数据与第一组密文异运算/后运算进行DES加密,得到第二组密文,以此类推
4 拼接各组密文,得到密文结果 - 解密步骤
解密是加密的逆过程
- 加密步骤
-
特点
1 不容易主动攻击,安全性高于ECB,每个密文块都依赖上一个密文块,所以一个解密有问题就会影响所有密文块.
2 发送方和接收方除了key密钥之外, 还需要知道初始向量的值.
3 加密过程是串行的, 相对并行模式ECB较慢.
使用方法
go get -u github.com/forgoer/openssl
DES
密钥key: 必须为8字节(最好是8位英文+字母的组合)
DES-ECB:
openssl.DesECBEncrypt(src, key, openssl.PKCS7_PADDING)
openssl.DesECBDecrypt(src, key, openssl.PKCS7_PADDING)
DES-CBC:
openssl.DesCBCEncrypt(src, key, iv, openssl.PKCS7_PADDING)
openssl.DesCBCDecrypt(src, key, iv, openssl.PKCS7_PADDING)
注:
这里如果多语言进行通信, 需要注意的就是key为8字节, 一致即可, 补齐方式通信双方定义好.
二、3DES加密原理
简介
3DES(Triple DES)是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)块密码的通称. 原理就是对每个数据块进行了三次DES加密算法, 由于计算机能力增强, 原版DES密钥(key)的长度变得容易被暴力破解, 所以3DES通过一种相对简单的办法, 即增加密钥key值的长度(变为24字节)避免类似的攻击.
3DES是DES向AES过渡的加密算法. 具体实现如下:
> 假设:
==Encry()--代表加密
Decry()代表解密
k1,k2,k3代码24字节的密钥分为3段
src代表明文
c代表密文==
过程就是这样的:
3DES加密: c = Encryk3(Decryk2(Encryk1(src)) 加密(解密(加密))
3DES解密: src = Decryk1(Encryk2(Decryk3(c)) 解密(加密(解密))
使用方法
3DES
密钥key: 长度为24字节(最好是24位英文+字母的组合)
3DES-ECB:
openssl.Des3ECBEncrypt(src, key, openssl.PKCS7_PADDING)
openssl.Des3ECBDecrypt(src, key, openssl.PKCS7_PADDING)
3DES-CBC:
openssl.Des3CBCEncrypt(src, key, iv, openssl.PKCS7_PADDING)
openssl.Des3CBCDecrypt(src, key, iv, openssl.PKCS7_PADDING)
注: 这里很多小伙伴会说key不足24字节怎么办, 或者多出24字节怎么办, 这里说下
1.24位密钥,不足24位的右补0x00;
2.加密内容8位补齐,补齐方式为:少1位补一个0x01,少2位补两个0x02,…
3.本身已8位对齐的,后面补八个0x08。
下面是GO的key密钥不足24字节的补齐代码
func ZeroPadding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{0}, padding)
return append(ciphertext, padtext...)
}
如果超出24字节, 建议直接截取, 如果多语言通信, 建议双方商量好.
注意点:
-
在CBC模式下, 会有向量的问题也需要注意, 这里建议key值和初始向量用同一个值
-
需要注意的是,密钥长度必须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
三、多语言通信
-
了解概念
在多语言进行通信时, 应先了解几个概念:- 模式: 是CBC还是ECB, 加/解密及不同之处上面已经提到过
- key: 密钥, 如果是DES, 密钥长度必须为8字节, 3DES必须为24字节. 不足或超过, 用相同的填充方式填充或切片.
- iv: 初始向量, 只有在CBC模式才会用到此值, 建议跟key值相同.
- padding: 填充方式, src原始串在加密解密过程中是分组运算的, 8个字节一组, 所以是总长度/8, 这个时候可能会有一组不足8字节, 这里多语言情况下, 填充方式要一样. 还有数据长度刚好是8的整数倍时, 是否要额外填充
-
链接
这里小编找到了大佬写的其他语言和GO语言的版本. 具体代码看这里
GitHub -
这里说明一下,Java中,默认模式是ECB,且没有用”\0″填充的情况,只有NoPadding和PKCS5Padding;而PHP中(mcrypt扩展),默认填充方式是”\0″,而且,当数据长度刚好是block size的整数倍时,默认不会填充”\0″,这样,如果数据刚好是block size的整数倍且结尾字符是”\0″,会有问题。
综上,跨语言加密解密,应该使用PKCS5Padding填充.