# Signature Calculation
This document describes the HMAC-SHA256 signature calculation process used for API authentication. The signature is generated by combining the request URL, query parameters, and request body into a signature string, then computing an HMAC-SHA256 hash using a secret key.
**Algorithm**: HMAC-SHA256
**Encoding**: Base64
**Signature Header**: X-App-Signature
## Signature Generation Process
Step 1: Extract Request Components
1. **URL**: Extract the base URL (without query parameters)
2. **Query Parameters**: Extract all enabled query parameters
3. **Request Body**: Process the request body content
Step 2: Build Signature String
The signature string is constructed by concatenating the following components with `&` as separator:
```curl
signature_string = URL + "&" + sorted_query_params + "&" + request_body
```
2.1 URL Processing
- Use the base URL without query parameters
- Example: `https://api.example.com/v1/orders`
2.2 Query Parameters Processing
- Extract all enabled query parameters
- Sort parameters by key name (alphabetical order)
- Format each parameter as `key=value`
- Join multiple parameters with `&`
- Handle null/undefined values as empty strings
**Example:**
```curl
Original parameters: {c: "3", a: "1", b: "2"}
Sorted result: a=1&b=2&c=3
```
2.3 Request Body Processing
- JSON Format: Use JSON string directly
- Null Value Handling: Empty objects `{}` or empty strings will be skipped
- Type Conversion: Non-string types will be converted to JSON strings
Step 3: Calculate HMAC-SHA256 Signature
1. Use the constructed signature string as the message
2. Use the provided secret key
3. Calculate HMAC-SHA256 hash
4. Encode the result in Base64 format
## Examples
Example 1: GET Request with Query Parameters
**Request:**
``` curl
URL: https://api.example.com/v1/users
Query Parameters: {page: "2", limit: "10", sort: "name"}
Body: (empty)
```
**Signature String:**
``` curl
https://api.example.com/v1/users&limit=10&page=2&sort=name
```
Example 2: POST Request with JSON Body
**Request:**
``` curl
URL: https://api.example.com/v1/orders
Query Parameters: (empty)
Body: {"userId": 123, "productId": 456, "quantity": 2}
```
**Signature String:**
``` curl
https://api.example.com/v1/orders&{"userId":123,"productId":456,"quantity":2}
```
Example 3: PUT Request with Both Query Parameters and Body
**Request:**
```curl
URL: https://api.example.com/v1/products
Query Parameters: {version: "v2", format: "json"}
Body: {"name": "Product A", "price": 99.99}
```
**Signature String:**
```curl
https://api.example.com/v1/products&format=json&version=v2&{"name":"Product A","price":99.99}
```
## Signature calculation script example
- Below is a complete calculation script example of the signature (**JAVA**). You can refer to it to implement the signature calculation script of your development language.
```java
import org.apache.commons.lang.StringUtils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Map;
import java.util.TreeMap;
public class SignatureUtils {
/**
* generate signature
* @param url: request url
* @param queryParamMap: request query parameters Map Structure
* @param bodyJSONString: request body JSON String
* @param clientSecret: client secret
* @return
*/
public String generateSignature(String url, Map queryParamMap, String bodyJSONString, String clientSecret) {
StringBuilder sb = new StringBuilder();
// step 1: concat url
sb.append(url);
// step 2: concat query parameters
if (!queryParamMap.isEmpty()) {
// 2.1: sort by alphabetical order
TreeMap sortedParams = new TreeMap<>(queryParamMap);
// 2.2: concat key and value
for (Map.Entry entry : sortedParams.entrySet()) {
String key = entry.getKey();
String value = entry.getValue() != null ? entry.getValue().toString() : null;
sb.append("&");
sb.append(key + "=" + value);
}
}
// step 3: concat body
if (StringUtils.isNotBlank(bodyJSONString)) {
sb.append("&");
sb.append(bodyJSONString);
}
// step 4: got a complete string waiting to generate a signature
String signatureString = sb.toString();
try {
// step 5: initialize the HmacSHA256 algorithm with the clientSecret
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(clientSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
// step 6: calculate signature
byte[] bytes = mac.doFinal(signatureString.getBytes(StandardCharsets.UTF_8));
// step 7: encode the signature in Base64 format
String signature = new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8);
return signature;
}catch (NoSuchAlgorithmException e){
throw new IllegalStateException("Algorithm HmacSHA256 does not exist.", e);
}catch (InvalidKeyException e){
throw new RuntimeException("Key is invalid.", e);
}
}
}
```
## Signature tools
You can easily calculate the correct signature through this tool, helping you quickly verify whether your code implementation is correct. >>> Click here