JAVA实现AES加密、解密
⼀、什么是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个字节的整数倍,这个可以使⽤者本⾝⾃⼰去实现填充,
java源代码加密
除了该种模式以外的其他填充模式,如果已经是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要⽤对应的⽅法解密pto.Cipher;
pto.Cipher;
pto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
/**
* java使⽤AES加密解密 AES-128-ECB加密
* 与mysql数据库aes加密算法通⽤
* 数据库aes加密解密
* -- 加密
*    SELECT to_base64(AES_ENCRYPT('where.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 = Bytes("utf-8");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Instance("AES/ECB/PKCS5Padding");//"算法/模式/补码⽅式"
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.Bytes("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 = Bytes("utf-8");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Instance("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.String());
return null;
}
} catch (Exception ex) {
System.out.String());
System.out.String());
return null;
}
}
public static void main(String[] args) throws Exception {
/*
* 此处使⽤AES-128-ECB加密模式,key需要为16位。
*/
String cKey = "jkl;POIU1234++==";
// 需要加密的字串
String cSrc = "where.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;
pto.Cipher;
pto.spec.IvParameterSpec;
pto.spec.SecretKeySpec;
import dec.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 = Instance("AES/CBC/NoPadding");
int blockSize = BlockSize();
byte[] dataBytes = Bytes();
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 Bytes(), "AES");

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。