JAVA加密--AES加密算法JAVA实现及使⽤中的各种坑,超实⽤
1. AES
1.1. 概念
密码学中的⾼级加密标准(Advanced Encryption Standard,AES),⼜称Rijndael加密法,是美国联邦政府采⽤的⼀种区块加密标准。这个标准⽤来替代原先的DES(Data Encryption Standard),已经被多⽅分析且⼴为全世界所使⽤,已然成为对称密钥加密中最流⾏的算法之⼀。详见
1.2. JAVA实现AES加解密
import Slf4j;
import Cipher;
import KeyGenerator;error parse new
import SecretKey;
import SecretKeySpec;
import SecureRandom;
import Arrays;
@Slf4j
class AESCryptoUtil {
public static void main(String[] args){
String content ="美好的世界,美好的中国,美好的未来";
byte[] encryptResult =encrypt(content);
byte[] decryptResult =decrypt(encryptResult);
String encryptStr =parseByte2HexStr(encryptResult);
printMsg("========\n加密前:%s\n加密后:%s\n解密后:%s", content, encryptStr,new String(decryptResult));
//解密时,不能new String(encryptResult,"utf-8").getBytes("utf-8")
decryptResult =decrypt(parseHexStr2Byte(encryptStr));
printMsg("========\n加密前:%s\n加密后:%s\n解密后:%s", content, encryptStr,new String(decryptResult));
//采⽤AES/ECB/NoPadding时,要求密钥长度16、24、32中的⼀个、待加密内容的byte[]长度必须是16的倍数
encryptResult =encrypt2(content);
decryptResult =decrypt2(encryptResult);
encryptStr =parseByte2HexStr(encryptResult);
printMsg("========\n加密前:%s\n加密后:%s\n解密后:%s", content, encryptStr,new String(decryptResult));
}
private static void printMsg(String template, args){
System.out.println(String.format(template, args));
}
private static String password ="科技兴国@!##";
private static SecretKeySpec key;
static{
init();
}
private static void init(){
try{
KeyGenerator kgen = Instance("AES");
kgen.init(256,new Bytes()));
SecretKey secretKey = ateKey();
byte[] enCodeFormat = Encoded();
key =new SecretKeySpec(enCodeFormat,"AES");
key =new SecretKeySpec(enCodeFormat,"AES");
}catch(Exception e){
<("初始化AES算法失败"+ e.getMessage(), e);
}
}
/**
* 加密
*
* @param content 需要加密的内容
* @return
*/
public static byte[]encrypt(String content){
try{
Cipher cipher = Instance("AES");
byte[] byteContent = Bytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] result = cipher.doFinal(byteContent);
return result;
}catch(Exception e){
<("AES加密失败"+ e.getMessage(), e);
}
return null;
}
public static byte[]decrypt(byte[] content){
try{
Cipher cipher = Instance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] result = cipher.doFinal(content);
return result;
}catch(Exception e){
<("AES解密失败"+ e.getMessage(), e);
}
return null;
}
/**
* 加密
*
* @param content 需要加密的内容
* @return
*/
public static byte[]encrypt2(String content){
try{
byte[] bytePassword = Bytes();
bytePassword =checkByteLength(bytePassword);
SecretKeySpec key =new SecretKeySpec(bytePassword,"AES"); Cipher cipher = Instance("AES/ECB/NoPadding");
byte[] byteContent = Bytes("utf-8");
byteContent =checkByteLength(byteContent);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] result = cipher.doFinal(byteContent);
return result;
}catch(Exception e){
<("AES加密失败"+ e.getMessage(), e);
}
return null;
}
public static byte[]decrypt2(byte[] content){
try{
byte[] bytePassword = Bytes();
bytePassword =checkByteLength(bytePassword);
SecretKeySpec key =new SecretKeySpec(bytePassword,"AES"); Cipher cipher = Instance("AES/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] result = cipher.doFinal(content);
return result;
}catch(Exception e){
<("AES解密失败"+ e.getMessage(), e);
}
return null;
}
private static byte[]checkByteLength(byte[] byteContent){
int length = byteContent.length;
int remainder = length %16;
if(remainder ==0){
return byteContent;
}else{
pyOf(byteContent,length+(16-remainder));
}
}
/**将⼆进制转换成16进制
* @param buf
* @return
*/
public static String parseByte2HexStr(byte buf[]){
StringBuffer sb =new StringBuffer();
for(int i =0; i < buf.length; i++){
String hex = HexString(buf[i]&0xFF);
if(hex.length()==1){
hex ='0'+ hex;
}
sb.UpperCase());
}
String();
}
public static byte[]parseHexStr2Byte(String hexStr){
if(hexStr.length()<1){
return null;
}
byte[] result =new byte[hexStr.length()/2];
for(int i =0;i< hexStr.length()/2; i++){
int high = Integer.parseInt(hexStr.substring(i*2, i*2+1),16);
int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2),16);
result[i]=(byte)(high *16+ low);
}
return result;
}
}
2. 使⽤中的各种坑
2.1. 加密后的byte数组不能强制转换成字符串
加密后返回byte[],此byte数组强制转换为字符串是乱码,然后再⽤转换后的字符串转换为byte数组去解码(见下⾯的代码)会报错:Input length must be multiple of 16 when decrypting with padded cipher
在这种情况下: 字符串和byte数组不是互逆的;要避免这种情况,需要做⼀些修订,可以考虑将⼆进制数据转换成⼗六进制表⽰。详见parseByte2HexStr、parseHexStr2Byte两个⽅法
decryptResult =decrypt(new String(encryptResult,"utf-8").getBytes("utf-8"));
2021-07-02 11:27:39.067 [main] ERROR pattern.chain.AESCryptoUtil:decrypt:87 - AES解密失败Input length must be multiple of 16 when decrypting with padded cipher
at pto.provider.CipherCore.doFinal(CipherCore.java:936) ~[sunjce_provider.jar:1.8.0_171]
at pto.provider.CipherCore.doFinal(CipherCore.java:847) ~[sunjce_provider.jar:1.8.0_171]
at pto.ineDoFinal(AESCipher.java:446) ~[sunjce_provider.jar:1.8.0_171]
pto.Cipher.doFinal(Cipher.java:2164) ~[?:1.8.0_171]
at org.apache.design.pattern.chain.AESCryptoUtil.decrypt(AESCryptoUtil.java:84) [classes/:?]
at org.apache.design.pattern.chain.AESCryptoUtil.main(AESCryptoUtil.java:26) [classes/:?]
2.2. 采⽤AES/ECB/NoPadding加密的限制
当采⽤如下⽅式获取Cipher实例时,则要求密钥长度16、24、32中的⼀个、待加密内容的byte[]长度必须是16的倍数,否则报如下错误: Cipher cipher = Instance("AES/ECB/NoPadding");
2021-07-02 11:37:15.432 [main] ERROR pattern.chain.AESCryptoUtil:encrypt2:111 - AES加密失败Input length not multiple of 16 bytes
at pto.provider.CipherCore.finalNoPadding(CipherCore.java:1042) ~[sunjce_provider.jar:1.8.0_171]
at pto.provider.CipherCore.doFinal(CipherCore.java:1010) ~[sunjce_provider.jar:1.8.0_171]
at pto.provider.CipherCore.doFinal(CipherCore.java:847) ~[sunjce_provider.jar:1.8.0_171]
at pto.ineDoFinal(AESCipher.java:446) ~[sunjce_provider.jar:1.8.0_171]
pto.Cipher.doFinal(Cipher.java:2164) ~[?:1.8.0_171]
或
2021-07-02 11:36:39.064 [main] ERROR pattern.chain.AESCryptoUtil:encrypt2:111 - AES加密失败Invalid AES key length: 18 bytes
java.security.InvalidKeyException: Invalid AES key length: 18 bytes
at pto.provider.AESCrypt.init(AESCrypt.java:87) ~[sunjce_provider.jar:1.8.0_171]
at pto.provider.ElectronicCodeBook.init(ElectronicCodeBook.java:94) ~[sunjce_provider.jar:1.8.0_171]
at pto.provider.CipherCore.init(CipherCore.java:592) ~[sunjce_provider.jar:1.8.0_171]
at pto.provider.CipherCore.init(CipherCore.java:468) ~[sunjce_provider.jar:1.8.0_171]
at pto.ineInit(AESCipher.java:313) ~[sunjce_provider.jar:1.8.0_171]
pto.Cipher.implInit(Cipher.java:801) ~[?:1.8.0_171]
pto.Cipher.chooseProvider(Cipher.java:863) ~[?:1.8.0_171]
2.3 美国的出⼝管制限制引起的坑
2.3.1. 背景
因为美国的出⼝管制限制,Java发布的运⾏环境包中的加解密有⼀定的限制。
⽐如默认不允许256位密钥的AES加解密,如果使⽤,会报如下错误:
2021-07-02 11:40:34.048 [main] ERROR pattern.chain.AESCryptoUtil:encrypt:75 - AES加密失败Illegal key size or default parameters
java.security.InvalidKeyException: Illegal key size or default parameters
pto.Cipher.checkCryptoPerm(Cipher.java:1025) ~[?:1.8.0_171]
pto.Cipher.implInit(Cipher.java:800) ~[?:1.8.0_171]
pto.Cipher.chooseProvider(Cipher.java:863) ~[?:1.8.0_171]
pto.Cipher.init(Cipher.java:1248) ~[?:1.8.0_171]
pto.Cipher.init(Cipher.java:1185) ~[?:1.8.0_171]
从Java 1.8.0_151
2.3.2. 解决⽅案
⽅案1
将%JDK_HOME%\jre\lib\security\java.security, 到定义java安全性属性crypto.policy的⾏,将值修改为unlimited。默认情况下,您应该能到⼀条注释掉的⾏:
#crypto.policy=unlimited
您可以通过取消注释该⾏来启⽤⽆限制,删除#:
⽅案2
如果%JDK_HOME%\jre\lib\security⽬录下有jar包 且 crypto.policy 未配置,则使⽤jar包内置的规则
将%JDK_HOME%\jre\lib\security\policy\unlimited下的2个JAR【local_policy.jar、US_export_policy.jar】复制到%JDK_HOME%\jre\lib\security⽬录下,如果有,直接覆盖。
⽅案3
如果%JDK_HOME%\jre\lib\security⽬录下没有jar包 且 crypto.policy 未配置,则默认启⽤⽆限制的。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论