wpc
2018-11-27 c52fb0e4d9168e75390b3cf3536d66c06c50d605
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
package com.huawei.android.hms.agent.pay;
 
import android.text.TextUtils;
import android.util.Base64;
 
import com.huawei.android.hms.agent.common.HMSAgentLog;
import com.huawei.hms.support.api.entity.pay.HwPayConstant;
import com.huawei.hms.support.api.entity.pay.OrderRequest;
import com.huawei.hms.support.api.entity.pay.PayReq;
import com.huawei.hms.support.api.entity.pay.ProductPayRequest;
import com.huawei.hms.support.api.pay.OrderResult;
import com.huawei.hms.support.api.pay.PayResultInfo;
import com.huawei.hms.support.api.pay.ProductPayResultInfo;
 
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
/**
 * 支付签名工具类,
 * 签名和验签需要使用到私钥和公钥。
 * 对于私钥是非常保密的信息,强烈建议将私钥存储在服务端,由开发者的服务器对请求字符串进行签名。签名方法可以参考本类的实现。
 * 对于单机应用没有服务器支持的,可以直接使用此工具类的接口。私钥要注意加密保护,以确保私钥不会被泄露。
 */
public final class PaySignUtil {
    /**
     * 使用加密算法规则
     */
    private static final String SIGN_ALGORITHMS = "SHA256WithRSA";
 
    /**
     * 字符串编码
     */
    private static final String CHARSET = "UTF-8";
 
    private PaySignUtil() {
    }
 
    /**
     * 对于私钥是非常保密的信息,强烈建议将私钥存储在服务端。
     * 开发者首先调用getStringForSign获取待签名字符串,然后将待签名字符串发送到服务器,由开发者服务端对待签名字符串进行签名。
     * 签名方法可以参考rsaSign方法的实现。
     * @deprecated
     * @param request 要计算签名的请求
     * @param privKey 私钥
     * @return 生成的签名字符串
     */
    public static String calculateSignString(PayReq request, String privKey) {
        return rsaSign(getStringForSign(request), privKey);
    }
 
    /**
     * 对于私钥是非常保密的信息,强烈建议将私钥存储在服务端。
     * 开发者首先调用getStringForSign获取待签名字符串,然后将待签名字符串发送到服务器,由开发者服务端对待签名字符串进行签名。
     * 签名方法可以参考rsaSign方法的实现。
     * @deprecated
     * @param request 要计算签名的请求
     * @param privKey 私钥
     * @return 生成的签名字符串
     */
    public static String calculateSignString(OrderRequest request, String privKey) {
        return rsaSign(getStringForSign(request), privKey);
    }
 
    /**
     * 对于私钥是非常保密的信息,强烈建议将私钥存储在服务端。
     * 开发者首先调用getStringForSign获取待签名字符串,然后将待签名字符串发送到服务器,由开发者服务端对待签名字符串进行签名。
     * 签名方法可以参考rsaSign方法的实现。
     * @deprecated
     * @param request 要计算签名的请求
     * @param privKey 私钥
     * @return 生成的签名字符串
     */
    public static String calculateSignString(ProductPayRequest request, String privKey) {
        return rsaSign(getStringForSign(request), privKey);
    }
 
    /**
     * 计算支付请求的待签名字符串
     * 在服务端进行签名的cp,可以将此方法返回的字符串传给自己发服务端进行签名
     * @param request 支付请求
     */
    public static String getStringForSign(PayReq request) {
 
        Map<String, Object>  params = new HashMap<String, Object>();
 
        // 必选参数
        params.put(HwPayConstant.KEY_MERCHANTID, request.getMerchantId());
        params.put(HwPayConstant.KEY_APPLICATIONID, request.getApplicationID());
        params.put(HwPayConstant.KEY_PRODUCTNAME, request.getProductName());
        params.put(HwPayConstant.KEY_PRODUCTDESC, request.getProductDesc());
        params.put(HwPayConstant.KEY_REQUESTID, request.getRequestId());
        params.put(HwPayConstant.KEY_AMOUNT, request.getAmount());
 
        // 可选参数
        params.put(HwPayConstant.KEY_PARTNER_IDS, request.getPartnerIDs());
        params.put(HwPayConstant.KEY_CURRENCY, request.getCurrency());
        params.put(HwPayConstant.KEY_COUNTRY, request.getCountry());
        params.put(HwPayConstant.KEY_URL, request.getUrl());
        params.put(HwPayConstant.KEY_URLVER, request.getUrlVer());
        params.put(HwPayConstant.KEY_EXPIRETIME, request.getExpireTime());
        params.put(HwPayConstant.KEY_SDKCHANNEL, request.getSdkChannel());
 
        return getNoSign(params, false);
    }
 
