import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.RandomStringUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* AEC加密工具类
* @Date: 2019/11/6 14:05
*/
@Slf4j
public class AesUtil {
/**
* 编码格式
*/
public static final String ENCODING = "utf-8";
/**
* 密钥算法
*/
private static final String KEY_ALGORITHM = "AES";
public static String encryptCBC(String value, String key) {
try {
IvParameterSpec iv = new IvParameterSpec(key.getBytes(ENCODING));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(ENCODING), KEY_ALGORITHM);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.encodeBase64String(encrypted);
} catch (Exception ex) {
log.error("encryptCBC error:", ex);
}
return null;
}
public static String decryptCBC(String encrypted, String key) {
try {
IvParameterSpec iv = new IvParameterSpec(key.getBytes(ENCODING));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(ENCODING), KEY_ALGORITHM);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
return new String(original);
} catch (Exception ex) {
log.error("decryptCBC error:", ex);
}
return null;
}
/***
* AES加密算法,调用Java自有类库,采用CFB模式密文反馈无填充模式,可以保证数据长度在加密前后是相同的
* @paramcontent 待加密内容
* @paramkey 密钥
* @returnbyte[] 加密结果用byte数组表示
**/
public static String encryptCFB(String content, String key) {
try {
Cipher aesECB = Cipher.getInstance("AES/CFB/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(ENCODING), KEY_ALGORITHM);
IvParameterSpec ivSpec = new IvParameterSpec(key.getBytes(ENCODING));
aesECB.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] result = aesECB.doFinal(content.getBytes());
return Base64.encodeBase64String(result);
} catch (Exception ex) {
log.error("encryptCFB error:", ex);
}
return null;
}
/***
* @paramcontent 待解密内容,字符串形式
* @paramkey 解密用的密钥
* @return 使用字符串形式返回解密内容
* @throws Exception
**/
public static String decryptCFB(String content, String key) {
try {
Cipher aesECB = Cipher.getInstance("AES/CFB/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), KEY_ALGORITHM);
IvParameterSpec ivSpec = new IvParameterSpec(key.getBytes());
aesECB.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] result = aesECB.doFinal(Base64.decodeBase64(content));
String AES_decode = new String(result, ENCODING);
return AES_decode;
} catch (Exception ex) {
log.error("decryptCFB error:", ex);
}
return null;
}
/**
* AES加密
* @param source 源字符串
* @param key 密钥
* @return 加密后的密文
* @throws Exception
*/
public static String encryptECB(String source, String key){
try {
byte[] sourceBytes = source.getBytes(ENCODING);
byte[] keyBytes = key.getBytes(ENCODING);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, KEY_ALGORITHM));
byte[] decrypted = cipher.doFinal(sourceBytes);
return Base64.encodeBase64String(decrypted);
} catch (Exception ex) {
log.error("encryptECB error:", ex);
}
return null;
}
/**
* AES解密
* @param encryptStr 加密后的密文
* @param key 密钥
* @return 源字符串
* @throws Exception
*/
public static String decryptECB(String encryptStr, String key) {
try {
byte[] sourceBytes = Base64.decodeBase64(encryptStr);
byte[] keyBytes = key.getBytes(ENCODING);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, KEY_ALGORITHM));
byte[] decoded = cipher.doFinal(sourceBytes);
return new String(decoded, ENCODING);
} catch (Exception ex) {
log.error("decryptECB error:", ex);
}
return null;
}
public static final String randomSecretKey() {
return RandomStringUtils.randomNumeric(16);
}
/**
* 十六进制转字符串
* @param buf
* @return
*/
public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
/**
* 字符串转十六进制
* @param hexStr
* @return
*/
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;
}
public static void main(String[] args) {
try {
// String key = RandomStringUtils.randomNumeric(16);
String key = "7618927929652544";
String content = "{\"username\":\"1\",\"password\":\"1\"}";
String encryptStr = AesUtil.encryptCBC(content,key);
log.info("key:{}, encryptStr: {}", key, encryptStr);
String decryptStr = AesUtil.decryptCBC(encryptStr,key);
System.out.println("decryptStr: "+decryptStr);
} catch (Exception e) {
e.printStackTrace();
}
}
}
说明:
AES加密后的数据需要转字符串传给服务端,转字符串有两种方式,一种是base64,一种是使用十六进制(Hex),方法中使用的是Base64方式。
Base64方式:编码:Base64.encodeBase64String(result) ;解码:Base64.decodeBase64(content)
Hex方式:转十六进制:parseHexStr2Byte(String hexStr); 转字符串:parseByte2HexStr(byte buf[])
之前在和客户端(使用vue.js) 调试过程中,客户端对加密后的报文转字符串处理方式上有问题。会存在客户端加密,服务端解密失败问题,因此把两种方式都补充了下。