共计 7902 个字符,预计需要花费 20 分钟才能阅读完成。
一、什么是 AES?
高级加密规范(英语:Advanced Encryption Standard,缩写:AES),是一种区块加密规范。这个规范用来代替原先的 DES,曾经被多方剖析且广为全世界所应用。
那么为什么原来的 DES 会被取代呢,,起因就在于其应用 56 位密钥,比拟容易被破解。而 AES 能够应用 128、192、和 256 位密钥,并且用 128 位分组加密和解密数据,相对来说平安很多。欠缺的加密算法在实践上是无奈破解的,除非应用穷尽法。应用穷尽法破解密钥长度在 128 位以上的加密数据是不事实的,仅存在实践上的可能性。统计显示,即便应用目前世界上运算速度最快的计算机,穷尽 128 位密钥也要花上几十亿年的工夫,更不用说去破解采纳 256 位密钥长度的 AES 算法了。
目前世界上还有组织在钻研如何攻破 AES 这堵坚厚的墙,然而因为破解工夫太长,AES 失去保障,然而所用的工夫一直放大。随着计算机计算速度的增快,新算法的呈现,AES 受到的攻打只会越来越剧烈,不会进行的。
AES 当初宽泛用于金融财务、在线交易、无线通信、数字存储等畛域,禁受了最严格的考验,但说不定哪天就会步 DES 的后尘。
二、AES 加密形式简析
- AES 加密是对称加密 128 192 256 别离示意密钥的长度
-
AES 的加密形式会将明文拆分成不同的块进行加密,例如一个 256 位的数据用 128 的密钥加密,则分成
明文 1(128 位)明文 2(128 位)
加密
密文 1(128 位)密文 2(128 位)
填充:
如果明文不是 128 位(16 字节)的则须要填充,即在明文某个中央补充到 16 个字节整数倍的长度,加解密时须要采纳同样的填充形式,否则无奈解密胜利,以下是几种填充形式
** NoPadding
不进行填充,然而这里要求明文必须要是 16 个字节的整数倍,这个能够使用者自身本人去实现填充,除了该种模式以外的其余填充模式,如果曾经是 16 个字节的数据的话,会再填充一个 16 字节的数据
** PKCS5Padding(默认)在明文的开端进行填充,填充的数据是以后和 16 个字节相差的数量,例如:未填充明文
1,2,3,4,5,6,7,8,9,10,11
填充明文(短少五个满足 16 个字节)1,2,3,4,5,6,7,8,9,10,11,5,5,5,5,5
因为应用 PKCS7Padding/PKCS5Padding 填充时,最初一个字节必定为填充数据的长度,所以在解密后能够精确删除填充的数据
** ISO10126Padding
在明文的开端进行填充,以后和 16 个字节相差的数量填写在最初,其余字节填充随机数,例如:未填充明文
1,2,3,4,5,6,7,8,9,10,11
填充明文(短少五个满足 16 个字节)1,2,3,4,5,6,7,8,9,10,11,c,b,4,1,5
模式
模式是须要制订 AES 对明文进行加密时应用的模式(这里并不波及具体的加密办法,只是加密步骤上的不同模式,在加解密时同样须要雷同的模式,否则无奈胜利),一共提供了五种模式,模式的基本原理是近似的,然而细节上会有一些变动,如下:
** ECB 模式(默认)电码本模式 Electronic Codebook Book
这个模式是默认的,就只是依据密钥的位数,将数据分成不同的块进行加密,加密实现后,再将加密后的数据拼接起来,过程如下:明文(64 字节)密钥(16 字节)明文 1(16 字节)明文 2(16 字节)明文 3(16 字节)明文 4(16 字节)密文 1(16 字节)密文 2(16 字节)密文 3(16 字节)密文 4(16 字节)密文(64 字节)长处:简略、速度快、可并行
毛病:如果明文块雷同,则生成的密文块也雷同,这样会导致安全性升高
** CBC 模式 明码分组链接模式 Cipher Block Chaining
为了解决 ECB 模式的密文块雷同的毛病,CBC 的模式引入了一个初始向量概念,该向量必须是一个与密钥长度相等的数据,在第一次加密前,会应用初始化向量与第一块数据做异或运算,生成的新数据再进行加密,加密第二块之前,会拿第一块的密文数据与第二块明文进行异或运算后再进行加密,以此类推,解密时也是在解密后,进行异或运算,生成最终的明文。过程如下:明文(63 字节)密钥(16 字节)初始向量 iv(16 字节)明文 1(16 字节)明文 2(16 字节)明文 3(16 字节)明文 4 + 一个 0(16 字节)异或 + 初始向量 + 密文 1 + 密文 2 + 密文 3
密文 1(16 字节)密文 2(16 字节)密文 3(16 字节)密文 4(16 字节)密文(64 字节)这里须要留神如下几点:1. 向量必须是一个与密钥长度相等的数据
2. 因为在加密前和解密后都会做异或运算,因而咱们的明文能够不必补全,不是 16 个字节的倍数也能够,CBC 中会主动用 0 补全进行异或运算
3. 在解密时是解密后才会再做异或运算,保证数据解密胜利
4. 因为主动进行了补全,所以解密出的数据也会在前面补全 0,因而获取到数据时,须要将开端的 0 去除,或者依据源数据长度来截取解密后的数据
长处:每次加密密钥不同,增强了安全性
CBC 的形式解决了 EBC 的毛病,然而也有其毛病:1. 加密无奈并行运算,然而解密能够并行,必须在前一个块加密实现后,能力加密后块,并且也须要填充 0 在前面,所以并不适宜流数据(不适宜的起因可能是,须要满足 128 位的数据之后能力进行加密,这样前面才不会有 0 的补全)2. 如果前一个数据加密谬误,那么后续的数据都是错的了
3. 两端须要同时约定初始向量 iv
** CFB 模式:明码反馈模式 Cipher FeedBack
这个模式只应用了加密办法,原理是用到了一个数值异或运算之后再进行一次异或运算,值不扭转的原理。并且在加密的时候,如果数据并不满足一个密钥的字节,那么只做保留,待满足一个密钥的字节后再进行加密 过程如下:加密:明文(260 个字节)iv(128 个字节)明文 1(128 个字节)明文 2(128 个字节)明文 3(4 个字节)(iv+key)异或 明文 1(密文 1 +key)异或 明文 1(密文 1 +key)异或明文 3
密文 1(128 个字节)密文 2(128 个字节)密文 3(4 个字节)解密:密文(260 个字节)iv(128 个字节)密钥(128 字节)密文 1(128 个字节)密文 2(128 个字节)密文 3(4 个字节)(iv+key)异或密文 1(密文 1 +key)异或密文 2(密文 1 +key)异或密文 3
明文 1(128 个字节)明文 2(128 个字节)明文 3(4 个字节)这里须要留神如下几点:1. 加解密时会返回一个 num,这个 num 示意还须要几个数字,才会应用上一个密文加密,否则始终应用上上一个
2. 加解密时也须要传入字符串的长度
3. 因为解密时应用的都是密文来进行解密,并没有应用上一次解密的明文,因而解密也能够并行
4. 因为 CFB 模式并不需要补全,或者一个残缺的 128 字节能力加解密,综合第三点,所以适宜流数据的传输。5.CFB 模式不止有 CFB128(即与密钥长度统一),还有 CFB1 和 CFB8 即加解密 1 或 8 位后,再调用一次加密器生成新的值,这样能够使加密更平安,然而就会解决更多的运算,CFB1 的运算工夫是 CFB8 的八倍 CFB128 的 128 倍
6. 应用 CFB128 或者 CFB8 的时候传入的 length 单位是字节,CFB1 是 length 的单位是位。7. 应用 CFB1 和 CFB8 的时候,num 值会始终为 0
长处: 解密可同步,能够传入非 16 字节倍数的数据,适宜流数据
CFB 模式当然也有一个毛病,解密的时候能够并行解密,然而加密的时候并不能够并行加密。并且也须要抉择 iv
** OFB 模式:输入反馈模式 Output FeedBack
该模式与 CFB 相似,然而是将 iv 或者上一个 iv 加密后的数据加密,生成的 key 与明文做异或运算,解密时采纳的是同样的办法,利用了异或运算的对称性来进行加解密,除了这一点,其余与 CFB 统一
加密 / 解密:CFB:(iv+key)异或 明文 1(密文 1 +key)异或 明文 1(密文 1 +key)异或明文 3
OFB(iv+key)异或明文 1((iv+key)+key)异或明文 1(((iv+key)+key)+key)异或明文 3
长处:与 CFB 一样,不便传输流数据
毛病:因为依赖上一次的加密后果,所以并不能并行处理,个性是解密步骤完全一致,因而应用办法上不会有区别。** CTR 模式:计算器模式 Counter
OFB 不能并行的起因就在于须要上一次的 iv 进行加密后的后果,因而在 CTR 中咱们将(iv+key)+key 替换成了(iv+1)+key, 这样咱们就不须要依赖上一次的加密后果了。比照如下:
OFB(iv+key)异或明文 1((iv+key)+key)异或明文 1(((iv+key)+key)+key)异或明文 3
CTR(iv+key)异或明文 1((iv+1)+key)异或明文 1(((iv+1)+1)+key)异或明文 3
长处:因为加解密能够并行,因而 CTR 模式的加解密速度也很快
毛病:iv+ 1 的获取比拟负责,须要获取刹时 iv
三、提供两个示例
1、java mysql 通用 aes 加密算法
通用的 aes 加密,应用场景,插入数据时,应用 java 进行加密数据,查问时,通过 sql 进行解密,不必取出再遍历解密
注:to_base64 只实用 mysql5.6 之后的,之前的没有这个函数,不实用,能够应用 HEX,UNHEX,当然 java 要用对应的办法解密
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
/**
- java 应用 AES 加密解密 AES-128-ECB 加密
- 与 mysql 数据库 aes 加密算法通用
- 数据库 aes 加密解密
- — 加密
- SELECT to_base64(AES_ENCRYPT(‘www.gowhere.so’,’jkl;POIU1234++==’));
- — 解密
- SELECT AES_DECRYPT(from_base64(‘Oa1NPBSarXrPH8wqSRhh3g==’),’jkl;POIU1234++==’);
- @author 836508
*
*/
public class MyAESUtil {
// 加密
public static String Encrypt(String sSrc, String sKey) throws Exception {if (sKey == null) {System.out.print("Key 为空 null");
return null;
}
// 判断 Key 是否为 16 位
if (sKey.length() != 16) {System.out.print("Key 长度不是 16 位");
return null;
}
byte[] raw = sKey.getBytes("utf-8");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法 / 模式 / 补码形式"
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));
return new BASE64Encoder().encode(encrypted);// 此处应用 BASE64 做转码性能,同时能起到 2 次加密的作用。}
// 解密
public static String Decrypt(String sSrc, String sKey) throws Exception {
try {
// 判断 Key 是否正确
if (sKey == null) {System.out.print("Key 为空 null");
return null;
}
// 判断 Key 是否为 16 位
if (sKey.length() != 16) {System.out.print("Key 长度不是 16 位");
return null;
}
byte[] raw = sKey.getBytes("utf-8");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);// 先用 base64 解密
try {byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original,"utf-8");
return originalString;
} catch (Exception e) {System.out.println(e.toString());
return null;
}
} catch (Exception ex) {System.out.println(ex.toString());
return null;
}
}
public static void main(String[] args) throws Exception {
/*
* 此处应用 AES-128-ECB 加密模式,key 须要为 16 位。*/
String cKey = "jkl;POIU1234++==";
// 须要加密的字串
String cSrc = "www.gowhere.so";
System.out.println(cSrc);
// 加密
String enString = MyAESUtil.Encrypt(cSrc, cKey);
System.out.println("加密后的字串是:" + enString);
// 解密
String DeString = MyAESUtil.Decrypt(enString, cKey);
System.out.println("解密后的字串是:" + DeString);
}
}
2、java AES-128-CBC 加密模式
package com.zhongzhi.utils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
/**
- @Classname ZzSecurityHelper
- @Description TODO
- @Date 2019/6/24 16:50
- @Created by whd
*/
public class ZzSecurityHelper {
/*
* 加密用的 Key 能够用 26 个字母和数字组成 应用 AES-128-CBC 加密模式,key 须要为 16 位。*/
private static final String key="hj7x89H$yuBI0456";
private static final String iv ="NIfb&95GUY86Gfgh";
/**
* @author miracle.qu
* @Description AES 算法加密明文
* @param data 明文
* @param key 密钥,长度 16
* @param iv 偏移量,长度 16
* @return 密文
*/
public static String encryptAES(String data) throws Exception {
try {Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
int blockSize = cipher.getBlockSize();
byte[] dataBytes = data.getBytes();
int plaintextLength = dataBytes.length;
if (plaintextLength % blockSize != 0) {plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
}
byte[] plaintext = new byte[plaintextLength];
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes()); // CBC 模式,须要一个向量 iv,可减少加密算法的强度
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(plaintext);
return ZzSecurityHelper.encode(encrypted).trim(); // BASE64 做转码。} catch (Exception e) {e.printStackTrace();
return null;
}
}
/**
* @author miracle.qu
* @Description AES 算法解密密文
* @param data 密文
* @param key 密钥,长度 16
* @param iv 偏移量,长度 16
* @return 明文
*/
public static String decryptAES(String data) throws Exception {
try
{byte[] encrypted1 = ZzSecurityHelper.decode(data);// 先用 base64 解密
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original);
return originalString.trim();}
catch (Exception e) {e.printStackTrace();
return null;
}
}
/**
* 编码
* @param byteArray
* @return
*/
public static String encode(byte[] byteArray) {return new String(new Base64().encode(byteArray));
}
/**
* 解码
* @param base64EncodedString
* @return
*/
public static byte[] decode(String base64EncodedString) {return new Base64().decode(base64EncodedString);
}
}