AES加密算法
很多的对称加密算法的,如DES、3DES加密算法等,但是因为AES加密算法的安全性要高于DES和3DES,所以AES已经成为了主要的对称加密算法,它的英文全称是Advanced Encryption Standard,翻译过来是高级加密标准,它是用来替代之前的DES加密算法的。
AES加密算法采用分组密码体制,每个分组数据的长度为128位16个字节,密钥长度可以是128位16个字节、192位或256位,一共有四种加密模式,我们通常采用需要初始向量IV的CBC模式,初始向量的长度也是128位16个字节。
AES的加密关键词
要理解AES的加密流程,会涉及到AES加密的五个关键词,分别是:分组密码体制、Padding、密钥、初始向量IV和四种加密模式。
- 分组密码体制:所谓分组密码体制就是指将明文切成一段一段的来加密,然后再把一段一段的密文拼起来形成最终密文的加密方式。AES采用分组密码体制,即AES加密会首先把明文切成一段一段的,而且每段数据的长度要求必须是128位16个字节,如果最后一段不够16个字节了,就需要用Padding来把这段数据填满16个字节,然后分别对每段数据进行加密,最后再把每段加密数据拼起来形成最终的密文。
- Padding:Padding就是用来把不满16个字节的分组数据填满16个字节用的,它有三种模式PKCS5、PKCS7和NOPADDING。 PKCS5是指分组数据缺少几个字节,就在数据的末尾填充几个字节的几,比如缺少5个字节,就在末尾填充5个字节的5。PKCS7是指分组数据缺少几个字节,就在数据的末尾填充几个字节的0,比如缺少7个字节,就在末尾填充7个字节的0。NoPadding是指不需要填充,也就是说数据的发送方肯定会保证最后一段数据也正好是16个字节。那如果在PKCS5模式下,最后一段数据的内容刚好就是16个16怎么办?那解密端就不知道这一段数据到底是有效数据还是填充数据了,因此对于这种情况,PKCS5模式会自动帮我们在最后一段数据后再添加16个字节的数据,而且填充数据也是16个16,这样解密段就能知道谁是有效数据谁是填充数据了。PKCS7最后一段数据的内容是16个0,也是同样的道理。解密端需要使用和加密端同样的Padding模式,才能准确的识别有效数据和填充数据。我们开发通常采用PKCS7 Padding模式。
- 初始向量IV:初始向量IV的作用是使加密更加安全可靠,我们使用AES加密时需要主动提供初始向量,而且只需要提供一个初始向量就够了,后面每段数据的加密向量都是前面一段的密文。初始向量IV的长度规定为128位16个字节,初始向量的来源为随机生成。 至于为什么初始向量能使加密更安全可靠,会在下面的加密模式中提到。
- 密钥:AES要求密钥的长度可以是128位16个字节、192位或者256位,位数越高,加密强度自然越大,但是加密的效率自然会低一些,因此要做好衡量。我们开发通常采用128位16个字节的密钥,我们使用AES加密时需要主动提供密钥,而且只需要提供一个密钥就够了,每段数据加密使用的都是这一个密钥,密钥来源为随机生成。 (注意:后面我们在谈到AES的加密原理时,会提到一个初始密钥,那个初始密钥指的就是这里我们提供的这个密钥。)
- 四种加密模式:AES一共有四种加密模式,分别是ECB(电子密码本模式) 、CBC(密码分组链接模式) 、CFB、OFB,我们一般使用的是CBC模式。
AES的加密流程
我们通常使用AES加密,会采用128位16个字节的密钥和CBC加密模式。
因此,我们这里要说的加密流程就是针对这种情况的。让我们再把CBC加密模式的图托到这里,重新整理一下AES的加密流程:
- 首先AES加密会把明文按128位16个字节,切成一段一段的数据,如果数据的最后一段不够16个字节,会用Padding来填充。
- 然后把明文块0与初始向量IV做异或操作,再用密钥加密,得到密文块0,同时密文块0也会被用作明文块1的加密向量。
- 然后明文块1与密文块0进行异或操作,再用密钥加密,得到密文块1。(当然这里只是假设数据只有两段,如果数据不止两段,依次类推,就可以得到很多段密文块。)
- 最后把密文块拼接起来就能得到最终的密文。
实际开发中使用AES加密解密需要注意的地方
- 服务端和我们客户端必须使用一样的密钥和初始向量IV。
- 服务端和我们客户端必须使用一样的加密模式。
- 服务端和我们客户端必须使用一样的Padding模式。
以上三条有一个不满足,双方就无法完成互相加解密。
同时针对对称加密密钥传输问题这个不足:我们一般采用RSA+AES加密相结合的方式,用AES加密数据,而用RSA加密AES的密钥。同时密钥和IV可以随机生成,这要是128位16个字节就行,但是必须由服务端来生成,因为如果由我们客户端生成的话,就好比我们客户端存放了非对称加密的私钥一样,这样容易被反编译,不安全,一定要从服务端请求密钥和初始向量IV。
作为前端人员,常需要进行AES解密,解密函数写法如下:
function decodeData(data) {
const key = CryptoJS.MD5(decipherSecretKey); // 密钥
const iv = CryptoJS.MD5(decipherSecretIv); // 密钥偏移量
const base64 = CryptoJS.enc.Base64.parse(data);
const src = CryptoJS.enc.Base64.stringify(base64); // 密文 AES解密的密文必须是 base64 编码
const decrypt = CryptoJS.AES.decrypt(src, key, {
iv: iv, // 密钥偏移量
mode: CryptoJS.mode.CBC, // 解密模式CBC
padding: CryptoJS.pad.Pkcs7 // 填充方式
});
const decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
return decryptedStr.toString(); // 解密后的结果是对象,要转为文本(以CryptoJS.enc.Utf8方式转换为文本)
}