🔐 HMAC-SHA256
HMAC-SHA256 là một thuật toán dùng để tạo chữ ký số (digital signature) nhằm đảm bảo tính toàn vẹn (integrity) và xác thực (authentication) của dữ liệu. HMAC (Hash-based Message Authentication Code) là một kỹ thuật mã hóa sử dụng:
-
Một hàm băm (hash function) — ví dụ như SHA-256
-
Một khóa bí mật (secret key)
-
Một thông điệp (message) cần xác thực
Kết quả là một chuỗi băm (digest) đại diện cho thông điệp đó.
🚀 Ứng dụng HMAC-SHA256 tại ZENGI
Tại Zengi, phần HMAC-SHA256 sẽ được thực hiện theo quy trình sau:
Sau đây là các đoạn code mẫu để phục vụ cho quy trình trên.
Code mẫu với Go:
// Function for signature a json object by a array key of json object
// BuildQueryStringFromJSON builds a string like "key1=value1&key2=value2" from a JSON map and a list of keys
func BuildQueryStringFromJSON(data map[string]interface{}, keys []string) (string, error) {
// If keys is empty, extract all keys from the data map
if len(keys) == 0 {
for key := range data {
keys = append(keys, key)
}
}
sort.Strings(keys) // consistent ordering
query := ""
for i, key := range keys {
val, ok := data[key]
if !ok {
return "", fmt.Errorf("key '%s' not found in data", key)
}
if i > 0 {
query += "&"
}
query += fmt.Sprintf("%s=%v", key, val)
}
return query, nil
}
func HmacAction(key string, jsonData map[string]interface{}, keyList []string) (string, string, string, error) {
if len(key) == 0 {
newKey, err := utils.GenerateSecretHex(16) //0 mean usse default length
if err != nil {
return "", "", "", err
}
key = newKey
}
log.Infof("key 1: %s", key)
queryStr, err := BuildQueryStringFromJSON(jsonData, keyList)
if err != nil {
return "", "", "", err
}
signData := utils.GenerateHMAC256(key, queryStr)
return key, queryStr, signData, nil
}
Code mẫu với Java
import java.util.*;
import java.util.stream.Collectors;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class HmacHelper {
// Build query string like "key1=value1&key2=value2"
public static String buildQueryStringFromJSON(Map<String, Object> data, List<String> keys) throws Exception {
if (keys == null || keys.isEmpty()) {
keys = new ArrayList<>(data.keySet());
}
Collections.sort(keys); // consistent ordering
StringBuilder query = new StringBuilder();
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
if (!data.containsKey(key)) {
throw new Exception("key '" + key + "' not found in data");
}
if (i > 0) query.append("&");
query.append(key).append("=").append(data.get(key));
}
return query.toString();
}
// HMAC SHA256 action
public static Result hmacAction(String key, Map<String, Object> jsonData, List<String> keyList) throws Exception {
if (key == null || key.isEmpty()) {
key = generateSecretHex(16); // 16 bytes -> 32 hex characters
}
System.out.println("key 1: " + key);
String queryStr = buildQueryStringFromJSON(jsonData, keyList);
String signData = generateHMAC256(key, queryStr);
return new Result(key, queryStr, signData);
}
// Generate a random 16-byte hex key
public static String generateSecretHex(int byteLength) {
byte[] bytes = new byte[byteLength];
new SecureRandom().nextBytes(bytes);
StringBuilder hex = new StringBuilder();
for (byte b : bytes) {
hex.append(String.format("%02x", b));
}
return hex.toString();
}
// Generate HMAC SHA256 signature
public static String generateHMAC256(String key, String data) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(secretKey);
byte[] hmacBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hmacBytes);
}
// Result holder class
public static class Result {
public final String key;
public final String queryStr;
public final String signData;
public Result(String key, String queryStr, String signData) {
this.key = key;
this.queryStr = queryStr;
this.signData = signData;
}
}
}
Code mẫu với python
import hashlib
import hmac
import base64
import secrets
from typing import Dict, List, Tuple
def generate_secret_hex(byte_length: int = 16) -> str:
return secrets.token_hex(byte_length)
def build_query_string_from_json(data: Dict[str, object], keys: List[str]) -> str:
if not keys:
keys = list(data.keys())
keys.sort()
query_parts = []
for key in keys:
if key not in data:
raise ValueError(f"Key '{key}' not found in data")
query_parts.append(f"{key}={data[key]}")
return "&".join(query_parts)
def generate_hmac256(key: str, message: str) -> str:
hmac_obj = hmac.new(key.encode(), message.encode(), hashlib.sha256)
return base64.b64encode(hmac_obj.digest()).decode()
def hmac_action(
key: str,
json_data: Dict[str, object],
key_list: List[str],
) -> Tuple[str, str, str]:
if not key:
key = generate_secret_hex(16)
print(f"key 1: {key}")
query_str = build_query_string_from_json(json_data, key_list)
sign_data = generate_hmac256(key, query_str)
return key, query_str, sign_data