场景
调用第三方接口返回数据需要AES/ECB/PKCS5Padding解密。
导入的包
import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Decoder;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
代码片段
public class AESUtil {
private static Logger log = LoggerFactory.getLogger(AESUtil.class);
/**
* 密钥算法
*/
private static final String ALGORITHM = "AES";
/**
* 加解密算法/工作模式/填充方式
*/
private static final String ALGORITHM_STR = "AES/ECB/PKCS5Padding";
/**
* SecretKeySpec类是KeySpec接口的实现类,用于构建秘密密钥规范
*/
private SecretKeySpec key;
public AESUtil(String hexKey) {
key = new SecretKeySpec(hexKey.getBytes(), ALGORITHM);
}
/**
* AES解密
* @param base64Data
* @return
* @throws Exception
*/
public String decryptData(String base64Data) throws Exception{
Cipher cipher = Cipher.getInstance(ALGORITHM_STR);
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(new BASE64Decoder().decodeBuffer(base64Data)));
}
/**
* 获取密钥
* @return
* @throws NoSuchAlgorithmException
*/
public static String initKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
//192,256
keyGenerator.init(128);
SecretKey secretKey = keyGenerator.generateKey();
return Base64.getEncoder().encodeToString(secretKey.getEncoded());
}
/**
* AES+BASE64加密
* 加密方法
* 与 解密方法 {@link #decrypt} 配套使用
* @param data 要加密的数据
* @param key 加密所使用的密钥
* @return 加密后的数据
* @throws Exception
*/
public static String encrypt(String data, String key) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(key);
SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
Cipher decryptionCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = KeyGenerator.getInstance("AES").generateKey().getEncoded();
IvParameterSpec ivSpec = new IvParameterSpec(iv);
decryptionCipher.init(Cipher.ENCRYPT_MODE, secretKey , ivSpec );
byte[] cipherText = decryptionCipher.doFinal(data.getBytes());
byte[] iv_cipher = new byte[iv.length + cipherText.length];
int i = 0 ;
for (; i < iv.length; i++) {
iv_cipher[i] = iv[i];
}
for (int j = 0; j < cipherText.length; j++) {
iv_cipher[(i + j)] = cipherText[j];
}
return Base64.getEncoder().encodeToString(iv_cipher);
}
/**
* AES+BASE64解密
* 与 加密方法{@link #encrypt} 配套使用
* @param data 要解密的数据
* @param key 解密所使用的密钥
* @return 解密后的数据, 即源数据
* @throws Exception
*/
public static String decrypt(String data, String key) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(key);
SecretKey keySpec = new SecretKeySpec(keyBytes, "AES");
Cipher decryptionCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv_cipher = Base64.getDecoder().decode(data);
byte[] iv = new byte[16];
byte[] cipher = new byte[iv_cipher.length - 16];
int i = 0;
int j = 0;
for (; i < 16; i++) {
iv[i] = iv_cipher[i];
}
for (; i < iv_cipher.length; i++) {
cipher[j] = iv_cipher[i];
j++;
}
IvParameterSpec ivSpec = new IvParameterSpec(iv);
decryptionCipher.init(2, keySpec, ivSpec);
return new String(decryptionCipher.doFinal(cipher), "UTF-8");
}
/**
* 加密方法
* 与 解密方法{@link #decryptWithHex} 配套使用
* 将不符合要求的密钥处理为16位的ASCII编码的数组,使用AES加密,
* 将加密的字节数组转为十六进制字符数组,然后将其转换为utf-8字符串。
* @param preString 待加密的字符串
* @param strKey 密钥
* @return
*/
public static String encryptWithHex(String preString, String strKey) {
try {
SecretKey key = generateMySQLAESKey(strKey,"ASCII");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cleartext = preString.getBytes("UTF-8");
byte[] ciphertextBytes = cipher.doFinal(cleartext);
return new String(Hex.encodeHex(ciphertextBytes));
} catch (Exception e) {
log.error("{}字符串通过AES密钥{}加密异常",preString, strKey, e);
}
return null;
}
/**
* 解密方法
* 与 加密方法{@link #encryptWithHex} 配套使用
* @param preString 待加密的字符串
* @param strKey 密钥
*/
public static String decryptWithHex(String preString, String strKey){
try {
SecretKey key = generateMySQLAESKey(strKey,"ASCII");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] cleartext = Hex.decodeHex(preString.toCharArray());
byte[] ciphertextBytes = cipher.doFinal(cleartext);
return new String(ciphertextBytes, "UTF-8");
} catch (Exception e) {
log.error("{}字符串通过AES密钥{}解密异常",preString, strKey, e);
}
return null;
}
/**
* 将不符合要求的密钥处理为16位的ASCII编码的字节数组
* 数据库AES算法也是如此处理的
*/
private static SecretKeySpec generateMySQLAESKey(final String key, final String encoding) {
try {
final byte[] finalKey = new byte[16];
int i = 0;
for(byte b : key.getBytes(encoding)){
finalKey[i++%16] ^= b;
}
return new SecretKeySpec(finalKey, "AES");
} catch(UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}
总结
如有其他更好的方法或者想法,可以留言或联系分享,大家互相学习进步。