API 签名

所有请求需要时间戳和签名

生成签名的具体步骤如下:

将请求参数按照参数名称的字典序升序排列。

包括header里的timestamp和appid,以及请求body里的参数或url拼接的参数

将排序后的参数拼接成一个字符串,格式为:appid=xxx×timestamp=xxxx&key1=value1&key2=value2&...&keyN=valueN。
对拼接后的字符串进行 HMAC-SHA256 加密,使用 SecretKey(即秘钥)作为加密密钥。
将加密后的结果转换为十六进制字符串,即为生成的签名。

API签名生成范例

public class HmacUtil {

    private final static Charset UTF8 = StandardCharsets.UTF_8;

    public static String hmac256(String key, String msg) throws Exception {
        // 获取Mac实例并指定HmacSHA256算法
        Mac mac = Mac.getInstance("HmacSHA256");
        // 创建 SecretKeySpec实例 存储密钥信息 mac.getAlgorithm() 方法获取 Mac 实例使用的算法名称
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(UTF8), mac.getAlgorithm());
        // 密钥进行初始化
        mac.init(secretKeySpec);
        // 计算消息的 HmacSHA256 摘要值
        byte[] data = mac.doFinal(msg.getBytes(UTF8));
        // 将摘要值转换为十六进制字符串
        String hmac256Sign = DatatypeConverter.printHexBinary(data).toLowerCase();
        log.info("HmacSHA256 rawContent is [{}], key is [{}], hash result is [{}]", msg, key, hmac256Sign);
        return hmac256Sign;
    }

  

    public static String getStringToSign(Map<String, Object> params) {
        TreeMap<String, Object> treeMap = new TreeMap<>(params);
        StringBuilder s2s = new StringBuilder();
        for (String k : treeMap.keySet()) {
            if(StringUtils.hasText(k)){
                Object value = params.get(k);
                if(value instanceof Map){
                    continue;
                }else if(value instanceof List){
                    continue;
                }
                if (Objects.nonNull(value) && StrUtil.isNotEmpty(value.toString())) {
                    s2s.append(k).append("=").append(params.get(k).toString()).append("&");
                }
            }
        }
        return s2s.substring(0, s2s.length() - 1);
    }

    public static void main(String[] args) throws Exception {

        final String appid = "aaa";
        final String secretKey = "bbb";

        Map<String, Object> map = new HashMap<>();
        // 实际调用需要更新参数,这里仅作为演示签名验证通过的例子
        map.put("orderId", "1400006666");
        map.put("network", "TRX");
        map.put("amount", "1234");
        map.put("cryptoCurrency","USDT");
        map.put("fiat", "USD");
        map.put("type", "ONE");
        map.put("timestamp", String.valueOf(System.currentTimeMillis()));
        map.put("appid", appid);

        String sign = getStringToSign(map);
        log.info("sign is {}", sign);

        String hmac256Sign = hmac256(secretKey, sign);
        log.info("hmac256Sign is {}", hmac256Sign);

    }

}

const crypto = require('crypto');

class HmacUtil {
  static hmac256(key, msg) {
    const mac = crypto.createHmac('sha256', key);
    const data = mac.update(msg).digest('hex').toLowerCase();
    console.log(`HmacSHA256 rawContent is [${msg}], key is [${key}], hash result is [${data}]`);
    return data;
  }

  static getStringToSign(params) {
    const treeMap = new Map(Object.entries(params).sort());
    let s2s = '';

    for (const [k, v] of treeMap) {
      if (!k || typeof v === 'object') {
        continue;
      }
      if (v !== null && v !== undefined && String(v)) {
        s2s += `${k}=${v}&`;
      }
    }

    return s2s.slice(0, -1);
  }
}

const appid = 'aaa';
const secretKey = 'bbb';

const map = {
  orderId: '1400006666',
  network: 'TRX',
  amount: '1234',
  cryptoCurrency: 'USDT',
  fiat: 'USD',
  type: 'ONE',
  timestamp: String(Date.now()),
  appid: appid,
};

const sign = HmacUtil.getStringToSign(map);
console.log(`sign is ${sign}`);

const hmac256Sign = HmacUtil.hmac256(secretKey, sign);
console.log(`hmac256Sign is ${hmac256Sign}`);

import hashlib
import hmac
from typing import Dict

