跳转至

签名介绍

签名规则

设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),key1=value1&key2=value2…,拼接成待签名字符串stringA。 对stringA做RSA签名,并进行base64编码,然后将结果赋值给sign字段。
特别注意以下重要规则:

  • 参数名ASCII码从小到大排序(字典序,区分大小写)
  • 如果参数的值为空则不参与签名
  • sign参数本身不参与签名
  • 除以上规则排除的字段,其他所有字段均需要参与签名

请求示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
{
    "attach": "",
    "body": "yujing_test",
    "clientIp": "2a01:e0a:3af:a010:1086:b026:67dd:bb1d",
    "currency": "CNY",
    "detail": "",
    "extra": "{\"appId\":\"\",\"openId\":\"oNzHb6upfqZxpUoaeIgmmop4nScI\"}",
    "isNeedShare": "false",
    "mchId": "93751497",
    "mchMemberInfo": "",
    "nonceStr": "XCHNIWGFKGWR",
    "notifyUrl": "https://onebuytest.shengpay.com/main/notify",
    "outTradeNo": "p0bihp26hidb4ua5x1qm",
    "signType": "RSA",
    "subMchId": "",
    "timeExpire": "20210715100526",
    "totalFee": "3",
    "tradeType": "wx_jsapi"
}
生成的待签名字符串样例:

body=yujing_test&clientIp=2a01:e0a:3af:a010:1086:b026:67dd:bb1d&currency=CNY&extra={"appId":"","openId":"oNzHb6upfqZxpUoaeIgmmop4nScI"}&isNeedShare=false&mchId=93751497&nonceStr=XCHNIWGFKGWR&notifyUrl=https://onebuytest.shengpay.com/main/notify&outTradeNo=p0bihp26hidb4ua5x1qm&signType=RSA&timeExpire=20210715100526&totalFee=3&tradeType=wx_jsapi

签名函数示例

 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
// 签名函数,仅作为示例
public String sign(Map<String, Object> params, String privateKey) throws Exception {
    // 检查并转换私钥格式
    if (privateKey.startsWith("-----")) {
        privateKey = privateKey.replace("-----BEGIN RSA PRIVATE KEY-----", "").replace("-----END RSA PRIVATE KEY-----", "").replaceAll("\r?\n", "");
    }

    // 过滤掉值为空的参数
    Map<String, Object> filteredParams = params.entrySet().stream()
        .filter(e -> e.getValue() != null && !"".equals(e.getValue()))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

    // 对参数进行排序并连接成一个字符串
    List<String> paramList = new TreeSet<>(filteredParams.keySet()).stream()
        .map(key -> String.format("%s=%s", key, filteredParams.get(key).toString()))
        .collect(Collectors.toList());
    String content = String.join("&", paramList);

    // 获取私钥对象
    KeyFactory kf = KeyFactory.getInstance("RSA");
    PKCS8EncodedKeySpec keySpecPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
    PrivateKey priKey = kf.generatePrivate(keySpecPKCS8);

    // 对连接的字符串进行签名
    Signature sign = Signature.getInstance("SHA1WithRSA");
    sign.initSign(priKey);
    sign.update(content.getBytes("utf-8"));
    return Base64.getEncoder().encodeToString(sign.sign());
}

public Map<String, Object> buildDemoMessage() throws Exception {
    HashMap<String, Object> params  = new HashMap<>();
    params.put("attach", "");
    params.put("body", "yujing_test");
    params.put("clientIp", "2a01:e0a:3af:a010:1086:b026:67dd:bb1d");
    params.put("currency", "CNY");
    params.put("detail", "");
    params.put("extra", "{\"appId\":\"\",\"openId\":\"oNzHb6upfqZxpUoaeIgmmop4nScI\"}");
    params.put("isNeedShare", "false");
    params.put("mchId", "93751497");
    params.put("mchMemberInfo", "");
    params.put("outTradeNo", "p0bihp26hidb4ua5x1qm");
    params.put("subMchId", "");
    params.put("timeExpire", "20210715100526");
    params.put("totalFee", "3");
    params.put("tradeType", "wx_jsapi");
    params.put("notifyUrl", "https://onebuytest.shengpay.com/main/notify");
    params.put("nonceStr", "XCHNIWGFKGWR");
    params.put("signType", "RSA");

    String sign = sign(params, "your private key");
    params.put("sign", sign);

    return params;
}   
 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