    /**
     * 计算查询订单请求的待签名字符串
     * 在服务端进行签名的cp,可以将此方法返回的字符串传给自己发服务端进行签名
     * @param request 查询订单请求
     */
    public static String getStringForSign(OrderRequest request) {
        Map<String, Object> params = new HashMap<String, Object>();
 
        // 必选
        params.put("merchantId", request.getMerchantId());
        params.put("requestId", request.getRequestId());
        params.put("keyType", request.getKeyType());
        params.put("time", request.getTime());
 
        return getNoSign(params, false);
    }
 
    /**
     * 计算PMS支付请求的待签名字符串
     * 在服务端进行签名的cp,可以将此方法返回的字符串传给自己发服务端进行签名
     * @param request PMS支付请求
     */
    public static String getStringForSign(ProductPayRequest request) {
        Map<String, Object>  params = new HashMap<String, Object>();
 
        // 必选参数
        params.put(HwPayConstant.KEY_MERCHANTID, request.getMerchantId());
        params.put(HwPayConstant.KEY_APPLICATIONID, request.getApplicationID());
        params.put(HwPayConstant.KEY_PRODUCT_NO, request.getProductNo());
        params.put(HwPayConstant.KEY_REQUESTID, request.getRequestId());
        params.put(HwPayConstant.KEY_SDKCHANNEL, request.getSdkChannel());
 
        // 可选参数
        params.put(HwPayConstant.KEY_URL, request.getUrl());
        params.put(HwPayConstant.KEY_URLVER, request.getUrlVer());
 
        return getNoSign(params, false);
    }
 
    /**
     * 对支付结果的签名进行校验
     * @param rst 支付结果
     * @param pubKey 公钥
     * @return 是否校验通过
     */
    public static boolean checkSign(PayResultInfo rst, String pubKey) {
 
        if (rst == null || pubKey == null) {
            return false;
        }
 
        Map<String, Object> paramsa = new HashMap<String, Object>();
 
        // 必选参数
        paramsa.put("returnCode", rst.getReturnCode());
        paramsa.put("userName", rst.getUserName());
        paramsa.put("requestId", rst.getRequestId());
        paramsa.put("amount", rst.getAmount());
        paramsa.put("time", rst.getTime());
 
        // 可选参数
        paramsa.put("orderID", rst.getOrderID());
        paramsa.put("withholdID", rst.getWithholdID());
        paramsa.put("errMsg", rst.getErrMsg());
 
        String noSignStr = getNoSign(paramsa, false);
        return doCheck(noSignStr, rst.getSign(), pubKey);
    }
 
    /**
     * 对支付结果的签名进行校验
     * @param rst 支付结果
     * @param pubKey 公钥
     * @return 是否校验通过
     */
    public static boolean checkSign(ProductPayResultInfo rst, String pubKey) {
 
        if (rst == null || pubKey == null) {
            return false;
        }
 
        Map<String, Object> paramsa = new HashMap<String, Object>();
 
        // 必选参数
        paramsa.put("returnCode", rst.getReturnCode());
        paramsa.put("merchantId", rst.getMerchantId());
        paramsa.put("requestId", rst.getRequestId());
        paramsa.put("productNo", rst.getProductNo());
        paramsa.put("microsAmount", rst.getMicrosAmount());
        paramsa.put("currency", rst.getCurrency());
        paramsa.put("country", rst.getCountry());
        paramsa.put("time", rst.getTime());
 
        // 可选参数
        paramsa.put("orderID", rst.getOrderID());
        paramsa.put("errMsg", rst.getErrMsg());
 
        String noSignStr = getNoSign(paramsa, false);
        return doCheck(noSignStr, rst.getSign(), pubKey);
    }
 
