对称加密和Base64编码
Posted 7rule
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对称加密和Base64编码相关的知识,希望对你有一定的参考价值。
http://blog.7rule.com/2018/03/09/crypto-base64.html
缘起
前阵子在用golang重构一段php逻辑,那段代码中用到了mcrypt_encrypt和base64_encode方法,大致如下:
base64_encode(mcrypt_encrypt(self::MCRYPT_CIPHER, self::MCRYPT_KEY, $str, self::MCRYPT_MODE, self::MCRYPT_IV))
看起来挺简单的php调用,使用golang重构时发现没那么简单。
无论是mcrypt_encrypt还是base64_encode,在golang中都无法很简单的用一个现成的方法调用,且看官方包文档时很多概念都搞不懂。
网上查看了一些资料,感觉理解的还不是很清楚,最终我从这两个方法的基本概念入手,简单学习了下,感觉清晰了很多,完成了重构。
本篇文章就是把这次学到的关于对称加密和base64编码的基本概念在这里做个简单的讲解,让大家能从基本原理中理解这两个方法的正确使用方式。
对称加密
简单说:使用相同密钥进行加密解密叫做对称加密。
带着的问题
加密和解密操作是如何实现的?
DES、AES是什么意思?AES-128、AES-256又是什么东西?如何选择?
加密模式是什么?多种加密模式应当如何选择?
初始向量是什么?
加密和解密的运算本质(XOR)
现代加解密运算都是计算机运算,也就是0和1的位运算,对称加密中,最重要的当属XOR运算。
XOR也叫做异或运算,规则如下:
0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0
如果是比特序列之间的XOR运算,只需要对其中每个对应的比特进行XOR运算即可,例:
0 1 0 0 1 1 0 0 A
1 0 1 0 1 0 1 0 B
---------------------------------
1 1 1 0 0 1 1 0 A XOR B
这里就有了个有趣的地方:
0 1 0 0 1 1 0 0 A
1 0 1 0 1 0 1 0 B
---------------------------------
1 1 1 0 0 1 1 0 A XOR B
1 0 1 0 1 0 1 0 B
---------------------------------
0 1 0 0 1 1 0 0 A
即:
A XOR B XOR B = A
如果A是明文,B是密钥,那么 A XOR B = crypted
即实现了加密, crypted XOR B = A
即实现了解密。
分组加密的概念
这里用一个最简单的示例来说明基本概念。
上面大家看到了,加密需要进行XOR
运算,这就需要运算双方的长度相同,也就是明文和密钥的长度是相同的。
但是,实际通常要加密的明文长度很长,密钥通常是相对固定的,那么如何做呢?
这就需要把明文按照密钥的长度进行分组,即分成多个明文块(block)
,然后对每个block
分别和密钥进行迭代的XOR
运算,形成最终的密文,其中迭代的方式就称为模式(mode)
。
上面描述的就是最简单的分组加密的概念,实际中的算法会复杂很多,这些算法,就是所谓的DES、3DES、AES
等。
阶段小结
通过上面的解释,相信大家已经了解了对称加密的基本原理。
DES
本质上就是一种将64bit的明文加密成64bit的密文
的对称加密算法,但这种算法现在已经被认为是不安全的,所以后来又出现了3DES
,它对DES具备向下兼容性。
AES
则是为了取代DES而生的,它通过公开精选,最终选定了一个叫做Rijndael
的分组密码算法。上面提到的AES-128、AES-256
,即是指密钥的长度。
模式
模式指的就是分组加密的迭代方式,主要有如下几种:
ECB
CBC
CFB
OFB
CTR
先给个结论:
绝对不要使用
ECB
,这个模式是非常不安全的。现在使用最多的,就是
CBC
,这也是TLS
中在使用的模式。
ECB
ECB模式将明文分组加密之后的结果直接作为密文结果,如下图:
这个模式最大的问题,就是相同的明文分组会转换为相同的密文分组。因为只要观察一下密文,就可以知道明文中存在怎样的重复组合,并可以借此攻击。
举个例子,假如A要向B转账100元,数据由3个分组构成:
付款人A的账号
收款人B的账号
转账金额
ECB加密后的密文简单示例如下:
59 7D DE CC
DF 49 2A 1C
CD AF D5 9E
假如攻击者将1和2的内容进行调换,则变为:
DF 49 2A 1C
59 7D DE CC
CD AF D5 9E
这样一来,就变为了B向A转账100元。
ECB模式的一大漏洞,就是攻击者可以在不破解密文的情况下操纵明文。
CBC
CBC的全称是Cipher Block Chaining
(密文分组链接),正如其名,密文分组会像链条一样相互连接起来。
CBC模式首先将明文分组与前一个密文分组进行XOR运算,然后再进行加密,就这样一直加密完所有分组。
这里出现了初始化向量(IV)
这个概念:当加密第一个明文分组时,由于不存在“前一个密文分组”,因此就需要一个长度为一个分组的比特序列来代替,这个比特序列就叫做初始化向量(IV),ECB中不需要。
CBC模式现在是使用最广泛的对称加密模式,TLS协议(https中使用)中即使用此模式。
使用golang重构php的mcrypt_encode
我们这里使用AES的CBC模式,这里会涉及到3个重要的值:密钥(key)
、初始化向量(iv)
、要加密的明文(data)
。
密钥key
key的长度,可选16、24、32字节
,这是为了对应AES-128、AES-192、AES-256
。
AES-256
的复杂度最高,所以我们最好选择这个,这样就需要key的长度是32字节
,有个最好的办法就是对自定义的key做md5得到的字符串值即可。
初始化向量iv
iv的长度,需要和分组大小相同,AES的分组大小是128bit
,即16字节
。我们这里可以对自定义的iv做md5后得到的字符串值取前16个字节即可。
明文data
明文会被自动分组,为了能让每个分组的长度固定,就需要先对明文进行数据填充,再进行分组加密操作。这里我们使用最常用的一种数据分组填充方式PKCS5Padding
AES代码:https://github.com/goinbox/crypto/blob/master/aes.go
PKCS5Padding代码:https://github.com/goinbox/crypto/blob/master/padding.go
Demo
package main
import (
"github.com/goinbox/crypto"
)
func main() {
key := crypto.Md5String([]byte("gobox")) //The key argument should be the AES key, either 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
iv := crypto.Md5String([]byte("goinbox"))[:crypto.AES_BLOCK_SIZE] //The length of iv must be the same as the Block's block size
data := []byte("abc")
acc, _ := crypto.NewAesCBCCrypter([]byte(key), []byte(iv))
crypted := acc.Encrypt(data)
d := acc.Decrypt(crypted)
if string(d) != string(data) {
println("crypto error")
} else {
println("crypto success")
}
}
是不是发现原本php一个方法做的事情,到了golang中需要做这么多的事。
很多高级语言都做了很多的封装,让大家不必了解过多的细节即可使用,但同时我们也失去了学习更多东西的机会。
现在大家可以再回过头看看之前自己用过的对称加密方法,对其中的参数是不是就理解了,再想想看是否之前有使用不当的地方,笔者就发现了之前一个重要的业务中使用了ECB模式,赶紧改了,呵呵。
Base64编码
再来说下Base64编码。
上面加密后的密文,通常会包含很多不可见字符,这样通常会对加密后的结果做一次编码的工作,这样就可以在各处使用了。
带着的问题
这里我遇到的一个问题,就是为什么要用Base64进行编码,我看还有Base32啊,为什么不用那个呢?
有了前面的经验,我认识到还是要去了解下Base64编码和Base32编码的本质是什么,才能判断。
详解
Base64编码使用64个字符来对任意数据进行编码,同理Base32编码会使用32个字符对任意数据进行编码。
Base64编码的64个字符为:
编码的过程,先将数据转成二进制形式,然后每6bit(2的6次方=64)
计算十进制值,再在上面的对应表中转换为对应字符,最终得到一个字符串,示例:
最后的那些0,是需要进行补齐填充的bit。另外,标准Base64会使用=
来替代A
,这是因为=
不在索引表中,可以作为结束符存在。
小结
使用Base64编码后的数据长度会增加1/3,Base32因为要使用更少的字符,所以编码后的长度要增加3/5。
有此可见,Base64在某种程度上来说兼顾了字符集大小和编码后数据长度,所以它的应用场景也更加广泛。
缘起
图解密码技术:https://book.douban.com/subject/26265544/
Base64编码原理与应用:http://blog.xiayf.cn/2016/01/24/base64-encoding/
以上是关于对称加密和Base64编码的主要内容,如果未能解决你的问题,请参考以下文章