AES是一种高级加密标准(AES,Advanced Encryption Standard)为最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的)。对称加密算法也就是加密和解密用相同的密钥。AES又称Rijndael加密法,是DES的进阶版。
1-1加密传输的过程图
1) 非对称密钥加密算法(RSA、DSA、ECC、DH等)
非对称加密又叫公开密钥算法(public key algorithm)。这种加密算法是这样设计的:用作加密的密钥不同于用作解密的密钥,而且解密密钥不能根据加密密钥计算出来(至少在合理假定的长时间内)。之所以又叫做公开密钥算法是由于加密密钥可以公开,即陌生人可以得到它并用来加密信息,但只有用相应的解密密钥才能解密信息。在这种加密算法中,加密密钥被叫做公开密钥,而解密密钥被叫做私有密钥。非对称加密算法的加密、解密的效率比较低。在算法设计上,非对称加密算法对待加密的数据长度有着苛刻的要求。
2) 对称密钥加密算法(DES、3DES、AES等)
对称钥匙加密系统是加密和解密均采用同一把秘密钥匙,而且通信双方都必须获得这把钥匙,并保持钥匙的秘密。
3) Hash加密算法(MD5、SHA等)
散列是信息的提炼,通常其长度要比信息小得多,且为一个固定长度。加密性强的散列一定是不可逆的,这就意味着通过散列结果,无法推出任何部分的原始信息。任何输入信息的变化,哪怕仅一位,都将导致散列结果的明显变化,这称之为雪崩效应。散列还应该是防冲突的,即找不出具有相同散列结果的两条信息。具有这些特性的散列结果就可以用于验证信息是否被修改。
AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。密钥的长度可以使用128位、192位或256位。密钥的长度不同,加密轮数也不同,如下表所示:
AES |
密钥长度(32位比特字) |
分组长度(32位比特字) |
加密轮数 |
---|---|---|---|
AES-128 | 4 | 4 | 10 |
AES-192 | 6 | 4 | 12 |
AES-256 | 8 | 4 | 14 |
1)电码本模式(Electronic Codebook Book (ECB)
这种模式是将整个明文分成若干段相同的小段,然后对每一小段进行加密。ECB模式由于每块数据的加密是独立的因此加密和解密都可以并行计算,ECB模式最大的缺点是相同的明文块会被加密成相同的密文块,这种方法在某些环境下不能提供严格的数据保密性。
2)密码分组链接模式(Cipher Block Chaining (CBC)
这种模式是先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。CBC模式相比ECB有更高的保密性,但由于对每个数据块的加密依赖与前一个数据块的加密所以加密无法并行。与ECB一样在加密前需要对数据进行填充,不是很适合对流数据进行加密,目前用的最多的模式
3)计算器模式(Counter (CTR))
计算器模式很不常见, 有一个自增的算子,这个算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密。这种加密方式简单快速,安全可靠,而且可以并行加密,但是在计算器不能维持很长的情况下,密钥只能使用一次,应该不适用于我们网关的场景。
另外还有OFB模式(输出反馈:Output feedback)和CFB模式(密文反馈:Cipher feedback)两种模式,适用与流加密,没有进一步研究
AES加密过程涉及到4种操作,分别是字节替代、行移位、列混淆和轮密钥加。解密过程分别为对应的逆操作。由于每一步操作都是可逆的,按照相反的顺序进行解密即可恢复明文。加解密中每轮的密钥分别由初始密钥扩展得到。算法中16个字节的明文、密文和轮密钥都以一个4x4的矩阵表示。
1.字节替换:AES的字节代换其实就是一个简单的查表操作。AES定义了一个S盒和一个逆S
2.行移位:行移位是一个简单的左循环移位操作。当密钥长度为128比特时,状态矩阵的第0行左移0字节,第1行左移1字节,第2行左移2字节,第3行左移3字节
3. 列混合变换MixColumns()
列变换就是从状态中取出一列,表示成多项式的形式后,用它乘以一个固定的多项式a(x),然后将所得结果进行取模运算
4. 轮密钥的添加变换AddRoundKey()
在这个操作中,轮密钥被简单地异或到状态中,轮密钥根据密钥表获得,其长度等于数据块的长度Nb。
说明:AES-128/192/256他们代表他们的密钥长度为128比特、192比特、256比特
加密上的本质区别:
a.加密轮数不同分别为10/12/14轮;
b.行位移不同位移量依次从小到大;
c.安全程度256>196>128,加密耗时256>192>128,资源耗费256>192>128
d. 密钥的长度对应,16位密钥= AES-128,24位密钥= AES-192,32位密钥= AES-256位。
说明:本次代码演示的是,如何使用AES-128对数据进行加密几解密,AES-196/256的加密解密与之大同小异(AES-256加密使用时候需要重新弄jar包,java默认支持的是128的,具体网上有教程)
1.加密
/*
* 加密
* content 需要加密的内容
* key 加密秘钥
* return 返回加密后的内容
*/
public static String Encrypt(String content, String key) throws Exception {
if (key == null) {
System.out.print("Key为空null");
return null;
}
// 判断Key是否为16位
if (key.length() != 16) {
System.out.print("Key长度不是16位");
return null;
}
byte[] raw = key.getBytes("utf-8");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); //设置密钥规范为AES
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");//"算法/模式/补码方式"
//CBC模式需要配置偏移量,设置一个向量,达到密码唯一性,增加加密算法的强度
IvParameterSpec iv = new IvParameterSpec("1234567890123456".getBytes());
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(content.getBytes());
return new BASE64Encoder().encode(encrypted);//此处使用BASE64做转码功能
}
2.解密
/*
* 解密方法
* content 需要解密的密文
* key 解密的秘钥
* return 解密后的内容
*/
public static String Decrypt(String content, String key) throws Exception {
try {
// 判断Key是否正确
if (key == null) {
System.out.print("Key为空null");
return null;
}
// 判断Key是否为16位
if (key.length() != 16) {
System.out.print("Key长度不是16位");
return null;
}
byte[] raw = key.getBytes("ASCII"); //参数类型
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //"算法/模式/补码方式"
//CBC模式需要配置偏移量,设置这个后,不会出来同一个明文加密为同一个密文的问题,达到密文唯一性
IvParameterSpec iv = new IvParameterSpec("1234567890123456".getBytes());
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(content);//先用base64解密
try {
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original);
return originalString;
} catch (Exception e) {
System.out.println(e.toString());
return null;
}
} catch (Exception ex) {
System.out.println(ex.toString());
return null;
}
}
3.试例
public static void main(String[] args) throws Exception {
/*
* 此处使用AES-128-CBC加密模式,key需要为16位、
*/
String key = "abcdefghijklmnop";
// 需要加密的字串 ,我传入一个JSON对象进去
String param = "{data:[{'name':'测试','age':24},{'name':'test','age':24}]}";
System.out.println(param);
// 调用加密方法
long cureentTime = System.currentTimeMillis();
String endString = AseUtils.Encrypt(param, key);
System.out.println("加密后的字串是:" + endString);
long useTime = System.currentTimeMillis() - cureentTime;
System.out.println("加密耗时:" + useTime + "毫秒");
// 调用解密
cureentTime = System.currentTimeMillis();
String str = AseUtils.Decrypt(endString, key);
System.out.println("解密后的字串是:" + str);
useTime = System.currentTimeMillis() - cureentTime;
System.out.println("解密耗时:" + useTime + "毫秒");
}
4.结果展示