浅谈加密算法
前言
本文只涉及加密算法认识与使用,不涉及加密算法的源码分析与加密原理。(因为本人自己也看不懂源码,但是会用真就足够了,就算让我写一个这样的算法,给我源码也不会写,何况还是开源的)
本人并非密码学专家,但接触过 JS 逆向和安卓 Java 层,对一些加密算法也有所了解,借此来分享一下自己所接触过的常见加密算法与使用。
涉及到的常用的加密算法有
- 消息摘要算法
- MD5
- SHA1,SHA3,SHA256...
- HmacMD5,HmacSHA1,HmacSHA256...
- 对称加密算法
- DES
- 3DES(也称 TripleDES,DESede)
- AES
- 非对称加密算法
- RSA
编码
涉及到加密算法,必须要涉及到编码格式,主要涉及到的编码方式编码有以下几种
UTF-8
针对 Unicode 的一种可变长度字符编码。它可以用来表示 Unicode 标准中的任何字符,而且其编码中的第一个字节仍与ASCII相容,使得原来处理 ASCII 字符的软件无须或只进行少部份修改后,便可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。
GBK (gb2312)
GBK 即“国标” ,汉字编码的标准编码字库。
上述两者的区别
-
表示中文的所占字节不同
同样表示一个中文字符,gbk 所占 2 字节,而 utf8 占 3 字节,通俗点就是如果你的项目代码涉及的都是中文这些,不会有希腊文,韩文等等,那么优先 gbk 编码,因为字节少,占用空间少。但如果涉及到更广的语言,那么 uf8 无疑是首选的。一般来说 Unicode 标准中 utf8 已经够用了,在编写代码中多数环境也是再 utf8 的标准上。总之基本 utf8 就完事了。
如需更深入了解可自行百度相关编码知识,本文只做与加密算法相关。
Base64
-
所有的数据都能被编码为只用 65 个字符就能表示的文本。标准的 Base64 每行为 76 个字符,每行末尾添加一个回车换行符(\r\n)。base 是可以互相转化的
-
65 字符:A
Z az 0~9 + / =在 URL Base64 算法中,为了安全,会把 + 替换成 - ,把 / 替换成 _
= 有时候用 ~ 或 . 代替(了解即可)
-
Base64 的应用
密钥、密文、图片、数据简单加密或者预处理
例如下面这些数据 通过链接 base64 图片在线转化即可 base64 编码数据与图片互相转化
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAMbSURBVDhPARAD7/wAbcLpc8Poc7nbcbbVecPggrzQyN3g9/z1+P7yx9rUlr7Ipc/fwNHh8vn/+///3eLeAGy84W+73aPl/6Th/YK80IWvu+b18v3989/h04uXi3aTkb7a3tnj7L3Axe3z8cPJxQB/xON5u9ub2PWczOKDoKSNmJCkopWxqpiyq5izsZqtsJWPkn2al5CSkY3N0sz1//cAu+r8q9jri7TGlrO7q7Oov7qnv7ekyMCrzMSvz8iuycOjqqOHX1lNNTItMzgyYGlkAPT//9/r57jBvMLEuczHs8rCq87Gsc3Gs7q1oqKdioJ/bHVzZF5fWS4wLSIkIystLADe39HW1MXUzbvDu6jKwq29tqOmoJJ8eGx+fG+AfnKTk4dpbGOfpJ6HhYYmFR1ONkMAubutvLqropuLqKGRko6DcXBrPDw6PT07kZKMkpOL6+7l0NPKo6SftqWrfEhesm2KAJaajDg6LWdjV4SAdYWEgJCSkYuPkldbXKWnpuPl4tXa1K+xrJiXk4ZzeaZyiNmUswAyPDNLUkqio52ko56uramZmpVvdHCssa2oqKiRkZOHiIqChIONjIq1qq7Fsbz/9P8AKiQklZGQ///9///9/v37+fj2foB70NLNk5OTrq6wnp6gqqqs4+Lgz87MmpibvbvAAD8ZJodsdf/6/P7////9///9//v5+t/e3PT08vz7+f/+//78/f7/+19hXBkZGTMzNQCbdYqHbHvCvMD3+Pr59/zx7PPs5+v//P3///37+/nw7+3s6+lxc24RExAlJigyMjQA19jt0tHhqqmvycnH6Ofj9fHw+PL28+7029/gyM7M5+flsbCuKCYpISAlIyInMTI2AMDX6eT2//f//+js7evs5+no5Obi4/79//z//97k5P7//////6imqQIBBhARFSMkKADP7fjW8v3d9Pzi8fb7///+//3q7+vq7u3V2drEyMvm5ujQ0NKio6VlaWp1d3aGiIcA0fP8xebvu9jg1Ojv9Pz/+//+8vj02N7a19vc6+zw2djd1NXX+Pz9+///9/n27e/sqToTd5KpZ2kAAAAASUVORK5CYII=
-
浏览器内置 Base64 编码(btoa) 解码(atob)
Hex
二进制数据最常用的一种表示方式。用 0-9 a-f 16 个字符表示。每个十六进制字符代表 4bit。也就是2个十六进制字符代表一个字节。如a12345678
用 md5 加密的结果为 32 位 0-9a-f 字符e9bc0e13a8a16cbb07b175d92a113126
每 2 个十六进制字符为一个字节,32 位字符也就是 16 个字节。
在实际应用中,尤其在密钥初始化的时候,一定要分清楚自己传进去的密钥是哪种方式编码的,采用对应方式解析,才能得到正确的结果。
单向散列函数(消息摘要算法,哈希算法)
- MD5
- SHA1,SHA3,SHA256...
- HmacMD5,HmacSHA1,HmacSHA256...
先说最简单的也是用的最多的算法,性质如下
- 不管明文多长,散列后的密文定长
- 明文不一样,散列后结果一定不一样
- 散列后的密文不可逆
- 一般用于校验数据完整性、签名、sign
你只需要需要性质就行,下文会举实例。
由于密文不可逆,所以后台无法还原,也就是说他要验证,会在后台以跟前台一样的方式去重新签名一遍。也就是说他会把源数据和签名后的值一起提交到后台。常用于校验数据完整性、签名、sign
比如我有一篇毕业论文,我写的差不多了,然后去厕所回来,看到我的一个室友坐在我电脑前,我该如何知道他是否有更改过我的毕业论文。这是消息摘要算法就能解决这个问题,在你走之前将论文取 MD5(后面例子也都以 MD5 为例),然后去厕所完,再取一次 MD5 的值,将两者一比对,只要修改了一个字符或者添加了一个空格,两者的 MD5 值都完全不一样,基本差别巨大。也就可以知道你的论文有没有被改了,但是被改了你也没有办法还原回去,然后你就毕业不了了。
由于固定原文加密后的密文是固定的,理论上只要我一个一个字符试过去,将结果与密文对比,相同的话就可以知道原文。那么可以将这些原文和密文存入对应的数据库里,在查加密后的密文后去数据库找原文,如彩虹表就是专门暴力破解这种算法的(相关链接 什么是彩虹表)。防止通过彩虹表破解的话就需要对原文在做一次操作----加盐。加盐可以理解为就是添加了一些字符串,例如上面说到kuizuo
这个字符串 通过 MD5 算法后得到的结果是ff1fa96799ded9ee89d0f764b3e9ff54
这就是不加盐 MD5 返回的结果,万一我在kuizuo
后面加一个!
后呢,结果为7e74121af78b9555241fdf6538e2f22b
,可以看到两者完全天壤之别,这就是这个算法妙的地方,我不加!
的彩虹表所对应的kuizuo
密文可能在彩虹表里都有了,但是我这样处理,在kuizuo
这个字符串前或后,都加一个随机的字符串(这些随机的字符串可要记得,不然你自己到时候数据效验的时候也不知道原文对不对了),然后进行拼接在取 MD5 的值,这样他的彩虹表就废了,就需要猜测出我的加的盐,然后在重新生成密文与原文对应的数据库。
在涉及 HTTP 请求的时候,用到最多的还是 sign,如一段 post 的 data 数据为
{"phone":"15212345678”,"timestamp":"1598567732417" , sign:"41785be6d13c5e3a0112c79255607f3a"}
timestamp 是时间戳,可以知道 1598567732417 所对应的时间(年月日时分秒毫秒的那种)
这段数据用于发送手机验证码注册的,前端发送这段 post 请求给后端,后端要如果验证这个算法是否是伪造的,只要就需要将前端发送的原文与我的 sign 比对,是否正常来效验数据。比方说我上面的 sign 是通过 js 文件 将 phone 的值15280326573
加上 timestamp 的值1598567732417
再加上盐kuizuo
然后进行 MD5 得到的值。拼接后也就是152123456781598567732417kuizuo
加密后的结果。试想一下如果不加这个 sign 的值,那么我只要发送{"phone":"15212345678”,"timestamp":"1598567732417}
给后端就能收到验证码了?那可也太轻松了,所以一般网络都会添加这个 sign。但由于是浏览器,浏览器内访问的 web 所有文件都是可见的,也就是我们能看到这些源文件的代码,也就是能找到对应加密代码,就能找到是将 phone 的值与 timestamp 的值加上 kuizuo 拼接后的取 MD5 的,同样也能伪装,只不过你得会看的懂 JS 逆向,这里就不多涉及了。
那 MD5 SHA1 SHA3 HmacMD5 HmacSHA1。。。又有啥区别
可以说没多大区别,都是消息摘要算法,加密的结果都是不可逆 加密后的长度固定 但各不同,如 md5 加密后为 32 个字符(hex 表示)而 sha1 则是 40 个字符,sha256 则是 64 位字符,加密后长度越长安全更好,但是加密速度也会限制。但是 sha 的输出结果还可以为 Base64 编码,此外加密后结果就没什么区别了,实现原理相不相同我可就不知道了。
HMAC 倒是有点区别,就是它需要一个密钥 Key,其余的也就和上面那些没区别了
对称加密算法
- DES
- 3DES(也称 TripleDES,DESede)
- AES 根据密钥长度不同又分为 AES-128 AES-192 AES-256 其中 AES-192 AES-256 在 Java 中使用需获取无政策限制权限文件
这种算法是对称的,分组加密,也就是加密和解密是可逆的,那么就肯定有东西用来加密和解密,就是密钥 Key,并且这个密钥我加密可以用,解密也可以用,此乃居家旅行之必备啊。但是他与 MD5 等不同,他需要的参数就多了,如图
模式 Mode,填充方式 Padding,一个 Key,一个 IV
简要概述一下这里用 AES 来举例,(AES 算是 DES 的加强版,一般都是用 AES)
CryptoJS 提供 ECB,CBC,CFB,OFB,CTR 五种模式,但常见的 Mode 模式也就 ECB,CBC(其他几个我实战还真没见过)
填充方式提供 NoPadding ZeroPadding Pkcs7(Pkcs5) Iso10126 Iso97971 AnsiX923
CBC 模式 是需要 IV 向量的 最常用的就是它
而 EBC 是不需要 IV 向量的 (由于不需要 Iv 向量,容易遭到字典攻击,不推荐)
填充可以理解每次是对固定大小的分组数据进行处理。但是大多数需要加密的数据并不是固定大小的倍数长度。例如 AES 数据块为 128 位,也就是 16 字节长度,而需要加密的长度可能为 15、26 等等。为了解决这个问题,我们就需要对数据进行填补操作,将数据补齐至对应块长度。
而 Padding 可以说就决定了加密结果是否固定,如 Pkcs7 (我遇到的多),加密结果就是固定不变的
PKCS7 在填充时首先获取需要填充的字节长度 = (块长度 - (数据长度 % 块长度)), 在填充字节序列中所有字节填充为需要填充的字节长度值
例:
假定块长度为 8,数据长度为 3,则填充字节数等于 5,数据等于 FF FF FF :
| FF FF FF 05 05 05 05 05 |
ZeroPadding 则是填充 0x00
假定块长度为 8,数据长度为 2,则填充字节数等于 6,数据等于 FF FF :
| FF FF 00 00 00 00 00 00 |
ISO10126 在填充时首先获取需要填充的字节长度 = (块长度 - (数据长度 % 块长度)), 在填充字节序列中最后一个字节填充为需要填充的字节长度值
, 填充字节中其余字节均填充随机数值. 例:
假定块长度为 16,数据长度为 9,则填充字节数等于 7,数据等于 FF FF FF FF FF FF FF FF FF :
| FF FF FF FF FF FF FF FF FF 73 68 C4 81 A6 23 07 |