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(); } }