package com.nova.sankuai.domain.api.hengyidai;
|
|
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONObject;
|
import lombok.Data;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.io.IOUtils;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.http.*;
|
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
import org.springframework.stereotype.Component;
|
import org.springframework.web.client.RestTemplate;
|
import sun.misc.BASE64Decoder;
|
import sun.misc.BASE64Encoder;
|
|
import java.net.HttpURLConnection;
|
import java.net.URL;
|
import java.security.*;
|
import java.security.interfaces.RSAPrivateKey;
|
import java.security.interfaces.RSAPublicKey;
|
import java.security.spec.PKCS8EncodedKeySpec;
|
import java.security.spec.X509EncodedKeySpec;
|
import java.time.LocalDateTime;
|
import java.time.format.DateTimeFormatter;
|
import java.util.*;
|
|
/**
|
* RSA 加解密
|
* <p>
|
* 字符串格式的密钥在未在特殊说明情况下都为 BASE64 编码格式<br/>
|
* 由于非对称加密速度极其缓慢,一般文件不使用它来加密而是使用对称加密,<br/>
|
* 非对称加密算法可以用来对对称加密的密钥加密,这样保证密钥的安全也就保证了数据
|
* 的安全
|
* </P>
|
*
|
* @author kuangxiang
|
* @date 2021/05/19 15:31
|
*/
|
@Slf4j
|
@Component
|
@Data
|
public class HySignUtil {
|
|
|
/**
|
* 本地公钥
|
*/
|
@Value("${hengyidai.conf.local-public-key}")
|
private String local_public_key;
|
|
/**
|
* 本地私钥
|
*/
|
@Value("${hengyidai.conf.local-private-key}")
|
private String local_private_key;
|
|
/**
|
* 第三方公钥
|
*/
|
@Value("${hengyidai.conf.other-public-key}")
|
private String other_public_key;
|
|
/**
|
* 请求地址
|
*/
|
@Value("${hengyidai.conf.api-url}")
|
private String hyPath;
|
|
/**
|
* 产品编码
|
*/
|
@Value("${hengyidai.conf.product-no}")
|
private String productNo;
|
|
/**
|
* 加密算法 RSA
|
*/
|
public static final String KEY_ALGORITHM = "RSA";
|
/**
|
* 签名算法
|
*/
|
public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
|
/**
|
* 获取公钥的 key
|
*/
|
private static final String PUBLIC_KEY = "RSAPublicKey";
|
/**
|
* 获取私钥的 key
|
*/
|
private static final String PRIVATE_KEY = "RSAPrivateKey";
|
|
/**
|
* 生成数字签名 sign
|
*
|
* @param data 待加密数据
|
* @param privateKey 私钥(BASE64 编码)
|
* @return
|
* @throws Exception
|
*/
|
public static String sign(String data, String privateKey) throws Exception {
|
byte[] keyBytes = decode(privateKey);
|
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
|
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
|
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
|
signature.initSign(privateK);
|
signature.update(data.getBytes());
|
return encode(signature.sign());
|
}
|
|
/**
|
* 验签
|
*
|
* @param data 已加密数
|
* @param publicKey 公钥(BASE64 编码)
|
* @param sign 数字签
|
* @return
|
* @throws Exception
|
*/
|
public static boolean verify(String data, String publicKey, String sign) throws Exception {
|
byte[] keyBytes = decode(publicKey);
|
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
|
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
PublicKey publicK = keyFactory.generatePublic(keySpec);
|
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
|
signature.initVerify(publicK);
|
signature.update(data.getBytes());
|
return signature.verify(decode(sign));
|
}
|
|
/**
|
* 获取私钥
|
*
|
* @param keyMap 密钥对
|
* @return
|
* @throws Exception
|
*/
|
public static String getPrivateKey(Map<String, Object> keyMap)
|
throws Exception {
|
Key key = (Key) keyMap.get(PRIVATE_KEY);
|
return encode(key.getEncoded());
|
}
|
|
/**
|
* 获取公钥
|
*
|
* @param keyMap 密钥对
|
* @return
|
* @throws Exception
|
*/
|
public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
|
Key key = (Key) keyMap.get(PUBLIC_KEY);
|
return encode(key.getEncoded());
|
}
|
|
/**
|
* 生成密钥对(公钥和私钥)
|
*
|
* @return
|
* @throws Exception
|
*/
|
public static Map<String, Object> genKeyPair() throws Exception {
|
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
|
keyPairGen.initialize(2048);
|
KeyPair keyPair = keyPairGen.generateKeyPair();
|
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
|
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
|
Map<String, Object> keyMap = new HashMap<>(2);
|
keyMap.put(PUBLIC_KEY, publicKey);
|
keyMap.put(PRIVATE_KEY, privateKey);
|
return keyMap;
|
}
|
|
/**
|
* 按 key 进行正序排列,之间以&相连
|
* <功能描述>
|
*
|
* @param params
|
* @return
|
*/
|
public static String getSortParams(Map<String, Object> params) {
|
Map<String, Object> map = new TreeMap<>(Comparator.naturalOrder());
|
map.putAll(params);
|
StringBuffer stringBuffer = new StringBuffer();
|
map.forEach((k, v) -> stringBuffer.append(k).append("=").append(v).append("&"));
|
String str = stringBuffer.toString();
|
if (str.length() > 0) {
|
str = str.substring(0, str.length() - 1);
|
}
|
return str;
|
}
|
|
public static String getSortParams(String data) {
|
return jsonObjConverSortStr(data);
|
}
|
|
/**
|
* BASE64 字符串转二进制数据
|
*
|
* @param base64
|
* @return
|
* @throws Exception
|
*/
|
private static byte[] decode(String base64) throws Exception {
|
return new BASE64Decoder().decodeBuffer(base64);
|
}
|
|
/**
|
* 二进制数据转 BASE64 字符串
|
*
|
* @param bytes
|
* @return
|
* @throws Exception
|
*/
|
private static String encode(byte[] bytes) {
|
return new BASE64Encoder().encode(bytes);
|
}
|
|
/**
|
* 图片转为Base64字符串
|
*
|
* @param url
|
* @return
|
*/
|
public static String pictureToBase64(String url) {
|
String encode = null;
|
try {
|
HttpURLConnection httpUrl = (HttpURLConnection) new URL(url).openConnection();
|
httpUrl.connect();
|
encode = encode(IOUtils.toByteArray(httpUrl.getInputStream()));
|
httpUrl.disconnect();
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
return encode;
|
}
|
|
/**
|
* 加签
|
*
|
* @param data 参数
|
* @return
|
* @throws Exception
|
*/
|
public String buildRequest(String data) throws Exception {
|
// 时间戳
|
String timestamp = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now());
|
// 构建签名
|
JSONObject root = new JSONObject();
|
root.put("data", data);
|
root.put("timestamp", timestamp);
|
root.put("productNo", productNo);
|
// 排序并拼接成字符串
|
String paramsStr = getSortParams(root);
|
String sign = HySignUtil.sign(paramsStr, local_private_key);
|
root.put("sign", sign);
|
return root.toJSONString();
|
}
|
|
|
/**
|
* @param strReq
|
* @return
|
*/
|
public static Map<String, Object> parseHyRequest(Map<String, String> strReq) {
|
String code = strReq.get("code");
|
String msg = strReq.get("msg");
|
String data = strReq.get("data");
|
//构建响应返回参数
|
Map<String, Object> map = new HashMap<>();
|
map.put("code", code);
|
map.put("msg", msg);
|
map.put("data", data);
|
return map;
|
}
|
|
/**
|
* 验签
|
*
|
* @param strReq
|
* @return
|
* @throws Exception
|
*/
|
public Map<String, Object> parseRequest(String strReq) throws Exception {
|
Map<String, Object> map = JSON.parseObject(strReq, Map.class);
|
JSONObject root = new JSONObject();
|
root.put("productNo", map.get("productNo"));
|
root.put("timestamp", map.get("timestamp"));
|
root.put("data", map.get("data"));
|
//排序并拼接成字符串
|
String paramsStr2 = getSortParams(root.toJSONString());
|
// 验签
|
boolean passed = verify(paramsStr2, other_public_key, (String) map.get("sign"));
|
if (!passed) {
|
throw new RuntimeException("验签失败");
|
}
|
return map;
|
}
|
|
|
public static String jsonObjConverSortStr(String jsonText) {
|
StringBuffer returnBuStr = new StringBuffer();
|
jsonObjParseRecur(returnBuStr, jsonText, true);
|
return returnBuStr.toString();
|
}
|
|
private static void jsonObjParseRecur(StringBuffer returnBuStr, String jsonText, boolean isComplexObj) {
|
JSONObject jsonObj = JSONObject.parseObject(jsonText);
|
Iterator<String> keys = jsonObj.keySet().iterator();
|
// 局部 key 集合 并排序
|
Set<String> keySetSort = new HashSet<>();
|
while (keys.hasNext()) {
|
String key = keys.next();
|
keySetSort.add(key);
|
}
|
List<String> sortList = new ArrayList<>(keySetSort);
|
Collections.sort(sortList);
|
// 开始遍历 递归解析
|
for (String key : sortList) {
|
if (returnBuStr.length() == 0 || isComplexObj) {
|
returnBuStr.append(key).append("=");
|
isComplexObj = false;
|
} else {
|
returnBuStr.append("&").append(key).append("=");
|
}
|
StringBuffer sbSubArr = new StringBuffer();
|
if (jsonObj.get(key) instanceof JSONArray) { // 类型 - JSONArray
|
JSONArray jsonArray = jsonObj.getJSONArray(key);
|
returnBuStr.append("[");
|
for (int i = 0; i < jsonArray.size(); i++) {
|
Object obj = jsonArray.get(i);
|
// JSONArray - 依然是数组元素
|
if (obj instanceof JSONArray) {
|
String jsonArrayObjString = obj.toString();
|
if (sbSubArr.length() == 0) {
|
sbSubArr.append(jsonArrayObjString);
|
} else {
|
sbSubArr.append(",").append(jsonArrayObjString);
|
}
|
continue;
|
}
|
// JSONArray - jsonObj 复杂对象
|
if (obj instanceof JSONObject) {
|
JSONObject jsonObjTemp = jsonArray.getJSONObject(i);
|
if (i == 0) {
|
sbSubArr.append("{");
|
jsonObjParseRecur(sbSubArr, jsonObjTemp.toString(), true);
|
sbSubArr.append("}");
|
} else {
|
sbSubArr.append(",{");
|
jsonObjParseRecur(sbSubArr, jsonObjTemp.toString(), true);
|
sbSubArr.append("}");
|
}
|
}
|
// JSONArray - 普通元素
|
if (!(obj instanceof JSONArray) && !(obj instanceof JSONObject)) {
|
if (sbSubArr.length() == 0) {
|
sbSubArr.append(obj.toString());
|
} else {
|
sbSubArr.append(",").append(obj.toString());
|
}
|
}
|
}
|
returnBuStr.append(sbSubArr);
|
returnBuStr.append("]");
|
} else if (jsonObj.get(key) instanceof JSONObject) { // 类型 - JSONObject
|
returnBuStr.append("{");
|
jsonObjParseRecur(returnBuStr, jsonObj.getString(key), true);
|
returnBuStr.append("}");
|
} else { // 以上两种情况都不是的情况下,则为普通元素
|
returnBuStr.append(jsonObj.get(key).toString());
|
}
|
}
|
}
|
|
/**
|
* 方法描述 : 发送post请求 MediaType=application/json;charset=UTF-8
|
*
|
* @author : sunjb
|
*/
|
public <T> T sendPostJson(String url, Map<String, String> headerParams, Object obj, Class<T> cla, Integer readTimeOut) throws Exception {
|
|
String params = JSONObject.toJSONString(obj);
|
|
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
|
// 4s 连接时间 4s 超时时间
|
requestFactory.setConnectTimeout(8);
|
requestFactory.setReadTimeout(readTimeOut);
|
RestTemplate restTemplate = new RestTemplate(requestFactory);
|
HttpHeaders httpHeaders = new HttpHeaders();
|
// for (Map.Entry<String,String> entry : headerParams.entrySet()) {
|
// httpHeaders.add(entry.getKey(),entry.getValue());
|
// }
|
HttpMethod httpMethod = HttpMethod.POST;
|
// 以表单的方式提交
|
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
|
//将请求头部和参数合成一个请求
|
HttpEntity<String> httpEntity = new HttpEntity(params, httpHeaders);
|
|
ResponseEntity responseEntity = null;
|
try {
|
responseEntity = restTemplate.exchange(url, httpMethod, httpEntity, cla);
|
} catch (Exception e) {
|
throw new RuntimeException(e.getMessage());
|
}
|
|
return (T) responseEntity.getBody();
|
}
|
}
|