Skip to main content

How to calculate signature?

1. Description#

API Gateway verifies the identity of each API request, and the server will also verify whether the call parameters are valid. Therefore, each HTTP request must contain the signature information. The requests with invalid signature will be rejected.

API Gateway verifies the identity of the requests by the token that are assigned to your application. The token is used to generate the signature string in the HTTP request URL and server-side signature string. It must be kept strictly confidential.

If you compose HTTP request manually (instead of using the official SDK), you need to understand the following signature algorithm.

The process of generating the signature is as follows:

  1. Sort all request parameters (including system and application parameters, but except the “signature” and parameters with byte array type) according to the parameter name in ASCII table. For example:
Before sort: foo=1, bar=2, foo_bar=3, foobar=4
After sort: bar=2, foo=1, foo_bar=3, foobar=4
  1. concat all the key:value pairs into a string like '=' ':' ',' (Only outside the variable. Other values like "chanel":"alipay,wechat" keep "alipay,wechat". ) Concatenate the sorted parameters and their values into a string. For example:
bar2foo1foo_bar3foobar4
  1. Add API path in front of the concatenated string. For example, adding the API name “/test/api”:
/test/apibar2foo1foo_bar3foobar4
  1. Encode the concatenated string in UTF-8 format and make a digest by the signature algorithm (using HMAC_SHA256). For example:
hmac_sha256(/test/apibar2foo1foo_bar3foobar4)
  1. Convert the digest to hexadecimal format. For example:
hex("helloworld".getBytes("utf-8")) = "68656C6C6F776F726C64"

2. Samples#

Please find below some samples on how to caculate signature based on token(secret), API, and parameters.

2.1. PYTHON#

def sign(secret,api, parameters):
#===========================================================================
# @param secret
# @param parameters
#===========================================================================
sort_dict = sorted(parameters)
parameters_str = "%s%s" % (api,
str().join('%s%s' % (key, parameters[key]) for key in sort_dict))
h = hmac.new(secret.encode(encoding="utf-8"), parameters_str.encode(encoding="utf-8"), digestmod=hashlib.sha256)
return h.hexdigest().upper()

2.2. JAVA#

/**
* Sign the API request with body.
*/
public static String signApiRequest(Map<String, String> params, String body, String appSecret, String signMethod, String apiName) throws IOException {
// first: sort all text parameters
String[] keys = params.keySet().toArray(new String[0]);
Arrays.sort(keys);
// second: connect all text parameters with key and value
StringBuilder query = new StringBuilder();
query.append(apiName);
for (String key : keys) {
String value = params.get(key);
if (areNotEmpty(key, value)) {
query.append(key).append(value);
}
}
// third:put the body to the end
if (body != null) {
query.append(body);
}
// next : sign the whole request
byte[] bytes = null;
if(signMethod.equals(Constants.SIGN_METHOD_HMAC)) {
bytes = encryptWithHmac(query.toString(), appSecret);
} else if(signMethod.equals(Constants.SIGN_METHOD_SHA256)) {
bytes = encryptHMACSHA256(query.toString(), appSecret);
}
// finally : transfer sign result from binary to upper hex string
return byte2hex(bytes);
}
private static byte[] encryptHMACSHA256(String data, String secret) throws IOException {
byte[] bytes = null;
try {
SecretKey secretKey = new SecretKeySpec(secret.getBytes(Constants.CHARSET_UTF8), Constants.SIGN_METHOD_HMAC_SHA256);
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
bytes = mac.doFinal(data.getBytes(Constants.CHARSET_UTF8));
} catch (GeneralSecurityException gse) {
throw new IOException(gse.toString());
}
return bytes;
}
/**
* Transfer binary array to HEX string.
*/
public static String byte2hex(byte[] bytes) {
StringBuilder sign = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
sign.append("0");
}
sign.append(hex.toUpperCase());
}
return sign.toString();
}

2.3. C##

public static string SignRequest(IDictionary<string, string> parameters, string body, string appSecret, string signMethod, string apiName)
{
// first : sort all key with asci order
IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters, StringComparer.Ordinal);
// second : contact all params with key order
StringBuilder query = new StringBuilder();
query.Append(apiName);
foreach (KeyValuePair<string, string> kv in sortedParams)
{
if (!string.IsNullOrEmpty(kv.Key) && !string.IsNullOrEmpty(kv.Value))
{
query.Append(kv.Key).Append(kv.Value);
}
}
// third : add body to last
if (!string.IsNullOrEmpty(body))
{
query.Append(body);
}
// next : sign the string
byte[] bytes = null;
if (signMethod.Equals(Constants.SIGN_METHOD_SHA256))
{
HMACSHA256 sha256 = new HMACSHA256(Encoding.UTF8.GetBytes(appSecret));
bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(query.ToString()));
}
// finally : transfer binary byte to hex string
StringBuilder result = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
result.Append(bytes[i].ToString("X2"));
}
return result.ToString();
}

For the signature sample codes in other programming languages, refer to the source code of the official SDK.

3. Where can I find the token?#

You can find the token(secret) for generating the signature by login to the API gateway.

picture 2

In the example above 186d6c953c90f39c2973e6dd2e110d4057194996ef08fb4b3338180517b509c7 is the token.

4. How to debug signature error?#

There are two debugging features in the sandbox.

4.1. DO_NOT_CHECK#

You can disable the signature algorithm for testing purpose by setting it to "DO_NOT_CHECK" picture 1

4.2. Echo Signature Mode#

info

if Echo Signature Mode is on, all the API will be in echo mode, the reponse for each API call will be only with the valid signature and the input string of HMAC_SHA256 for debuging purpose.

Under the Integration -> Configuration menu of the gateway, you can open or close this signature calculation mode.

When the signature calculation mode is turned on, any request sent to the gateway will be used by the gateway to calculate the signature, and the calculated signature and the original string before the hash will be returned to the client to assist the client in verifying the implementation of the signature algorithm.

The following is an example of the return value after opening the signature calculation mode.

{
"reference": "3D8E509148E6AF7C8DC4F6DC6C090026AAC78681F887B7B29D7DC011744B5330",
"locked": false,
"error_message": "The correct signature is in the reference field, raw data before sign in note field for your debug purpose.",
"cleared": false,
"error_code": "DEBUG",
"note": "/api/v1/redirect/orders/1621348784.4028008providerKshertimestampvalue2",
"signature": "8A6D48E38B99BED18C8A0C538B078D179A91E68FDA1413B80738272DE8624EEB",
"force_clear": false
}

In the note field there is the original concatenated string before the HMAC-SHA256 operation, and there is the calculated hash value in the reference field.

5. Reference Design#