转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/120285594
本文出自【赵彦军的博客】
文章目录
- AES 简介
- 对称加密
- 加密模式
- PKCS5Padding到底是什么?
- 偏移量
- 字符集
- 实际工作中的加密流程
- AES 加密/解密 注意的问题
- 实战,AES加解密
AES 简介
- DES 全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS)
- AES 密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES(Data Encryption Standard),已经被多方分析且广为全世界所使用。
为什么 DES 被废弃?
我们知道数据加密标准(Data Encryption Standard: DES)的密钥长度是56比特,因此算法的理论安全强度是2的56次方。但二十世纪中后期正是计算机飞速发展的阶段,元器件制造工艺的进步使得计算机的处理能力越来越强,DES将不能提供足够的安全性。
简单来说,DES标准的秘钥长度要求太短,安全性不够。
为什么AES算法被称为 Rijndael 算法?
1997年1月2号,美国国家标准技术研究所(National Institute of Standards and Technology: NIST)宣布希望征集高级加密标准(Advanced Encryption Standard: AES)[3],用以取代DES。AES得到了全世界很多密码工作者的响应,先后有很多人提交了自己设计的算法。最终有5个候选算法进入最后一轮:Rijndael,Serpent,Twofish,RC6和MARS,下图分别为其中的5位作者。最终经过安全性分析、软硬件性能评估等严格的步骤,Rijndael算法获胜。
为什么AES算法安全性高?
AES的区块长度固定为128位,密钥长度则可以是128 bit,192 bit 或256位 bit 。换算成字节长度,就是密码必须是 16个字节,24个字节,32个字节。AES密码的长度更长了,破解难度就增大了,所以就更安全。
对称加密
- 对称加密 : 也就是加密秘钥和解密秘钥是一样的。
- 非对称加密 : 也就是加密秘钥和解密秘钥是不一样的。
AES 是对称加密算法,优点:加密速度快;缺点:如果秘钥丢失,就容易解密密文,安全性相对比较差
RSA 是非对称加密算法 , 优点:安全 ;缺点:加密速度慢
AES加密需要:明文 + 密钥+ 偏移量(IV)+密码模式(算法/模式/填充)
AES解密需要:密文 + 密钥+ 偏移量(IV)+密码模式(算法/模式/填充)
AES的算法模式一般为 AES/CBC/PKCS5Padding
加密模式
AES
的加密模式有以下几种
- 电码本模式(ECB)
- 密码分组链接模式(CBC)
- 计算器模式(CTR)
- 密码反馈模式(CFB)
- 输出反馈模式(OFB)
密码分组链接模式(CBC)
:将整段明文切成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。
根据图示,在 CBC 模式下,使用 AES 加解密方式进行分组加解密时,需要用到的两个参数
-
1、初始化向量,也就是偏移量
-
2、加解密秘钥
PKCS5Padding到底是什么?
为什么 JAVA 里指定算法时,写的是 AES/CBC/PKCS5Padding
,每个都是什么含义,又有什么作用。
-
AES,加解密算法
-
CBC,数据分组模式
-
PKCS5Padding,数据按照一定的大小进行分组,最后分剩下那一组,不够长度,就需要进行补齐, 也可以叫
补齐模式
简单的说:拿到一个原始数据以后,首先需要对数据进行分组,分组以后如果长度不满足分组条件,需要进行补齐,最后形成多个分组,在使用加解密算法,对这多个分组进行加解密。所以这个过程中,AES,CBC,PKCS5Padding 缺一不可。
在对数据进行加解密时,通常将数据按照固定的大小(block size)分成多个组,那么随之就产生了一个问题,如果分到最后一组,不够一个 block size 了,要怎么办?此时就需要进行补齐操作。
补齐规则:The value of each added byte is the number of bytes that are added, i.e. N bytes, each of value N are added.
举例:
36 位的 UUID,如果按照 block size=16 字节(即 128 比特),那么就需要补齐到 48 位,差 12 个字节。那么最后填充的 12 个字节的内容,都是字节表示的 0x0c(即 12)。
偏移量
偏移量
的添加一般是为了增加 AES
加密的复杂度,增加数据的安全性。一般在 AES_256
中会使用到 偏移量
,而在 AES_128
加密中不会使用到。
字符集
在 AES
加密中,特别也要注意到字符集的问题。一般用到的字符集是 utf-8
和 gbk
。
实际工作中的加密流程
在实际的工作中,客户端跟服务器交互一般都是字符串格式,所以一个比较好的加密流程是:
- 加密流程 :明文通过
密钥
(有时也需要偏移量
),利用AES
加密算法,然后通过Base64
转码,最后生成加密后的字符串。 - 解密流程 :加密后的字符串通过
密钥
(有时也需要偏移量
),利用AES
解密算法,然后通过Base64
转码,最后生成解密后的字符串。
AES 加密/解密 注意的问题
AES
加密/解密的时候,通常是用在服务端和客户端通讯的过程中,一端加密传输,另一端解密使用。虽然 AES
加密看似简单,但在使用过程过程中,仍然会出现在一端加密ok,但是另一端解密失败的情况。一旦出现 AES
解密失败,我们可以通过以下几个方面进行排查:
1. AES 加密/解密 使用相同的密钥
2. 若涉及到偏移量,则AES 加密/解密 使用的偏移量要一样
3. AES 加密/解密 要使用相同加密数位,如都使用`AES_256`
4. AES 加密/解密 使用相同的字符集
5. AES 加密/解密 使用相同的加密,填充模式,如都使用`AES/CBC/PKCS5Padding`模式
6. 由于不同开发语言(如C 和 Java)及不同开发环境(如 Java 和 Android)的影响,可能相同的加解密算法在实现上出现差异,若你们注意到这个差异,就可能导致加解密出现问题
最后,当我们需要验证自己的 AES
解密算法是否与别人的加密方法为一套的时候。可以让加密方发你一份加密后的密文和加密前的明文,然后你用密文解密,看解密结果和加密方发你的是否一致。需要注意的是,加密方给你的明文要尽量简洁,如就 中国
二字,这样既能看出加密方和解密方的字符集是否一致,而且能避免复制粘贴等环节出现空格,回车等转义字符对验证结果的干扰。
实战,AES加解密
首先定义加密、解密工具类
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.GeneralSecurityException;
/**
* AES加解密工具类
*/
public class AES {
/**
* AES加密
*
* @param key
* @param iv
* @throws GeneralSecurityException
* @throws IOException
*/
public static byte[] encryptAes(byte[] data, byte[] key, byte[] iv)
throws GeneralSecurityException, IOException {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
return cipher.doFinal(data);
}
/**
* AES解密
*
* @param key
* @param iv
* @return
* @throws GeneralSecurityException
* @throws IOException
*/
public static byte[] decryptAesToByteString(byte[] data, byte[] key, byte[] iv)
throws GeneralSecurityException, IOException {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
return cipher.doFinal(data);
}
}
加密和解密的代码很像,唯一的不同点是,加密 Cipher.ENCRYPT_MOD
, 解密用的是 Cipher.DECRYPT_MODE
下面我们写一个测试代码:
try {
//加密密码
String key = "zhaoyanjunzhaoy1";
//偏移量
String iv = "1234567890123456";
String message = "今天是周二,我好开心";
//加密
byte[] encryResult = AES.encryptAes(message.getBytes(), key.getBytes(), iv.getBytes());
//解密
byte[] decryResult = AES.decryptAesToByteString(encryResult, key.getBytes(), iv.getBytes());
System.out.println("解密数据 = " + new String(decryResult));
} catch (IOException | GeneralSecurityException e) {
e.printStackTrace();
}
输出结果:
解密数据 = 今天是周二,我好开心
可以看到数据已经正常解密了。