|
|
@@ -0,0 +1,479 @@
|
|
|
+package org.springblade.common.utils.encrypt;
|
|
|
+
|
|
|
+import cn.hutool.core.codec.Base64;
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import lombok.SneakyThrows;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springblade.common.aop.bean.JsonRequest;
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
+
|
|
|
+import javax.crypto.Cipher;
|
|
|
+import java.beans.BeanInfo;
|
|
|
+import java.beans.Introspector;
|
|
|
+import java.beans.PropertyDescriptor;
|
|
|
+import java.lang.reflect.Method;
|
|
|
+import java.security.*;
|
|
|
+import java.security.interfaces.RSAPrivateKey;
|
|
|
+import java.security.interfaces.RSAPublicKey;
|
|
|
+import java.security.spec.InvalidKeySpecException;
|
|
|
+import java.security.spec.PKCS8EncodedKeySpec;
|
|
|
+import java.security.spec.X509EncodedKeySpec;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author July
|
|
|
+ * @version 1.0.0
|
|
|
+ * @ClassName RSAEncryptUtil.java
|
|
|
+ * @Description RSA秘钥工具
|
|
|
+ * @createTime 2021年11月02日 16:01:09
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+public class RSAEncryptUtil {
|
|
|
+
|
|
|
+ public static final String CHARSET = "UTF-8";
|
|
|
+
|
|
|
+ public static final String RSA_ALGORITHM = "RSA";
|
|
|
+ //定义签名算法
|
|
|
+ private final static String KEY_RSA_SIGNATURE = "MD5withRSA";
|
|
|
+ //定义公钥算法
|
|
|
+ private final static String KEY_RSA_PUBLICKEY = "RSAPublicKey";
|
|
|
+ //定义私钥算法
|
|
|
+ private final static String KEY_RSA_PRIVATEKEY = "RSAPrivateKey";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成秘钥对
|
|
|
+ */
|
|
|
+ private static Map<String, Object> generateKeyPairToMap() throws NoSuchAlgorithmException {
|
|
|
+ Map<String, Object> map = null;
|
|
|
+ try {
|
|
|
+ KeyPairGenerator generator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
|
|
|
+ KeyPair keyPair = generator.generateKeyPair();
|
|
|
+ // 公钥
|
|
|
+ RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
|
|
|
+ // 私钥
|
|
|
+ RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
|
|
|
+ // 将密钥封装为map
|
|
|
+ map = new HashMap<>();
|
|
|
+ map.put(KEY_RSA_PUBLICKEY, publicKey);
|
|
|
+ map.put(KEY_RSA_PRIVATEKEY, privateKey);
|
|
|
+ } catch (NoSuchAlgorithmException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成秘钥对
|
|
|
+ */
|
|
|
+ private static void generateKeyPair() throws NoSuchAlgorithmException {
|
|
|
+ KeyPairGenerator keyTools = KeyPairGenerator.getInstance(RSA_ALGORITHM);
|
|
|
+ KeyPair keyPair = keyTools.generateKeyPair();
|
|
|
+ System.out.println(Base64.encode(keyPair.getPrivate().getEncoded()));
|
|
|
+ System.out.println(Base64.encode(keyPair.getPublic().getEncoded()));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 得到公钥
|
|
|
+ *
|
|
|
+ * @param publicKey 密钥字符串(经过base64编码)
|
|
|
+ * @throws Exception
|
|
|
+ */
|
|
|
+ public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
|
|
+ //通过X509编码的Key指令获得公钥对象
|
|
|
+ KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
|
|
|
+ X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decode(publicKey));
|
|
|
+ RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
|
|
|
+ return key;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取秘钥对中的公钥
|
|
|
+ *
|
|
|
+ * @param map 秘钥对
|
|
|
+ * @return 公钥
|
|
|
+ */
|
|
|
+ public static String getPublicKey(Map<String, Object> map) {
|
|
|
+ String str = "";
|
|
|
+ try {
|
|
|
+ Key key = (Key) map.get(KEY_RSA_PUBLICKEY);
|
|
|
+ str = Base64.encode(key.getEncoded());
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return str;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 得到私钥
|
|
|
+ *
|
|
|
+ * @param privateKey 密钥字符串(经过base64编码)
|
|
|
+ * @throws Exception
|
|
|
+ */
|
|
|
+ public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
|
|
+ //通过PKCS#8编码的Key指令获得私钥对象
|
|
|
+ KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
|
|
|
+ PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
|
|
|
+ RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
|
|
|
+ return key;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取秘钥对中的私钥
|
|
|
+ *
|
|
|
+ * @param map 秘钥对
|
|
|
+ * @return 私钥
|
|
|
+ */
|
|
|
+ public static String getPrivateKey(Map<String, Object> map) {
|
|
|
+ String str = "";
|
|
|
+ try {
|
|
|
+ Key key = (Key) map.get(KEY_RSA_PRIVATEKEY);
|
|
|
+ str = Base64.encode(key.getEncoded());
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return str;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 公钥加密
|
|
|
+ *
|
|
|
+ * @param data
|
|
|
+ * @param publicKey
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public static String publicEncrypt(String data, RSAPublicKey publicKey) {
|
|
|
+ return encode(data, publicKey);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 公钥加密
|
|
|
+ *
|
|
|
+ * @param data
|
|
|
+ * @param publicKey
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public static String publicEncrypt(String data, String key) {
|
|
|
+ String result = null;
|
|
|
+ try {
|
|
|
+ byte[] bytes = Base64.decode(key);
|
|
|
+ // 取得公钥
|
|
|
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
|
|
|
+ KeyFactory factory = KeyFactory.getInstance(RSA_ALGORITHM);
|
|
|
+ PublicKey publicKey = factory.generatePublic(keySpec);
|
|
|
+ // 对数据加密
|
|
|
+ //Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", "BC");
|
|
|
+ Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
|
|
|
+ cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
|
|
+ byte[] encode = cipher.doFinal(data.getBytes());
|
|
|
+ // 再进行Base64加密
|
|
|
+ result = Base64.encode(encode);
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 公钥解密
|
|
|
+ *
|
|
|
+ * @param data
|
|
|
+ * @param publicKey
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+
|
|
|
+ public static String publicDecrypt(String data, RSAPublicKey publicKey) {
|
|
|
+ return decode(data, publicKey);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 私钥解密
|
|
|
+ *
|
|
|
+ * @param data
|
|
|
+ * @param privateKey
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+
|
|
|
+ public static String privateDecrypt(String data, RSAPrivateKey privateKey) {
|
|
|
+ return decode(data, privateKey);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 私钥解密
|
|
|
+ *
|
|
|
+ * @param data 加密数据
|
|
|
+ * @param key 私钥
|
|
|
+ */
|
|
|
+ public static String privateDecrypt(byte[] data, String key) {
|
|
|
+ String result = null;
|
|
|
+ try {
|
|
|
+ // 对私钥解密
|
|
|
+ byte[] bytes = Base64.decode(key);
|
|
|
+ // 取得私钥
|
|
|
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
|
|
|
+ KeyFactory factory = KeyFactory.getInstance(RSA_ALGORITHM);
|
|
|
+ PrivateKey privateKey = factory.generatePrivate(keySpec);
|
|
|
+ // 对数据解密
|
|
|
+ //Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", "BC");
|
|
|
+ Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
|
|
|
+ cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
|
|
+ return new String(cipher.doFinal(Base64.decode(data)), CHARSET);
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 私钥加密
|
|
|
+ *
|
|
|
+ * @param data
|
|
|
+ * @param privateKey
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+
|
|
|
+ public static String privateEncrypt(String data, RSAPrivateKey privateKey) {
|
|
|
+ return encode(data, privateKey);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private static String decode(String data, Key key) {
|
|
|
+ try {
|
|
|
+ Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
|
|
|
+ cipher.init(Cipher.DECRYPT_MODE, key);
|
|
|
+ return new String(cipher.doFinal(Base64.decode(data)), CHARSET);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private static String encode(String data, Key key) {
|
|
|
+ try {
|
|
|
+ Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
|
|
|
+ cipher.init(Cipher.ENCRYPT_MODE, key);
|
|
|
+ return Base64.encode(cipher.doFinal(data.getBytes()));
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * bean转map
|
|
|
+ *
|
|
|
+ * @param obj
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public static Map<String, Object> bean2Map(Object obj) {
|
|
|
+ if (obj == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ Map<String, Object> map = new HashMap<>();
|
|
|
+ try {
|
|
|
+ BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
|
|
|
+ PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
|
|
|
+ for (PropertyDescriptor property : propertyDescriptors) {
|
|
|
+ String key = property.getName();
|
|
|
+ // 过滤class属性
|
|
|
+ if (!key.equals("class")) {
|
|
|
+ // 得到property对应的getter方法
|
|
|
+ Method getter = property.getReadMethod();
|
|
|
+ Object value = getter.invoke(obj);
|
|
|
+ if (StringUtils.isEmpty(value)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ map.put(key, value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按照红黑树(Red-Black tree)的 NavigableMap 实现
|
|
|
+ * 按照字母大小排序
|
|
|
+ */
|
|
|
+ public static Map<String, Object> sort(Map<String, Object> map) {
|
|
|
+ if (map == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ Map<String, Object> result = new TreeMap<>((Comparator<String>) (o1, o2) -> {
|
|
|
+ return o1.compareTo(o2);
|
|
|
+ });
|
|
|
+ result.putAll(map);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 组合参数
|
|
|
+ *
|
|
|
+ * @param map
|
|
|
+ * @return 如:key1Value1Key2Value2....
|
|
|
+ */
|
|
|
+ public static String groupStringParam(Map<String, Object> map) {
|
|
|
+ if (map == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ StringBuffer sb = new StringBuffer();
|
|
|
+ for (Map.Entry<String, Object> item : map.entrySet()) {
|
|
|
+ if (item.getValue() != null) {
|
|
|
+ sb.append(item.getKey());
|
|
|
+ if (item.getValue() instanceof List) {
|
|
|
+ sb.append(JSON.toJSONString(item.getValue()));
|
|
|
+ } else {
|
|
|
+ sb.append(item.getValue());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用私钥对信息生成数字签名
|
|
|
+ *
|
|
|
+ * @param data 加密数据
|
|
|
+ * @param privateKey 私钥
|
|
|
+ */
|
|
|
+ public static String sign(byte[] data, String privateKey) {
|
|
|
+ String str = "";
|
|
|
+ try {
|
|
|
+ // 解密由base64编码的私钥
|
|
|
+ byte[] bytes = Base64.decode(privateKey);
|
|
|
+ // 构造PKCS8EncodedKeySpec对象
|
|
|
+ PKCS8EncodedKeySpec pkcs = new PKCS8EncodedKeySpec(bytes);
|
|
|
+ // 指定的加密算法
|
|
|
+ KeyFactory factory = KeyFactory.getInstance(RSA_ALGORITHM);
|
|
|
+ // 取私钥对象
|
|
|
+ PrivateKey key = factory.generatePrivate(pkcs);
|
|
|
+ // 用私钥对信息生成数字签名
|
|
|
+ Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
|
|
|
+ signature.initSign(key);
|
|
|
+ signature.update(data);
|
|
|
+ str = Base64.encode(signature.sign());
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return str;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验数字签名
|
|
|
+ *
|
|
|
+ * @param data 加密数据
|
|
|
+ * @param publicKey 公钥
|
|
|
+ * @param sign 数字签名
|
|
|
+ * @return 校验成功返回true,失败返回false
|
|
|
+ */
|
|
|
+ public static boolean verify(byte[] data, String publicKey, String sign) {
|
|
|
+ boolean flag = false;
|
|
|
+ try {
|
|
|
+ // 解密由base64编码的公钥
|
|
|
+ byte[] bytes = Base64.decode(publicKey);
|
|
|
+ // 构造X509EncodedKeySpec对象
|
|
|
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
|
|
|
+ // 指定的加密算法
|
|
|
+ KeyFactory factory = KeyFactory.getInstance(RSA_ALGORITHM);
|
|
|
+ // 取公钥对象
|
|
|
+ PublicKey key = factory.generatePublic(keySpec);
|
|
|
+ // 用公钥验证数字签名
|
|
|
+ Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
|
|
|
+ signature.initVerify(key);
|
|
|
+ signature.update(data);
|
|
|
+ flag = signature.verify(Base64.decode(sign));
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return flag;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @SneakyThrows
|
|
|
+ public static void main(String[] args) {
|
|
|
+ /****先给调用方分配一组RSA密钥和一个appId****/
|
|
|
+ //初始化RSA密钥
|
|
|
+ Map<String, Object> init = RSAEncryptUtil.generateKeyPairToMap();
|
|
|
+ //私钥
|
|
|
+ String privateKey = RSAEncryptUtil.getPrivateKey(init);
|
|
|
+ //公钥
|
|
|
+ String publicKey = RSAEncryptUtil.getPublicKey(init);
|
|
|
+ //appId,32位的uuid
|
|
|
+ String appId = getUUID32();
|
|
|
+ /****先给调用方分配一组RSA密钥和一个appId****/
|
|
|
+
|
|
|
+ /*****调用方(请求方)*****/
|
|
|
+ //业务参数
|
|
|
+ Map<String, Object> businessParams = new HashMap<>();
|
|
|
+ businessParams.put("name", "Longer");
|
|
|
+ businessParams.put("job", "程序猿");
|
|
|
+ businessParams.put("hobby", "打篮球");
|
|
|
+
|
|
|
+ JsonRequest jsonRequest = new JsonRequest();
|
|
|
+ jsonRequest.setRequestId(getUUID32());
|
|
|
+ jsonRequest.setAppId(appId);
|
|
|
+ jsonRequest.setTimestamp(System.currentTimeMillis());
|
|
|
+ //使用appId的前16位作为AES密钥,并对密钥进行rsa公钥加密
|
|
|
+ String aseKey = appId.substring(0, 16);
|
|
|
+ String aseKeyStr = RSAEncryptUtil.publicEncrypt(aseKey, publicKey);
|
|
|
+ jsonRequest.setAseKey(aseKeyStr);
|
|
|
+
|
|
|
+ //请求的业务参数进行加密
|
|
|
+ String body = "";
|
|
|
+ try {
|
|
|
+ body = AESEncryptUtil.encrypt(JSONObject.toJSONString(businessParams), aseKey, appId.substring(16));
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("报文加密异常", e);
|
|
|
+ }
|
|
|
+ jsonRequest.setBody(body);
|
|
|
+ //签名
|
|
|
+ Map<String, Object> paramMap = RSAEncryptUtil.bean2Map(jsonRequest);
|
|
|
+ paramMap.remove("sign");
|
|
|
+ // 参数排序
|
|
|
+ Map<String, Object> sortedMap = RSAEncryptUtil.sort(paramMap);
|
|
|
+ // 拼接参数:key1Value1key2Value2
|
|
|
+ String urlParams = RSAEncryptUtil.groupStringParam(sortedMap);
|
|
|
+ //私钥签名
|
|
|
+ String sign = RSAEncryptUtil.sign(urlParams.getBytes(), privateKey);
|
|
|
+ jsonRequest.setSign(sign);
|
|
|
+
|
|
|
+ /*****调用方(请求方)*****/
|
|
|
+
|
|
|
+ /*****接收方(自己的系统)*****/
|
|
|
+ //参数判空(略)
|
|
|
+ //appId校验(略)
|
|
|
+ //本条请求的合法性校验《唯一不重复请求;时间合理》(略)
|
|
|
+ //验签
|
|
|
+ Map<String, Object> paramMap2 = RSAEncryptUtil.bean2Map(jsonRequest);
|
|
|
+ paramMap2.remove("sign");
|
|
|
+ //参数排序
|
|
|
+ Map<String, Object> sortedMap2 = RSAEncryptUtil.sort(paramMap2);
|
|
|
+ //拼接参数:key1Value1key2Value2
|
|
|
+ String urlParams2 = RSAEncryptUtil.groupStringParam(sortedMap2);
|
|
|
+ //签名验证
|
|
|
+ boolean verify = RSAEncryptUtil.verify(urlParams2.getBytes(), publicKey, jsonRequest.getSign());
|
|
|
+ if (!verify) {
|
|
|
+ throw new RuntimeException("签名验证失败");
|
|
|
+ }
|
|
|
+ //私钥解密,获取aseKey
|
|
|
+ String aseKey2 = RSAEncryptUtil.privateDecrypt(jsonRequest.getAseKey().getBytes(), privateKey);
|
|
|
+ if (!StringUtils.isEmpty(jsonRequest.getBody())) {
|
|
|
+ // 解密请求报文
|
|
|
+ String requestBody = "";
|
|
|
+ try {
|
|
|
+ requestBody = AESEncryptUtil.decrypt(jsonRequest.getBody(), aseKey2, jsonRequest.getAppId().substring(16));
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("请求参数解密异常");
|
|
|
+ }
|
|
|
+ System.out.println("业务参数解密结果:" + requestBody);
|
|
|
+ }
|
|
|
+ /*****接收方(自己的系统)*****/
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String getUUID32() {
|
|
|
+ String uuid = UUID.randomUUID().toString();
|
|
|
+ uuid = uuid.replace("-", "");
|
|
|
+ return uuid;
|
|
|
+ }
|
|
|
+}
|