1、加密算法
一两个月没写过博客了,上次把尚筹网的博客补了一波,然后就在找工作,这不,这个月刚入职,领导给了个代码优化的任务,使用SornarQube对项目代码进行分析,然后根据分析出来的点逐点优化。
加密算法有很多,时间长了,以前的加密算法可能会被发现漏洞,所以加密算法也要不停地更新。在项目中SornarQube就分析出了这么个问题,提示当前的加密算法(DES)太老了,不安全,应该使用AES加密。
2、AES加密
对于一个加密算法,我们首先需要了解几个概念:
- 明文(没有加密的数据)
- 密文(加密后的数据)
- 密钥(约定好的用于加密和解密的钥匙)
- 加密函数(具体加密解密的算法)
弄懂了这个之后,就可以很清楚地知道,要在Java中应用AES加密算法,需要定义两个方法,一个是加密,一个是解密。
2.1 具体加解密算法
AES算法在Java中已有具体实现,我们只需要调用即可:
private byte[] aes(byte[] contentArray, int mode, SecretKey secretKey) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(mode, secretKey);
return cipher.doFinal(contentArray);
}
其中三个参数分别为:mode表示本次操作是加密还是解密,contentArray表示加密的明文或解密的密文,secretKey表示密钥。
2.2 加密方法
/**
* 加密
* @param content 原文
* @return 密文
* @throws Exception 异常
*/
public String encode(String content) throws Exception {
BASE64Encoder base64Encoder = new BASE64Encoder();
return base64Encoder.encode(aes(content.getBytes(charset), Cipher.ENCRYPT_MODE, secretKey));
}
2.3 解密方法
/**
* 解密
* @param content 密文
* @return 原文
* @throws Exception 异常
*/
public String decode(String content) throws Exception {
BASE64Decoder base64Decoder = new BASE64Decoder();
byte[] result = aes(base64Decoder.decodeBuffer(content),Cipher.DECRYPT_MODE,secretKey);
return new String(result,charset);
}
2.4 代码总览
将上面几个方法放在一个工具类中,代码总体如下:
public class EncryptUtil {
private static final Logger log = LoggerFactory.getLogger(EncryptUtil.class);
private static final String ALGORITHM = "AES";
private Charset charset = null;
private SecretKey secretKey = null;
/**
* 初始化
* @param deSkey 密钥
* @throws Exception
*/
public EncryptUtil(String deSkey, String charset) throws Exception {
if (StringUtils.isNotBlank(charset)) {
this.charset = Charset.forName(charset);
}
secretKey = generateKey();
}
/**
* 生成密钥
* @return 密钥对象
* @throws NoSuchAlgorithmException
*/
public SecretKey generateKey() throws NoSuchAlgorithmException {
KeyGenerator secretGenerator = KeyGenerator.getInstance(ALGORITHM);
SecureRandom secureRandom = new SecureRandom();
secretGenerator.init(secureRandom);
return secretGenerator.generateKey();
}
/**
* 加密
* @param content 原文
* @return 密文
* @throws Exception 异常
*/
public String encode(String content) throws Exception {
BASE64Encoder base64Encoder = new BASE64Encoder();
return base64Encoder.encode(aes(content.getBytes(charset), Cipher.ENCRYPT_MODE, secretKey));
}
/**
* 解密
* @param content 密文
* @return 原文
* @throws Exception 异常
*/
public String decode(String content) throws Exception {
BASE64Decoder base64Decoder = new BASE64Decoder();
byte[] result = aes(base64Decoder.decodeBuffer(content),Cipher.DECRYPT_MODE,secretKey);
return new String(result,charset);
}
private byte[] aes(byte[] contentArray, int mode, SecretKey secretKey) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(mode, secretKey);
return cipher.doFinal(contentArray);
}
public static void main(String[] args) {
try {
//String test = "pms-adapter-pwd";
String test = "pms-adapter-pwd-prod";
// 自定义密钥
String key = "abcd9ba45bfd500642328ec03ad8ef1b6e75";
EncryptUtil des = new EncryptUtil(key, "utf-8");
System.out.println("加密前的字符:" + test);
String encode = des.encode(test);
System.out.println("加密后的字符:" + encode);
String decode = des.decode(encode);
System.out.println("解密后的字符:" + decode);
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
在工具类初始化的时候,需要传入两个值,一个是加密方和解密方约定的密钥,一个是字符串的编码格式。
3、工具类测试
public static void main(String[] args) {
try {
// 明文
String test = "I love you.";
// 自定义密钥
String key = "abcd9ba45bfd500642328ec03ad8ef1b6e75";
// 密钥生成
EncryptUtil des = new EncryptUtil(key, "utf-8");
System.out.println("加密前的字符:" + test);
// 加密
String encode = des.encode(test);
System.out.println("加密后的字符:" + encode);
// 解密
String decode = des.decode(encode);
System.out.println("解密后的字符:" + decode);
} catch (Exception e) {
log.error(e.getMessage());
}
}
4、注意事项
在写这个代码的时候,我也看了好多博客,它们有一个共同点,就是在加密和解密的中间过程,输出的是字节数组,但是因为我这个是代码优化,我必须要返回字符串,于是我就用了byte数组转String的函数,最后失败了,因为byte数组转String后再转回byte数组,数据会发生变化,而AES加密算法最重要的就是解密的时候密文的位数必须是16位的倍数,而数据发生变化后,解密就会失败,所以要用BASE64Decoder。