Air
2024-11-04 29c405353029f033e187e8dc033b93385365ee43
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
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();
    }
}