// 签名函数,仅作为示例
function sign($params, $privateKey) {
    // 检查并转换私钥格式
    if (strpos($privateKey, '-----') === false) {
        $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" . implode("\n", str_split($privateKey, 64)) . "\n-----END RSA PRIVATE KEY-----\n";
    }

    // 过滤掉值为空的参数
    $filteredParams = array_filter($params, function($v, $k) {
        return isset($v) && (!is_string($v) || $v != '');
    }, ARRAY_FILTER_USE_BOTH);

    // 对参数进行排序并连接成一个字符串
    ksort($filteredParams);
    $paramList = array_map(function($v, $k) {
        return $k . '=' . $v;
    }, $filteredParams, array_keys($filteredParams));
    $content = implode('&', $paramList);

    // 对连接的字符串进行签名
    $sign = '';
    $priKey = openssl_get_privatekey($privateKey);
    openssl_sign($content, $sign, $priKey);
    openssl_free_key($priKey);

    return base64_encode($sign);
}

function buildDemoMessage() {
    $params = array(
        "clientIp" => "2a01:e0a:3af:a010:1086:b026:67dd:bb1d",
        "body" => "yujing_test",
        "attach" => "",
        "currency" => "CNY",
        "detail" => "",
        "extra" => "{\"appId\":\"\",\"openId\":\"oNzHb6upfqZxpUoaeIgmmop4nScI\"}",
        "isNeedShare" => "false",
        "mchId" => "93751497",
        "mchMemberInfo" => "",
        "outTradeNo" => "p0bihp26hidb4ua5x1qm",
        "subMchId" => "",
        "timeExpire" => "20210715100526",
        "totalFee" => "3",
        "tradeType" => "wx_jsapi",
        "notifyUrl" => "https://onebuytest.shengpay.com/main/notify",
        "nonceStr" => "XCHNIWGFKGWR",
        "signType" => "RSA"
    );

    $sign = sign($params, "your private key");
    $params["sign"] = $sign;

    return $params;
}
 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
// 签名函数,仅作为示例
func sign(params map[string]interface{}, privateKey string) string {
    // 检查并转换私钥格式
    if !strings.HasPrefix(privateKey, "-----") {
        privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" + privateKey + "\n-----END RSA PRIVATE KEY-----"
    }

    // 过滤掉值为空的参数
    keys := make([]string, 0, len(params))
    for k, v := range params {
        if v == nil {
            continue
        }
        if str, ok := v.(string); ok && str == "" {
            continue
        }
        keys = append(keys, k)
    }

    // 对参数进行排序并连接成一个字符串
    sort.Strings(keys)
    paramList := make([]string, 0, len(keys))
    for _, key := range keys {
        paramList = append(paramList, fmt.Sprintf("%s=%v", key, params[key]))
    }
    content := strings.Join(paramList, "&")

    // 获取私钥对象
    block, _ := pem.Decode([]byte(privateKey))
    priKey, _ := x509.ParsePKCS8PrivateKey(block.Bytes)

    // 对连接的字符串进行签名
    h := crypto.Hash.New(crypto.SHA1)
    h.Write([]byte(content))
    hashed := h.Sum(nil)
    rb, _ := rsa.SignPKCS1v15(rand.Reader, priKey.(*rsa.PrivateKey), crypto.SHA1, hashed)
    return base64.StdEncoding.EncodeToString(rb)
}

func buildDemoMessage() map[string]interface{} {
    params := map[string]interface{}{
        "attach":        "",
        "body":          "yujing_test",
        "clientIp":      "2a01:e0a:3af:a010:1086:b026:67dd:bb1d",
        "currency":      "CNY",
        "detail":        "",
        "extra":         "{\"appId\":\"\",\"openId\":\"oNzHb6upfqZxpUoaeIgmmop4nScI\"}",
        "isNeedShare":   "false",
        "mchId":         "93751497",
        "mchMemberInfo": "",
        "outTradeNo":    "p0bihp26hidb4ua5x1qm",
        "subMchId":      "",
        "timeExpire":    "20210715100526",
        "totalFee":      "3",
        "tradeType":     "wx_jsapi",
        "notifyUrl":     "https://onebuytest.shengpay.com/main/notify",
        "nonceStr":      "XCHNIWGFKGWR",
        "signType":      "RSA",
    }

    sign := sign(params, "your private key")
    params["sign"] = sign

    return params
}
Back to top