AES对称加密,比DES安全性高。
AES通常用于移动通信系统加密以及基于SSH协议的软件。
AES对称加密:AES常用的有ECB和CBC两种模式。常用的填充方式有PKCS5Padding、PKCS7Padding、zeropadding
CBC模式比ECB模式安全,ECB模式比CBC模式快。 推荐使用CBC模式。
相同密码的加密结果不会变化,是固定的。
这个标准用来替代原先的DES
key的字节长度只能是16位、24位、32位。
iv的字节长度只能是16位。
KEY和IV可以相同,也可以不相同。
key和iv长度不足时,这里将以0x00自动填充,超出部分将被忽略。注意:其他系统不一定是这种逻辑,所以key建议设置为16/24/32位,iv设置为16位。
说明:AES数据块长度为128位(16字节),所以IV长度需要为16个字节(ECB模式不用IV),IV与KEY超过长度则截取,不足则在末尾填充'\0'补足
package com.study;
import com.sun.org.slf4j.internal.Logger;
import com.sun.org.slf4j.internal.LoggerFactory;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
/**
* AES对称加密:AES常用的有ECB和CBC两种模式。常用的填充方式有PKCS5Padding、PKCS7Padding、zeropadding
* CBC模式比ECB模式安全,ECB模式比CBC模式快。 推荐使用CBC模式。
* 相同密码的加密结果不会变化,是固定的。
* 这个标准用来替代原先的DES
* <p>
* key的字节长度只能是16位、24位、32位。
* iv的字节长度只能是16位。
* KEY和IV可以相同,也可以不相同。
* key和iv长度不足时,这里将以0x00自动填充,超出部分将被忽略。注意:其他系统不一定是这种逻辑,所以key建议设置为16/24/32位,iv设置为16位。
* <p>
* 说明:AES数据块长度为128位(16字节),所以IV长度需要为16个字节(ECB模式不用IV),IV与KEY超过长度则截取,不足则在末尾填充'\0'补足
* @date 2019年12月23日 下午7:56:33
*/
public class AESUtil {
private static final Logger logger = LoggerFactory.getLogger(AESUtil.class);
private static final String CBCMode = "AES/CBC/PKCS5Padding";//填充方式
private static final String ECBMode = "AES/ECB/PKCS5Padding";//填充方式
public static void main(String[] args) {
String key = "1234567812345678";// key长度建议设置为16位或者24位或者32位。
String iv = "1234567887654321";// iv长度建议设置为16位。
String msg = "测试字符串";
//CBC模式
String cbcEncrypt = AESUtil.encryptCBC(msg, key, iv);
System.out.println(cbcEncrypt);
String cbcDecryptStr = AESUtil.decryptCBC(cbcEncrypt, key, iv);
System.out.println(cbcDecryptStr);
//ECB模式
String ecbEncrypt = AESUtil.encryptECB(msg, key);
System.out.println(ecbEncrypt);
String decryptStr = AESUtil.decryptECB(ecbEncrypt, key);
System.out.println(decryptStr);
}
/**
* AES CBC 加密
* @param message 需要加密的字符串
* @param key 密匙
* @param iv IV,需要和key长度相同
* @return 返回加密后密文,编码为base64
*/
public static String encryptCBC(String message, String key, String iv) {
try {
byte[] content = message.getBytes(StandardCharsets.UTF_8);
byte[] keyByte = key.getBytes(StandardCharsets.UTF_8);
keyByte = fillKey(keyByte);
SecretKeySpec keySpec = new SecretKeySpec(keyByte, "AES");
byte[] ivByte = iv.getBytes(StandardCharsets.UTF_8);
ivByte = fillIv(ivByte);
IvParameterSpec ivSpec = new IvParameterSpec(ivByte);
Cipher cipher = Cipher.getInstance(CBCMode);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] data = cipher.doFinal(content);
return Base64.getEncoder().encodeToString(data);
} catch (NoSuchAlgorithmException e) {
logger.error("没有这样的算法", e);
} catch (NoSuchPaddingException e) {
logger.error("没有这样的填充", e);
} catch (InvalidKeyException e) {
logger.error("无效的密钥", e);
} catch (InvalidAlgorithmParameterException e) {
logger.error("无效的算法参数", e);
} catch (IllegalBlockSizeException e) {
logger.error("非法块大小", e);
} catch (BadPaddingException e) {
logger.error("不良填充", e);
}
return null;
}
/**
* AES CBC 解密
* @param messageBase64 密文,base64编码
* @param key 密匙,和加密时相同
* @param iv IV,需要和key长度相同
* @return 解密后数据
*/
public static String decryptCBC(String messageBase64, String key, String iv) {
try {
byte[] messageByte = Base64.getDecoder().decode(messageBase64);
byte[] keyByte = key.getBytes(StandardCharsets.UTF_8);
keyByte = fillKey(keyByte);
SecretKeySpec keySpec = new SecretKeySpec(keyByte, "AES");
byte[] ivByte = iv.getBytes(StandardCharsets.UTF_8);
ivByte = fillIv(ivByte);
IvParameterSpec ivSpec = new IvParameterSpec(ivByte);
Cipher cipher = Cipher.getInstance(CBCMode);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] content = cipher.doFinal(messageByte);
return new String(content, StandardCharsets.UTF_8);
} catch (NoSuchAlgorithmException e) {
logger.error("没有这样的算法", e);
} catch (NoSuchPaddingException e) {
logger.error("没有这样的填充", e);
} catch (InvalidKeyException e) {
logger.error("无效的密钥", e);
} catch (InvalidAlgorithmParameterException e) {
logger.error("无效的算法参数", e);
} catch (IllegalBlockSizeException e) {
logger.error("非法块大小", e);
} catch (BadPaddingException e) {
logger.error("不良填充", e);
}
return null;
}
/**
* AES ECB 加密
* @param message 需要加密的字符串
* @param key 密匙
* @return 返回加密后密文,编码为base64
*/
public static String encryptECB(String message, String key) {
try {
byte[] content = message.getBytes(StandardCharsets.UTF_8);
byte[] keyByte = key.getBytes(StandardCharsets.UTF_8);
keyByte = fillKey(keyByte);
SecretKeySpec keySpec = new SecretKeySpec(keyByte, "AES");
Cipher cipher = Cipher.getInstance(ECBMode);
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] data = cipher.doFinal(content);
return Base64.getEncoder().encodeToString(data);
} catch (NoSuchAlgorithmException e) {
logger.error("没有这样的算法", e);
} catch (NoSuchPaddingException e) {
logger.error("没有这样的填充", e);
} catch (InvalidKeyException e) {
logger.error("无效的密钥", e);
} catch (IllegalBlockSizeException e) {
logger.error("非法块大小", e);
} catch (BadPaddingException e) {
logger.error("不良填充", e);
}
return null;
}
/**
* AES ECB 解密
* @param messageBase64 密文,base64编码
* @param key 密匙,和加密时相同
* @return 解密后数据
*/
public static String decryptECB(String messageBase64, String key) {
try {
byte[] messageByte = Base64.getDecoder().decode(messageBase64);
byte[] keyByte = key.getBytes(StandardCharsets.UTF_8);
keyByte = fillKey(keyByte);
SecretKeySpec keySpec = new SecretKeySpec(keyByte, "AES");
Cipher cipher = Cipher.getInstance(ECBMode);
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] content = cipher.doFinal(messageByte);
return new String(content, StandardCharsets.UTF_8);
} catch (NoSuchAlgorithmException e) {
logger.error("没有这样的算法", e);
} catch (NoSuchPaddingException e) {
logger.error("没有这样的填充", e);
} catch (InvalidKeyException e) {
logger.error("无效的密钥", e);
} catch (IllegalBlockSizeException e) {
logger.error("非法块大小", e);
} catch (BadPaddingException e) {
logger.error("不良填充", e);
}
return null;
}
/**
* 填充key
* key的字节长度只能是16位、24位、32位。
* key长度不足时,这里将以0x00填充,超出部分将被忽略。
* @return 填充后的key
*/
public static byte[] fillKey(byte[] keyByte) {
int length = keyByte.length;
int len;
if (length == 16 || length == 24 || length == 32) {
return keyByte;
} else if (length < 16) {
len = 16;
} else if (length < 24) {
len = 24;
} else {
len = 32;
}
byte[] newKeyByte = new byte[len];
System.arraycopy(keyByte, 0, newKeyByte, 0, length < len ? length : len);
return newKeyByte;
}
/**
* 填充iv
* iv的字节长度只能是16位。
* iv长度不足时,这里将以0x00填充,超出部分将被忽略。
* @return 填充后的iv
*/
public static byte[] fillIv(byte[] ivByte) {
int length = ivByte.length;
int len;
if (length == 16) {
return ivByte;
} else {
len = 16;
}
byte[] newIvByte = new byte[len];
System.arraycopy(ivByte, 0, newIvByte, 0, length < len ? length : len);
return newIvByte;
}
}
AES GCM 模式
package com.study;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
public class AESUtil {
private static final Logger logger = LoggerFactory.getLogger(AESUtil.class);
private static String gcm256algorithm = "AES/GCM/PKCS5Padding";
public static void main(String[] args) {
KeyGenerator generator = null;
try {
generator = KeyGenerator.getInstance("AES");
//初始化密钥生成器,AES要求密钥长度为128位、192位、256位
generator.init(256);
SecretKey secretKey = generator.generateKey();
// String gcmSecretKey = Base64.encodeBase64String(secretKey.getEncoded());
String key = "1234567812345678";//key长度为16的倍数
String gcmSecretKey = Base64.encodeBase64String(key.getBytes(StandardCharsets.UTF_8));
System.out.println("生成密钥key: " + key + "\n");
String toDoStr = "12345678";
System.out.println("明文文本:" + toDoStr);
String encryptResult = AEGCMEncrypt(toDoStr, gcmSecretKey);
System.out.println("加密结果: " + encryptResult);
String decryptResult = AEGCMDecrypt(encryptResult, gcmSecretKey);
System.out.println("解密结果: " + decryptResult + "\n");
} catch (NoSuchAlgorithmException e) {
logger.error("密钥生成异常,error:", e);
}
}
//解密
public static String AEGCMDecrypt(String content, String keyStr) {
try {
if (StringUtils.isEmpty(content) || StringUtils.isEmpty(keyStr)) {
throw new Exception("AESGCM256解密异常,检查文本或密钥");
}
Cipher cipher = Cipher.getInstance(gcm256algorithm);
SecretKey key = new SecretKeySpec(Base64.decodeBase64(keyStr), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] message = Base64.decodeBase64(content);
// 这里的12和16是加密的时候设置的偏移参数及加密长度
if (message.length < 12 + 16) throw new IllegalArgumentException();
GCMParameterSpec params = new GCMParameterSpec(128, message, 0, 12);
cipher.init(Cipher.DECRYPT_MODE, key, params);
byte[] decryptData = cipher.doFinal(message, 12, message.length - 12);
String decript = new String(decryptData);
return decript;
} catch (Exception e) {
logger.error("AESGCM256解密文本处理失败,error:{}", e);
}
return null;
}
//加密
public static String AEGCMEncrypt(String content, String keyStr) {
try {
if (StringUtils.isEmpty(content) || StringUtils.isEmpty(keyStr)) {
throw new Exception("AESGCM256加密异常,检查文本或密钥");
}
SecretKey secretKey = new SecretKeySpec(Base64.decodeBase64(keyStr), "AES");
Cipher cipher = Cipher.getInstance(gcm256algorithm);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] iv = cipher.getIV();
assert iv.length == 12;// 偏移参数及长度要在解密的时候保持一致
byte[] encryptData = cipher.doFinal(content.getBytes());
assert encryptData.length == content.getBytes().length + 16;
byte[] message = new byte[12 + content.getBytes().length + 16];
System.arraycopy(iv, 0, message, 0, 12);
System.arraycopy(encryptData, 0, message, 12, encryptData.length);
return Base64.encodeBase64String(message);
} catch (Exception e) {
logger.error("AESGCM256加密文本处理失败,error:{}", e);
}
return null;
}
}