class HmacUtil:
    @staticmethod
    def hmac256(key: str, msg: str) -> str:
        # 将密钥和消息进行 HmacSHA256 计算
        data = hmac.new(key.encode(), msg.encode(), hashlib.sha256).digest()
        # 将摘要值转换为十六进制字符串
        hmac256Sign = data.hex().lower()
        print(f"HmacSHA256 rawContent is [{msg}], key is [{key}], hash result is [{hmac256Sign}]")
        return hmac256Sign

    @staticmethod
    def get_string_to_sign(params: Dict[str, object]) -> str:
        # 对参数按照字典序排序,生成待签名字符串
        s2s = '&'.join([f"{k}={v}" for k, v in sorted(params.items()) if v is not None and str(v)])
        return s2s

if __name__ == '__main__':
    appid = "aaa"
    secretKey = "bbb"

    params = {
        "orderId": "1400006666",
        "network": "TRX",
        "amount": "1234",
        "cryptoCurrency": "USDT",
        "fiat": "USD",
        "type": "ONE",
        "timestamp": str(int(time.time() * 1000)),
        "appid": appid
    }

    sign = HmacUtil.get_string_to_sign(params)
    print(f"sign is {sign}")

    hmac256Sign = HmacUtil.hmac256(secretKey, sign)
    print(f"hmac256Sign is {hmac256Sign}")

<?php

class HmacUtil {
  public static function hmac256($key, $msg) {
    // 将密钥和消息进行 HmacSHA256 计算
    $data = hash_hmac('sha256', $msg, $key, true);
    // 将摘要值转换为十六进制字符串
    $hmac256Sign = bin2hex($data);
    echo "HmacSHA256 rawContent is [{$msg}], key is [{$key}], hash result is [{$hmac256Sign}]\n";
    return $hmac256Sign;
  }

  public static function getStringToSign($params) {
    // 对参数按照字典序排序,生成待签名字符串
    ksort($params);
    $s2s = '';
    foreach ($params as $k => $v) {
      if (is_array($v)) {
        continue;
      } else if (!empty($v)) {
        $s2s .= "{$k}={$v}&";
      }
    }
    return rtrim($s2s, '&');
  }
}

$appid = 'aaa';
$secretKey = 'bbb';

$params = array(
  'orderId' => '1400006666',
  'network' => 'TRX',
  'amount' => '1234',
  'cryptoCurrency' => 'USDT',
  'fiat' => 'USD',
  'type' => 'ONE',
  'timestamp' => strval(time() * 1000),
  'appid' => $appid,
);

$sign = HmacUtil::getStringToSign($params);
echo "sign is {$sign}\n";

$hmac256Sign = HmacUtil::hmac256($secretKey, $sign);
echo "hmac256Sign is {$hmac256Sign}\n";

?>

package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"sort"
	"strings"
	"time"
)

func hmac256(key string, msg string) string {
	// 将密钥和消息进行 HmacSHA256 计算
	h := hmac.New(sha256.New, []byte(key))
	h.Write([]byte(msg))
	data := h.Sum(nil)
	// 将摘要值转换为十六进制字符串
	hmac256Sign := hex.EncodeToString(data)
	fmt.Printf("HmacSHA256 rawContent is [%s], key is [%s], hash result is [%s]\n", msg, key, hmac256Sign)
	return hmac256Sign
}

