昨天有个功能,跟其他系统对接,所以研究了下AES加密,综合的几个博客,新建了个demo。
效果预览,原理什么的就不解释了(因为我也不大懂(-_ - *),看着好像都差不多,凑合方法整成能用就行)
效果预览
好处是不用外部jar包
新建个随便整个springboot工程,搞个手写个工具类
package com.encryption.demo;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.util.Arrays;
/**
* @Description AES加密工具类
* @Author 古大帅哥
* @Date 2020/5/20 14:19
*/
public class AesUtils {
private IvParameterSpec iv;
private SecretKeySpec keySpec;
public AesUtils(byte[] aesKey) {
if (aesKey == null || aesKey.length < 16) {
throw new RuntimeException("错误的初始密钥");
}
keySpec = new SecretKeySpec(aesKey, "AES");
this.iv = new IvParameterSpec(getMD5Bytes(aesKey));
}
public AesUtils(byte[] aesKey, byte[] iv) {
if (aesKey == null || aesKey.length < 16 || (iv != null && iv.length < 16)) {
throw new RuntimeException("错误的初始密钥");
}
if (iv == null) {
iv = getMD5Bytes(aesKey);
}
keySpec = new SecretKeySpec(aesKey, "AES");
this.iv = new IvParameterSpec(iv);
}
public byte[] encryptBytes(String data){
Cipher cipher;
byte[] result = null;
try {
cipher = Cipher.getInstance("AES/CFB/NoPadding");
try {
cipher.init(cipher.ENCRYPT_MODE, keySpec, iv);
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
result = cipher.doFinal(data.getBytes());
} catch (NoSuchPaddingException | InvalidKeyException | NoSuchAlgorithmException | IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
return result;
}
public String decryptBytes(byte[] data){
Cipher cipher;
try {
cipher = Cipher.getInstance("AES/CFB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
data = cipher.doFinal(data);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return new String(data);
}
public static Key randomKey(int size) {
byte[] result = null;
Key key;
try {
KeyGenerator gen = KeyGenerator.getInstance("AES");
gen.init(size, new SecureRandom());
result = gen.generateKey().getEncoded();
key = new SecretKeySpec(result, "AES");
} catch (Exception e) {
throw new RuntimeException(e);
}
return key;
}
public byte[] getMD5Bytes(byte[] data){
MessageDigest md5 = null;
try{
md5 = MessageDigest.getInstance("MD5");
}catch (Exception e){
e.printStackTrace();
}
String s = new String(data);
char[] charArray = s.toCharArray();
byte[] byteArray = new byte[charArray.length];
for (int i = 0; i < charArray.length; i++) {
byteArray[i] = (byte) charArray[i];
}
byte[] md5Bytes = md5.digest(byteArray);
StringBuffer hexValue = new StringBuffer();
for (int i = 0; i < md5Bytes.length; i++){
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16){
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString().substring(8,24).getBytes();
}
public static SecretKeySpec getKey(String password) throws UnsupportedEncodingException {
int keyLength = 128;
byte[] keyBytes = new byte[keyLength / 8];
Arrays.fill(keyBytes, (byte) 0x0);
byte[] passwordBytes = password.getBytes("UTF-8");
int length = passwordBytes.length < keyBytes.length ? passwordBytes.length : keyBytes.length;
System.arraycopy(passwordBytes, 0, keyBytes, 0, length);
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
return key;
}
}
demo测试类
为了模拟接受端和发送端,另建了个工具类AesUtils2,代码跟上面的一样,就不重复贴了
package com.encryption;
import com.encryption.demo.AesUtils;
import com.encryption.demo.AesUtils2;
import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin;
import org.junit.jupiter.api.Test;
import java.io.UnsupportedEncodingException;
import java.security.Key;
/**
* @Description 加密测试类
* @Author 古大帅哥
* @Date 2020/5/20 9:33
*/
public class EncryptionTest {
@Test
public void test() throws UnsupportedEncodingException {
//发送端
Key key1 = AesUtils.getKey("{>|?23432512343dskljffy@123");
AesUtils aesUtils = new AesUtils(key1.getEncoded());
String str = "{\"APY18\":\"FE企业版\",\"ISMORSPS_REGISTE\":\"2020-05-19 00:00:00.0\",\"APY17\":\"没有\",\"APY19\":\"22\",\"APY10\":\"场馆中心综合科科长\",\"APY12\":\"56\",\"APY11\":\"13766589422\",\"APY14\":\"6565\",\"APY13\":\"656\",\"APY16\":\"2018-12-26 00:00:00.0\",\"APY15\":\"6565\",\"SERVICE_ID\":\"5\",\"ACCOUNT_MANAGER\":\"徐xx\",\"LOCAL_MAINTAIN_END\":\"2020-05-19 00:00:00.0\",\"CONTRACT_NO\":\"\",\"APY32\":\"D4592347-0A29-530C-AC5A-CC7FF1DE515D\",\"APY31\":\"\",\"APY34\":\"FE6.0.2企业版\",\"APY33\":\"场馆管理中心APP开发项目\",\"UFIDA_PASSWORD\":\"4\",\"APY30\":\"4\",\"SERVICE_PASSWORD\":\"4\",\"LOCAL_MAINTAIN_BEGIN\":\"2018-12-27 00:00:00.0\",\"APY29\":\"是\",\"APY28\":\"4\",\"ISMORSPS_DUE\":\"2020-05-19 00:00:00.0\",\"APY8\":\"深圳信息职业技术学院\",\"APY21\":\"500\",\"APY20\":\"500\",\"ABC01\":\"终端客户\",\"APY23\":\"\",\"APY22\":\"565665\",\"APY4\":\"邓水平\",\"APY25\":\"mysql5.6\",\"ABC04\":\"深信息\",\"APY24\":\"\",\"APY27\":\"2018-12-26 00:00:00.0\",\"APY26\":\"44\",\"ABC06\":\"深圳市龙岗区龙翔大道\"}";
byte[] encriptBytes = aesUtils.encryptBytes(str);
//转换成16进制字符串传输
String s = HexBin.encode(encriptBytes);
//接收端
System.out.println("接受端获取密文" + s);
Key key2 = AesUtils2.getKey("{>|?23432512343dskljffy@123");
AesUtils2 aesUtils2 = new AesUtils2(key2.getEncoded());
//16进制转换成2进制
byte[] decode = HexBin.decode(s);
byte[] data = aesUtils2.decryptBytes(decode);
System.out.println("解密后明文:" + new String(data, "UTF-8"));
}
}
第二天,同事给了链接
https://hutool.cn/docs/#/crypto/%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86-SymmetricCrypto
看了看,默默把昨天的代码删掉,尼玛有这种现成好东西等老子弄完了才给我。。。。
区别是:
一、需要导包
二、更简单
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.5</version>
</dependency>
demo上面文档里面有,由于过于简单,贴个解密的demo,加密基本一样
package com.testhttp.httprequest.controller;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin;
import com.testhttp.httprequest.utils.AesUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.Key;
/**
* @Description
* @Author 古大帅哥
* @Date 2020/5/21 11:30
*/
@Controller
public class HelloController {
private static String LOCAL_KEY = "11111111";
@ResponseBody
@RequestMapping("/getMessage")
public String getMessage(@RequestParam("data") String data, @RequestParam("key") String key){
System.out.println("接受密钥:"+ key);
System.out.println("接受密文:" + data);
String realkey = SecureUtil.md5(key).substring(7, 15);
try {
//解密
AES aes = new AES(Mode.CTS, Padding.PKCS5Padding, (LOCAL_KEY + realkey).getBytes("UTF-8"), "0102030405060708".getBytes("UTF-8"));
String ss = aes.decryptStr(data, CharsetUtil.CHARSET_UTF_8);
System.out.println("解密后密钥:" + realkey);
System.out.println("解密后明文:" + ss);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return "hello";
}
}
其实代码就两行,
AES aes = new AES(Mode.CTS, Padding.PKCS5Padding, (“16位长度的字符串”).getBytes("UTF-8"), "0102030405060708".getBytes("UTF-8"));
String ss = aes.decryptStr(data, CharsetUtil.CHARSET_UTF_8);
加密同样,只要保证那16位字符串的密钥相同即可
所以我做了写其他的骚操作,如demo中的,我传了个动态字符串key,然后再MD5加密(固定返回32位字符串),截取其中的8位进行拼接还原真正的密钥(加密同理),这样被人家拦截也难篡改
部分请求端代码
public void sendMessage(String id) throws UnsupportedEncodingException {
String data = getAcceptJsonString(id);
Properties properties = null;
try {
properties = PropertiesLoaderUtils.loadProperties(new ClassPathResource("fy.properties"));
} catch (IOException e) {
System.err.println("找不到配置文件");
}
String key = properties.getProperty("ENCRYPT_KEY");
String s2 = RandomUtil.randomString(32);
String s3 = SecureUtil.md5(s2).substring(7, 15);
//拼接密钥,加密数据
AES aes = new AES(Mode.CTS, Padding.PKCS5Padding, (key + s3).getBytes("UTF-8"), "0102030405060708".getBytes("UTF-8"));
String s1 = aes.encryptHex(data);
Map<String,Object> map = new HashMap<>();
map.put("data", "map");
map.put("key", s2);
HttpRequest.post("http://localhost:8087/hello")
.form(data)
.timeout(20000)
.execute().body();
}
执行请求结果
不得不说,Hutool这个工具库实在太好用,开发效率很高,很多东西比如加密,发起http请求这些东西就帮你封装好,一两行代码就可以执行,给他点个赞👍