    /**
     * 对查询订单结果签名进行校验
     * @param result 查询订单结果
     * @param pubKey 公钥
     * @return 是否校验通过
     */
    public static boolean checkSign(OrderResult result, String pubKey) {
 
        if (result == null || pubKey == null) {
            return false;
        }
 
        Map<String, Object> paramsa = new HashMap<String, Object>();
        //必选参数
        paramsa.put("returnCode", result.getReturnCode());
        paramsa.put("returnDesc", result.getReturnDesc());
 
        //可选参数
        paramsa.put("requestId", result.getRequestId());
        paramsa.put("orderID", result.getOrderID());
        paramsa.put("status", result.getOrderStatus());
        paramsa.put("orderTime", result.getOrderTime());
        paramsa.put("tradeTime", result.getTradeTime());
 
        String noSignStr = getNoSign(paramsa, false);
        return doCheck(noSignStr, result.getSign(), pubKey);
    }
 
    /**
     * 根据参数map获取待签名字符串
     * @param params 待签名参数map
     * @param includeEmptyParam 是否包含值为空的参数:
     *                          与 HMS-SDK 支付能力交互的签名或验签,需要为false(不包含空参数)
     *                          由华为支付服务器回调给开发者的服务器的支付结果验签,需要为true(包含空参数)
     * @return 待签名字符串
     */
    private static String getNoSign(Map<String, Object> params, boolean includeEmptyParam) {
        //对参数按照key做升序排序,对map的所有value进行处理,转化成string类型
        //拼接成key=value&key=value&....格式的字符串
        StringBuilder content = new StringBuilder();
 
        // 按照key做排序
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);
 
        String value = null;
        Object object = null;
        boolean isFirstParm = true;
        for (int i = 0; i < keys.size(); i++) {
            String key = (String) keys.get(i);
            object = params.get(key);
 
            if (object == null) {
                value = "";
            }else if (object instanceof String) {
                value = (String) object;
            } else {
                value = String.valueOf(object);
            }
 
            if (includeEmptyParam || !TextUtils.isEmpty(value)) {
                content.append((isFirstParm ? "" : "&") + key + "=" + value);
                isFirstParm = false;
            } else {
                continue;
            }
        }
 
        //待签名的字符串
        return content.toString();
    }
 
    /**
     * 签名字符串(SHA256WithRSA)
     * @param content 待签名字符串
     * @param privateKey 私钥
     * @return 字符串的签名
     */
    public static String rsaSign(String content, String privateKey) {
 
        if (null == content || privateKey == null) {
            return null;
        }
 
        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey, Base64.DEFAULT));
            KeyFactory keyf = KeyFactory.getInstance("RSA");
            PrivateKey priKey = keyf.generatePrivate(priPKCS8);
            java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
            signature.initSign(priKey);
            signature.update(content.getBytes(CHARSET));
            byte[] signed = signature.sign();
            return Base64.encodeToString(signed, Base64.DEFAULT);
        } catch (Exception e) {
            //  这里是安全算法,为避免出现异常时泄露加密信息,这里不打印具体日志
            HMSAgentLog.e("rsaSign error");
        }
 
        return null;
    }
 
    /**
     * 校验签名信息
     * @param noSignStr 待校验未字符串
     * @param sign 签名字符串
     * @param publicKey 公钥
     * @return 是否校验通过
     */
    public static boolean doCheck(String noSignStr, String sign, String publicKey) {
 
        if (sign == null || noSignStr == null || publicKey == null) {
            return false;
        }
 
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = Base64.decode(publicKey, Base64.DEFAULT);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
 
            java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
 
            signature.initVerify(pubKey);
            signature.update(noSignStr.getBytes(CHARSET));
 
            return signature.verify(Base64.decode(sign, Base64.DEFAULT));
        } catch (Exception e) {
            //  这里是安全算法,为避免出现异常时泄露加密信息,这里不打印具体日志
            HMSAgentLog.e("doCheck error");
        }
        return false;
    }
}