func getStringToSign(params map[string]interface{}) string {
	// 对参数按照字典序排序,生成待签名字符串
	keys := make([]string, 0, len(params))
	for k := range params {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	var s2s strings.Builder
	for _, k := range keys {
		if v, ok := params[k]; ok {
			switch t := v.(type) {
			case map[string]interface{}:
				continue
			case []interface{}:
				continue
			default:
				if str, ok := v.(string); ok && str != "" {
					s2s.WriteString(fmt.Sprintf("%s=%v&", k, v))
				}
			}
		}
	}
	return s2s.String()[:s2s.Len()-1]
}

func main() {
	appid := "aaa"
	secretKey := "bbb"

	params := map[string]interface{}{
		"orderId":        "1400006666",
		"network":        "TRX",
		"amount":         "1234",
		"cryptoCurrency": "USDT",
		"fiat":           "USD",
		"type":           "ONE",
		"timestamp":      fmt.Sprintf("%d", time.Now().UnixNano()/int64(time.Millisecond)),
		"appid":          appid,
	}

	sign := getStringToSign(params)
	fmt.Printf("sign is %s\n", sign)

	hmac256Sign := hmac256(secretKey, sign)
	fmt.Printf("hmac256Sign is %s\n", hmac256Sign)
}

import 'dart:convert';
import 'package:crypto/crypto.dart';

class HmacUtil {
  static String hmac256(String key, String msg) {
    // 将密钥和消息进行 HmacSHA256 计算
    var hmac = Hmac(sha256, utf8.encode(key));
    var data = hmac.convert(utf8.encode(msg)).bytes;
    // 将摘要值转换为十六进制字符串
    var hmac256Sign = hex.encode(data).toLowerCase();
    print('HmacSHA256 rawContent is [$msg], key is [$key], hash result is [$hmac256Sign]');
    return hmac256Sign;
  }

  static String getStringToSign(Map<String, dynamic> params) {
    // 对参数按照字典序排序,生成待签名字符串
    var keys = params.keys.toList()..sort();
    var s2s = StringBuffer();
    for (var k in keys) {
      if (params[k] is Map || params[k] is List) {
        continue;
      } else if (params[k] != null && params[k].toString().isNotEmpty) {
        s2s.write('$k=${params[k]}&');
      }
    }
    return s2s.toString().substring(0, s2s.length - 1);
  }
}

void main() {
  final appid = 'aaa';
  final secretKey = 'bbb';

  var map = <String, dynamic>{
    'orderId': '1400006666',
    'network': 'TRX',
    'amount': '1234',
    'cryptoCurrency': 'USDT',
    'fiat': 'USD',
    'type': 'ONE',
    'timestamp': '${DateTime.now().millisecondsSinceEpoch}',
    'appid': appid,
  };

  var sign = HmacUtil.getStringToSign(map);
  print('sign is $sign');

  var hmac256Sign = HmacUtil.hmac256(secretKey, sign);
  print('hmac256Sign is $hmac256Sign');
}

Webhook 签名的生成范例

public class MerSignCheckUtil {

    private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5',
        '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

    public static String getMerSign(String appId, String appSecret, String timestamp) {
        return encode("sha1", appId  + appSecret + timestamp);
    }


    private static String encode(String algorithm, String value) {
        if (value == null) {
            return null;
        }
        try {
            MessageDigest messageDigest
                = MessageDigest.getInstance(algorithm);
            messageDigest.update(value.getBytes());
            return getFormattedText(messageDigest.digest());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    private static String getFormattedText(byte[] bytes) {
        int len = bytes.length;
        StringBuilder buf = new StringBuilder(len * 2);
        for (int j = 0; j < len; j++) {
            buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
            buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
        }
        return buf.toString();
    }
}


// 生成时间戳 和sign
public static void main(String[] args) {
        String timestamp = String.valueOf(System.currentTimeMillis());
        System.out.println("timestamp = "+ timestamp);
        System.out.println("\n");
        System.out.println(MerSignCheckUtil.getMerSign("f83Is2**********", "4Yn*************", timestamp)); 
    }
//node v14.15.1
const sha1 = require('js-sha1');


function getMerSign(appId, appSecret, timestamp) {
  return sha1(appId + appSecret + String(timestamp));
}
#Python 3.7.4 
import hashlib
from time import time


def getMerSign(appId, appSecret, timestamp):
    strr = appId + appSecret + str(timestamp)
    encoded_str = strr.encode()
    hash_obj = hashlib.sha1(encoded_str)
    hexa_value = hash_obj.hexdigest()
    return hexa_value


timestamp = int(time() * 1000)
print("timestamp = ", timestamp)
print("\n")
res = getMerSign("f83Is2**********", "4Yn*************", timestamp)
print(res)
//PHP 8.0.7
<?php
function getMerSign($appId, $appSecret, $timestamp){
  $str = $appId.$appSecret.strval($timestamp);
  return sha1($str);
}
$timestamp = floor(microtime(true) * 1000);
printf("timestamp = %d\n", $timestamp);
$res = getMerSign("f83Is2**********", "4Yn*************", $timestamp);
print($res);
 ?>
//go version go1.18.2
package main

import (
    "crypto/sha1"
    "encoding/hex"
    "strconv"
)

func getMerSign(appId string, appSecret string, timestamp int64) string {
    s := appId + appSecret + strconv.FormatInt(timestamp, 10)
    h := sha1.New()
    h.Write([]byte(s))
    //io.WriteString(h, s)
    return hex.EncodeToString(h.Sum(nil))
}