对称加密和Base64编码¶
目录
缘起¶
前阵子在用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编码的基本概念在这里做个简单的讲解,让大家能从基本原理中理解这两个方法的正确使用方式。
对称加密¶
简单说:使用相同密钥进行加密解密叫做对称加密。
加密和解密的运算本质(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模式的一大漏洞,就是攻击者可以在不破解密文的情况下操纵明文。
使用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得到的字符串值即可。
Base64编码¶
再来说下Base64编码。
上面加密后的密文,通常会包含很多不可见字符,这样通常会对加密后的结果做一次编码的工作,这样就可以在各处使用了。
带着的问题¶
这里我遇到的一个问题,就是为什么要用Base64进行编码,我看还有Base32啊,为什么不用那个呢?
有了前面的经验,我认识到还是要去了解下Base64编码和Base32编码的本质是什么,才能判断。