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 加解密
*
* 字符串格式的密钥在未在特殊说明情况下都为 BASE64 编码格式
* 由于非对称加密速度极其缓慢,一般文件不使用它来加密而是使用对称加密,
* 非对称加密算法可以用来对对称加密的密钥加密,这样保证密钥的安全也就保证了数据
* 的安全
*
*
* @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 keyMap)
throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return encode(key.getEncoded());
}
/**
* 获取公钥
*
* @param keyMap 密钥对
* @return
* @throws Exception
*/
public static String getPublicKey(Map keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return encode(key.getEncoded());
}
/**
* 生成密钥对(公钥和私钥)
*
* @return
* @throws Exception
*/
public static Map 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 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 params) {
Map 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 parseHyRequest(Map strReq) {
String code = strReq.get("code");
String msg = strReq.get("msg");
String data = strReq.get("data");
//构建响应返回参数
Map map = new HashMap<>();
map.put("code", code);
map.put("msg", msg);
map.put("data", data);
return map;
}
/**
* 验签
*
* @param strReq
* @return
* @throws Exception
*/
public Map parseRequest(String strReq) throws Exception {
Map 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 keys = jsonObj.keySet().iterator();
// 局部 key 集合 并排序
Set keySetSort = new HashSet<>();
while (keys.hasNext()) {
String key = keys.next();
keySetSort.add(key);
}
List 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 sendPostJson(String url, Map headerParams, Object obj, Class 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 entry : headerParams.entrySet()) {
// httpHeaders.add(entry.getKey(),entry.getValue());
// }
HttpMethod httpMethod = HttpMethod.POST;
// 以表单的方式提交
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
//将请求头部和参数合成一个请求
HttpEntity 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();
}
}