javaAES加密和解密
java:AES加密和解密
1 前言
对称加密,即单秘钥加密,指加密和解密的过程中,使用相同的秘钥,相比于非对称加密,因仅有一把钥匙,故而速度更快,更适合解密大文件(常见于如视频文件的加密解密中)。AES算法就属于对称加密中的一种。
2 使用
依赖引入:
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.6</version>
</dependency>
<!-- spring-boot 3.12.0 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
AES加密与解密的工具类封装:
package com.xiaoxu.crawler.utils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.util.StringUtils;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/**
* @author xiaoxu
* @date 2022-11-06 21:24
* crawlerJ:com.xiaoxu.crawler.utils.AESUtils
*/
public class AESUtils {
private static final String AES_ALGORITHM = "AES";
private static final String UTF8 = StandardCharsets.UTF_8.name();
/* AES加密 String */
public static String encryptStrAES(String text, String key){
if(!StringUtils.hasLength(text)){
ExcpUtils.throwExp("encode text should not be null or empty.");
}
byte[] encodeBytes = encryptByteAES(text.getBytes(StandardCharsets.UTF_8), key);
return Base64.encodeBase64String(encodeBytes);
}
/* AES解密 String*/
public static String decryptStrAES(String text, String key){
if(!StringUtils.hasLength(text)){
ExcpUtils.throwExp("decode text should not be null or empty.");
}
byte[] decodeBytes = decryptByteAES(Base64.decodeBase64(text.getBytes(StandardCharsets.UTF_8)), key);
return new String(decodeBytes, StandardCharsets.UTF_8);
}
/* AES加密 originalBytes */
public static byte[] encryptByteAES(byte[] originalBytes, String key){
if(ArrayUtils.isEmpty(originalBytes)){
ExcpUtils.throwExp("encode originalBytes should not be empty.");
}
if(!StringUtils.hasLength(key)){
ExcpUtils.throwExp("key :" key ", encode key should not be null or empty.");
}
Cipher cipher = getAESCipher(key, Cipher.ENCRYPT_MODE);
byte[] encodeBytes = null;
try {
encodeBytes = cipher.doFinal(originalBytes);
} catch (IllegalBlockSizeException | BadPaddingException e) {
ExcpUtils.throwExp(e.getClass().getName() ": encode byte fail. " e.getMessage());
}
return encodeBytes;
}
/* AES解密 encryptedBytes */
public static byte[] decryptByteAES(byte[] encryptedBytes, String key){
if(ArrayUtils.isEmpty(encryptedBytes)){
ExcpUtils.throwExp("decode encryptedBytes should not be empty.");
}
if(!StringUtils.hasLength(key)){
ExcpUtils.throwExp("key :" key ", decode key should not be null or empty.");
}
Cipher cipher = getAESCipher(key, Cipher.DECRYPT_MODE);
byte[] decodeBytes = null;
try {
decodeBytes = cipher.doFinal(encryptedBytes);
} catch (IllegalBlockSizeException | BadPaddingException e) {
ExcpUtils.throwExp(e.getClass().getName() ": decode byte fail. " e.getMessage());
}
return decodeBytes;
}
public static Cipher getAESCipher(String key, int mode){
if(!StringUtils.hasLength(key)){
ExcpUtils.throwExp("key :" key ", should not be null or empty.");
}
Cipher cipher = null;
SecretKey secretKey;
try {
cipher = Cipher.getInstance(AES_ALGORITHM);
byte[] keyBytes = key.getBytes(UTF8);
secretKey = new SecretKeySpec(keyBytes, AES_ALGORITHM);
cipher.init(mode, secretKey);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
ExcpUtils.throwExp(e.getClass().getName() ": get cipher instance wrong. " e.getMessage());
} catch (UnsupportedEncodingException u){
ExcpUtils.throwExp(u.getClass().getName() ": key transfer bytes fail. " u.getMessage());
} catch (InvalidKeyException i) {
ExcpUtils.throwExp(i.getClass().getName() ": key is invalid. " i.getMessage());
}
return cipher;
}
public static void main(String[] args) {
String msg = "小徐123testAes!!!";
String key = "0382f2bcbacfa8be";
// String key = "0382f2bcbacfa8b";
System.out.println("AES秘钥长度只能为16、24、32:" key.getBytes(StandardCharsets.UTF_8).length);
String s = encryptStrAES(msg, key);
System.out.println("加密后:" s);
String s1 = decryptStrAES(s, key);
System.out.println("解密后:" s1);
}
}
执行如下:
AES秘钥长度只能为16、24、32:16
加密后:ZfZYz9Pk4RcA3QQcv rKnjM54AQV4s/LjsRiGbkpzw8=
解密后:小徐123testAes!!!
AES的秘钥字节数组长度仅能为16、24、32,修改秘钥长度为15再次执行:
public static void main(String[] args) {
String msg = "小徐123testAes!!!";
// String key = "0382f2bcbacfa8be";
String key = "0382f2bcbacfa8b";
System.out.println("AES秘钥长度只能为16、24、32:" key.getBytes(StandardCharsets.UTF_8).length);
String s = encryptStrAES(msg, key);
System.out.println("加密后:" s);
String s1 = decryptStrAES(s, key);
System.out.println("解密后:" s1);
}
结果如下,出现了Invalid AES key length异常:
AES秘钥长度只能为16、24、32:15
Exception in thread "main" com.xiaoxu.crawler.excp.CrawlerForJException: java.security.InvalidKeyException: key is invalid. Invalid AES key length: 15 bytes
at com.xiaoxu.crawler.utils.ExcpUtils.throwExp(ExcpUtils.java:28)
at com.xiaoxu.crawler.utils.AESUtils.getAESCipher(AESUtils.java:94)
at com.xiaoxu.crawler.utils.AESUtils.encryptByteAES(AESUtils.java:50)
at com.xiaoxu.crawler.utils.AESUtils.encryptStrAES(AESUtils.java:29)
at com.xiaoxu.crawler.utils.AESUtils.main(AESUtils.java:104)
debug源码可见:
AES使用的秘钥key参数有长度限制,如下可见ElectronicCodeBook的部分源码:
var2为传入的加密的算法,为AES;var3就是秘钥key转换的字节数组,总共长度为15;调用的是AESCrypt的init方法,如下可见AESCrypt的init源码:
void init(boolean var1, String var2, byte[] var3) throws InvalidKeyException {
if (!var2.equalsIgnoreCase("AES") && !var2.equalsIgnoreCase("Rijndael")) {
throw new InvalidKeyException("Wrong algorithm: AES or Rijndael required");
} else if (!isKeySizeValid(var3.length)) {
throw new InvalidKeyException("Invalid AES key length: " var3.length " bytes");
} else {
if (!MessageDigest.isEqual(var3, this.lastKey)) {
this.makeSessionKey(var3);
this.lastKey = (byte[])var3.clone();
}
this.K = this.sessionK[var1 ? 1 : 0];
}
}
可见,isKeySizeValid(var3.length)返回false时,即抛出Invalid AES key length异常,如下为isKeySizeValid源码:
AESCrypt部分源码如下:
static final boolean isKeySizeValid(int var0) {
for(int var1 = 0; var1 < AES_KEYSIZES.length; var1) {
if (var0 == AES_KEYSIZES[var1]) {
return true;
}
}
return false;
}
其中AES_KEYSIZES为接口中的常量:
interface AESConstants {
int AES_BLOCK_SIZE = 16;
int[] AES_KEYSIZES = new int[]{16, 24, 32};
}
即AES的秘钥字节数组长度如果不为16、24、32,将抛出Invalid AES key length异常。
由此可见,AES的秘钥字节数组长度需限制在16、24、32内。
package com.xiaoxu.crawler.utils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.util.StringUtils;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author xiaoxu
* @date 2022-11-06 21:24
* crawlerJ:com.xiaoxu.crawler.utils.AESUtils
*/
public class AESUtils {
private static final String AES_ALGORITHM = "AES";
private static final String UTF8 = StandardCharsets.UTF_8.name();
private static final List<Integer> AES_KEYSIZES = Arrays.asList(16, 24, 32);
public static String newKey(String key){
if(!StringUtils.hasLength(key)){
ExcpUtils.throwExp("new key should not be empty");
}
boolean fl = false;
int len = key.getBytes(StandardCharsets.UTF_8).length;
for (int keySize : AES_KEYSIZES) {
if(len == keySize){
fl = true;
break;
}
}
String newKey = key;
if(!fl){
/* 32、24、16倒序排序 */
List<Integer> new_sizes = AES_KEYSIZES.stream().sorted(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
}).collect(Collectors.toList());
int length = 0;
for (int var0 = 0; var0 < new_sizes.size(); var0 ) {
if(len > 0 && len < new_sizes.get(new_sizes.size()-1) && var0 == new_sizes.size()-1){
length = len;
}else if(new_sizes.get(var0) <= len){
length = new_sizes.get(var0);
}
if(new_sizes.get(var0) <= len || (len > 0 && len < new_sizes.get(new_sizes.size()-1)&& var0 == new_sizes.size()-1 ) ){
byte[] nKey = new byte[new_sizes.get(var0)];
for (int f = 0; f < new_sizes.get(var0); f ) {
/* 不足16位的末尾补0 */
nKey[f] = (byte)48;
}
System.arraycopy(key.getBytes(StandardCharsets.UTF_8),0,nKey,0,length);
newKey = new String(nKey, StandardCharsets.UTF_8);
break;
}
}
}
return newKey;
}
/* AES加密 String */
public static String encryptStrAES(String text, String key){
if(!StringUtils.hasLength(text)){
ExcpUtils.throwExp("encode text should not be null or empty.");
}
byte[] encodeBytes = encryptByteAES(text.getBytes(StandardCharsets.UTF_8), key);
return Base64.encodeBase64String(encodeBytes);
}
/* AES解密 String*/
public static String decryptStrAES(String text, String key){
if(!StringUtils.hasLength(text)){
ExcpUtils.throwExp("decode text should not be null or empty.");
}
byte[] decodeBytes = decryptByteAES(Base64.decodeBase64(text.getBytes(StandardCharsets.UTF_8)), key);
return new String(decodeBytes, StandardCharsets.UTF_8);
}
/* AES加密 originalBytes */
public static byte[] encryptByteAES(byte[] originalBytes, String key){
if(ArrayUtils.isEmpty(originalBytes)){
ExcpUtils.throwExp("encode originalBytes should not be empty.");
}
if(!StringUtils.hasLength(key)){
ExcpUtils.throwExp("key :" key ", encode key should not be null or empty.");
}
Cipher cipher = getAESCipher(key, Cipher.ENCRYPT_MODE);
byte[] encodeBytes = null;
try {
encodeBytes = cipher.doFinal(originalBytes);
} catch (IllegalBlockSizeException | BadPaddingException e) {
ExcpUtils.throwExp(e.getClass().getName() ": encode byte fail. " e.getMessage());
}
return encodeBytes;
}
/* AES解密 encryptedBytes */
public static byte[] decryptByteAES(byte[] encryptedBytes, String key){
if(ArrayUtils.isEmpty(encryptedBytes)){
ExcpUtils.throwExp("decode encryptedBytes should not be empty.");
}
if(!StringUtils.hasLength(key)){
ExcpUtils.throwExp("key :" key ", decode key should not be null or empty.");
}
Cipher cipher = getAESCipher(key, Cipher.DECRYPT_MODE);
byte[] decodeBytes = null;
try {
decodeBytes = cipher.doFinal(encryptedBytes);
} catch (IllegalBlockSizeException | BadPaddingException e) {
ExcpUtils.throwExp(e.getClass().getName() ": decode byte fail. " e.getMessage());
}
return decodeBytes;
}
public static Cipher getAESCipher(String key, int mode){
if(!StringUtils.hasLength(key)){
ExcpUtils.throwExp("key :" key ", should not be null or empty.");
}
Cipher cipher = null;
SecretKey secretKey;
try {
cipher = Cipher.getInstance(AES_ALGORITHM);
byte[] keyBytes = key.getBytes(UTF8);
secretKey = new SecretKeySpec(keyBytes, AES_ALGORITHM);
cipher.init(mode, secretKey);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
ExcpUtils.throwExp(e.getClass().getName() ": get cipher instance wrong. " e.getMessage());
} catch (UnsupportedEncodingException u){
ExcpUtils.throwExp(u.getClass().getName() ": key transfer bytes fail. " u.getMessage());
} catch (InvalidKeyException i) {
ExcpUtils.throwExp(i.getClass().getName() ": key is invalid. " i.getMessage());
}
return cipher;
}
public static void main(String[] args) {
String msg = "小徐123testAes!!!";
// String key = "0382f2bcbacfa8be";
String key = "0382f2bcbacfa8b";
System.out.println("转换前的秘钥长度:" key.getBytes(StandardCharsets.UTF_8).length);
System.out.println("转换后秘钥:" newKey(key) ";长度:" newKey(key).getBytes(StandardCharsets.UTF_8).length);
System.out.println("AES秘钥长度只能为16、24、32:" newKey(key).getBytes(StandardCharsets.UTF_8).length);
String s = encryptStrAES(msg, newKey(key));
System.out.println("加密后:" s);
String s1 = decryptStrAES(s, newKey(key));
System.out.println("解密后:" s1);
}
}
可如上简单对秘钥长度作出填充,执行结果如下:
转换前的秘钥长度:15
转换后秘钥:0382f2bcbacfa8b0;长度:16
AES秘钥长度只能为16、24、32:16
加密后:AAz4gPXlduN l1OX0BV9nWXqJqhXRS3ThRQXJsU0lWM=
解密后:小徐123testAes!!!
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhggaikc
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01