Signature的生成方法
更新时间: 2025-08-01 15:30:47
1.伪代码流程
Algorithm: GenerateSignature
Input:
Output: signature: string
BEGIN
1. canonicalUri ← normalizeURI(uri)
2. canonicalQuery ← buildCanonicalQueryString(queryParams)
3. bodyHash ← calculateSHA256Hash(body)
4. canonicalRequest ← buildCanonicalRequest(httpMethod, canonicalUri, canonicalQuery, bodyHash)
5. stringToSign ← buildStringToSign(canonicalRequest)
6. decodedSecret ← decrypt("aqj", secretKey)
7. signature ← HMAC_SHA256(stringToSign, decodedSecret)
8. RETURN signature
END
2.核心函数说明
2.1 URI规范化 (canonicalURI)
目的: 将URI路径按RFC3986标准编码
处理: 分割路径段,编码特殊字符,重新组合
2.2 查询参数规范化 (canonicalQueryString)
目的: 将查询参数按字母顺序排序并编码
处理: 键值对排序,URI编码,用&连接
2.3 请求体哈希 (calculateBodyHash)
目的: 计算请求体的SHA-256哈希值
处理: 空体返回固定值,非空体计算SHA-256
2.4 规范化请求构建 (buildCanonicalRequest)
目的: 构建用于签名的规范化请求字符串
格式: METHOD\nURI\nQUERY\nBODY_HASH
2.5 签名字符串构建 (buildStringToSign)
目的: 构建最终用于HMAC的字符串
格式: "ACS3-HMAC-SHA256\n" + SHA256(canonicalRequest)
3.代码示例
Python实现
pythonimport hashlib
import hmac
import urllib.parse
from typing import Dict, List
class SignatureGenerator:
def __init__(self):
pass
def generate\_signature(self, http\_method: str, uri: str,
query\_params: Dict[str, List[str]],
body: bytes, secret\_key: str) -> str:
"""生成签名"""
canonical\_uri = self.\_canonical\_uri(uri)
canonical\_query = self.\_canonical\_query\_string(query\_params)
body\_hash = self.\_calculate\_body\_hash(body)
canonical\_request = self.\_build\_canonical\_request(
http\_method, canonical\_uri, canonical\_query, body\_hash
)
string\_to\_sign = self.\_build\_string\_to\_sign(canonical\_request)
decoded\_secret = self.\_decrypt("aqj", secret\_key)
return self.\_hmac\_sha256(string\_to\_sign, decoded\_secret)
def \_canonical\_uri(self, uri: str) -> str:
"""规范化URI"""
if not uri:
return "/"
segments = [seg for seg in uri.split("/") if seg]
encoded\_segments = [self.\_encode\_rfc3986(seg) for seg in segments]
return "/" + "/".join(encoded\_segments)
def \_canonical\_query\_string(self, params: Dict[str, List[str]]) -> str:
"""规范化查询参数"""
if not params:
return ""
items = []
for key in sorted(params.keys()):
value = params[key][0] if params[key] else ""
encoded\_key = self.\_encode\_rfc3986(key)
encoded\_value = self.\_encode\_rfc3986(value)
items.append(f"{encoded\_key}={encoded\_value}")
return "&".join(items)
def \_encode\_rfc3986(self, s: str) -> str:
"""RFC3986编码"""
unreserved = set('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-\_.~')
return ''.join(c if c in unreserved else f'%{ord(c):02X}' for c in s)
def \_calculate\_body\_hash(self, body: bytes) -> str:
"""计算请求体哈希"""
if not body:
return "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
return hashlib.sha256(body).hexdigest()
def \_build\_canonical\_request(self, method: str, uri: str,
query: str, body\_hash: str) -> str:
"""构建规范化请求"""
return f"{method}\n{uri}\n{query}\n{body\_hash}"
def \_build\_string\_to\_sign(self, canonical\_request: str) -> str:
"""构建签名字符串"""
hash\_obj = hashlib.sha256(canonical\_request.encode())
return f"ACS3-HMAC-SHA256\n{hash\_obj.hexdigest()}"
def \_hmac\_sha256(self, source: str, secret: str) -> str:
"""HMAC-SHA256签名"""
return hmac.new(secret.encode(), source.encode(), hashlib.sha256).hexdigest()
def \_decrypt(self, key: str, encrypted: str) -> str:
"""解密密钥(需要根据实际加密方式实现)"""
# 此处需要根据实际的解密逻辑实现
return encrypted # 简化处理
Java实现
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class SignatureGenerator {
public String generateSignature(String httpMethod, String uri,
Map<String, List<String>> queryParams,
byte[] body, String secretKey) {
try {
String canonicalUri = canonicalURI(uri);
String canonicalQuery = canonicalQueryString(queryParams);
String bodyHash = calculateBodyHash(body);
String canonicalRequest = buildCanonicalRequest(
httpMethod, canonicalUri, canonicalQuery, bodyHash
);
String stringToSign = buildStringToSign(canonicalRequest);
String decodedSecret = decrypt("aqj", secretKey);
return hmacSha256(stringToSign, decodedSecret);
} catch (Exception e) {
throw new RuntimeException("签名生成失败", e);
}
}
private String canonicalURI(String uri) {
if (uri == null || uri.isEmpty()) {
return "/";
}
String[] segments = uri.split("/");
List<String> encodedSegments = new ArrayList<>();
for (String segment : segments) {
if (!segment.isEmpty()) {
encodedSegments.add(encodeRFC3986(segment));
}
}
return "/" + String.join("/", encodedSegments);
}
private String canonicalQueryString(Map<String, List<String>> params) {
if (params == null || params.isEmpty()) {
return "";
}
List<String> sortedKeys = new ArrayList<>(params.keySet());
Collections.sort(sortedKeys);
List<String> items = new ArrayList<>();
for (String key : sortedKeys) {
String value = params.get(key).isEmpty() ? "" : params.get(key).get(0);
String encodedKey = encodeRFC3986(key);
String encodedValue = encodeRFC3986(value);
items.add(encodedKey + "=" + encodedValue);
}
return String.join("&", items);
}
private String encodeRFC3986(String s) {
StringBuilder result = new StringBuilder();
for (byte b : s.getBytes(StandardCharsets.UTF\_8)) {
if (isUnreserved(b)) {
result.append((char) b);
} else {
result.append(String.format("%%%02X", b & 0xFF));
}
}
return result.toString();
}
private boolean isUnreserved(byte b) {
return (b >= 'A' && b <= 'Z') ||
(b >= 'a' && b <= 'z') ||
(b >= '0' && b <= '9') ||
b == '-' || b == '\_' || b == '.' || b == '~';
}
private String calculateBodyHash(byte[] body) {
try {
if (body == null || body.length == 0) {
return "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
}
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(body);
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
hexString.append(String.format("%02x", b));
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA-256算法不可用", e);
}
}
private String buildCanonicalRequest(String method, String uri,
String query, String bodyHash) {
return method + "\n" + uri + "\n" + query + "\n" + bodyHash;
}
private String buildStringToSign(String canonicalRequest) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(canonicalRequest.getBytes(StandardCharsets.UTF\_8));
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
hexString.append(String.format("%02x", b));
}
return "ACS3-HMAC-SHA256\n" + hexString.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA-256算法不可用", e);
}
}
private String hmacSha256(String source, String secret) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(
secret.getBytes(StandardCharsets.UTF\_8), "HmacSHA256"
);
mac.init(secretKey);
byte[] hash = mac.doFinal(source.getBytes(StandardCharsets.UTF\_8));
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
hexString.append(String.format("%02x", b));
}
return hexString.toString();
} catch (Exception e) {
throw new RuntimeException("HMAC-SHA256计算失败", e);
}
}
private String decrypt(String key, String encrypted) {
// 需要根据实际加密方式实现
return encrypted; // 简化处理
}
}
PHP实现
<?php
class SignatureGenerator {
public function generateSignature($httpMethod, $uri, $queryParams, $body, $secretKey) {
$canonicalUri = $this->canonicalURI($uri);
$canonicalQuery = $this->canonicalQueryString($queryParams);
$bodyHash = $this->calculateBodyHash($body);
$canonicalRequest = $this->buildCanonicalRequest(
$httpMethod, $canonicalUri, $canonicalQuery, $bodyHash
);
$stringToSign = $this->buildStringToSign($canonicalRequest);
$decodedSecret = $this->decrypt("aqj", $secretKey);
return $this->hmacSha256($stringToSign, $decodedSecret);
}
private function canonicalURI($uri) {
if (empty($uri)) return "/";
$segments = array\_filter(explode("/", $uri));
$encodedSegments = array\_map([$this, 'encodeRFC3986'], $segments);
return "/" . implode("/", $encodedSegments);
}
private function canonicalQueryString($params) {
if (empty($params)) return "";
ksort($params);
$items = [];
foreach ($params as $key => $values) {
$value = !empty($values) ? $values[0] : "";
$encodedKey = $this->encodeRFC3986($key);
$encodedValue = $this->encodeRFC3986($value);
$items[] = "$encodedKey=$encodedValue";
}
return implode("&", $items);
}
private function encodeRFC3986($str) {
$unreserved = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-\_.~';
$result = '';
for ($i = 0; $i < strlen($str); $i++) {
$char = $str[$i];
if (strpos($unreserved, $char) !== false) {
$result .= $char;
} else {
$result .= '%' . strtoupper(sprintf('%02x', ord($char)));
}
}
return $result;
}
private function calculateBodyHash($body) {
if (empty($body)) {
return "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
}
return hash('sha256', $body);
}
private function buildCanonicalRequest($method, $uri, $query, $bodyHash) {
return "$method\n$uri\n$query\n$bodyHash";
}
private function buildStringToSign($canonicalRequest) {
$hash = hash('sha256', $canonicalRequest);
return "ACS3-HMAC-SHA256\n$hash";
}
private function hmacSha256($source, $secret) {
return hash\_hmac('sha256', $source, $secret);
}
private function decrypt($key, $encrypted) {
// 需要根据实际加密方式实现
return $encrypted; // 简化处理
}
}
?>
Go语言实现
package main
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"fmt"
"net/url"
"sort"
"strings"
)
type SignatureGenerator struct{}
type SignRequest struct {
HttpMethod string
Uri string
QueryParams map[string][]string
Body []byte
}
func NewSignatureGenerator() *SignatureGenerator {
return &SignatureGenerator{}
}
func (sg *SignatureGenerator) GenerateSignature(req *SignRequest, secretKey string) string {
canonicalUri := sg.canonicalURI(req.Uri)
canonicalQuery := sg.canonicalQueryString(req.QueryParams)
bodyHash := sg.calculateBodyHash(req.Body)
canonicalRequest := sg.buildCanonicalRequest(
req.HttpMethod, canonicalUri, canonicalQuery, bodyHash,
)
stringToSign := sg.buildStringToSign(canonicalRequest)
decodedSecret := sg.decrypt("aqj", secretKey)
return sg.hmacSha256(stringToSign, decodedSecret)
}
func (sg *SignatureGenerator) canonicalURI(uri string) string {
if uri == "" {
return "/"
}
segments := strings.Split(uri, "/")
encodedSegments := make([]string, 0, len(segments))
for _, segment := range segments {
if segment == "" {
continue
}
encodedSegments = append(encodedSegments, sg.encodeWithRFC3986(segment))
}
return "/" + strings.Join(encodedSegments, "/")
}
func (sg *SignatureGenerator) canonicalQueryString(params map[string][]string) string {
if len(params) == 0 {
return ""
}
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
var buf bytes.Buffer
for _, k := range keys {
value := ""
if len(params[k]) > 0 {
value = params[k][0]
}
if buf.Len() > 0 {
buf.WriteByte('&')
}
encodedKey := sg.encodeWithRFC3986(k)
encodedValue := sg.encodeWithRFC3986(value)
buf.WriteString(encodedKey)
buf.WriteByte('=')
buf.WriteString(encodedValue)
}
return buf.String()
}
func (sg *SignatureGenerator) encodeWithRFC3986(s string) string {
var buf bytes.Buffer
for _, b := range []byte(s) {
if sg.isUnreserved(b) {
buf.WriteByte(b)
} else {
buf.WriteString(fmt.Sprintf("%%%02X", b))
}
}
return buf.String()
}
func (sg *SignatureGenerator) isUnreserved(b byte) bool {
return (b >= 'A' && b <= 'Z') ||
(b >= 'a' && b <= 'z') ||
(b >= '0' && b <= '9') ||
b == '-' || b == '_' || b == '.' || b == '~'
}
func (sg *SignatureGenerator) calculateBodyHash(body []byte) string {
if len(body) == 0 {
return "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
hash := sha256.Sum256(body)
return fmt.Sprintf("%x", hash)
}
func (sg *SignatureGenerator) buildCanonicalRequest(method, uri, query, bodyHash string) string {
return fmt.Sprintf("%s\n%s\n%s\n%s", method, uri, query, bodyHash)
}
func (sg *SignatureGenerator) buildStringToSign(canonicalRequest string) string {
hash := sha256.Sum256([]byte(canonicalRequest))
return fmt.Sprintf("ACS3-HMAC-SHA256\n%x", hash)
}
func (sg *SignatureGenerator) hmacSha256(source, secret string) string {
key := []byte(secret)
h := hmac.New(sha256.New, key)
h.Write([]byte(source))
return fmt.Sprintf("%x", h.Sum(nil))
}
func (sg *SignatureGenerator) decrypt(key, encrypted string) string {
// 需要根据实际加密方式实现
return encrypted // 简化处理
}
// 使用示例
func main() {
generator := NewSignatureGenerator()
req := &SignRequest{
HttpMethod: "POST",
Uri: "/api/v1/users",
QueryParams: map[string][]string{
"page": {"1"},
"size": {"10"},
},
Body: []byte(`{"name":"test"}`),
}
signature := generator.GenerateSignature(req, "your_secret_key")
fmt.Printf("生成的签名: %s\n", signature)
}
C语言实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#define MAX_URL_LEN 2048
#define MAX_QUERY_LEN 4096
#define MAX_BODY_LEN 8192
#define MAX_SIGNATURE_LEN 128
#define EMPTY_BODY_HASH "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
typedef struct {
char key[256];
char value[1024];
} QueryParam;
typedef struct {
char http_method[16];
char uri[MAX_URL_LEN];
QueryParam query_params[64];
int param_count;
unsigned char *body;
size_t body_len;
} SignRequest;
// 检查字符是否为RFC3986未保留字符
int is_unreserved(unsigned char c) {
return (c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
c == '-' || c == '_' || c == '.' || c == '~';
}
// RFC3986编码
void encode_rfc3986(const char *src, char *dest, size_t dest_size) {
size_t src_len = strlen(src);
size_t dest_pos = 0;
for (size\_t i = 0; i < src\_len && dest\_pos < dest\_size - 1; i++) {
unsigned char c = src[i];
if (is\_unreserved(c)) {
dest[dest\_pos++] = c;
} else {
if (dest\_pos < dest\_size - 3) {
sprintf(&dest[dest\_pos], "%%%02X", c);
dest\_pos += 3;
}
}
}
dest[dest\_pos] = '\0';
}
// 规范化URI
void canonical_uri(const char *uri, char *result, size_t result_size) {
if (!uri || strlen(uri) == 0) {
strcpy(result, "/");
return;
}
char temp\_uri[MAX\_URL\_LEN];
strncpy(temp\_uri, uri, sizeof(temp\_uri) - 1);
temp\_uri[sizeof(temp\_uri) - 1] = '\0';
char \*segments[128];
int segment\_count = 0;
// 分割路径
char \*token = strtok(temp\_uri, "/");
while (token && segment\_count < 128) {
if (strlen(token) > 0) {
segments[segment\_count++] = token;
}
token = strtok(NULL, "/");
}
// 重新组合
strcpy(result, "/");
for (int i = 0; i < segment\_count; i++) {
char encoded[512];
encode\_rfc3986(segments[i], encoded, sizeof(encoded));
if (i > 0) strcat(result, "/");
strcat(result, encoded);
}
}
// 比较函数用于排序
int compare_params(const void *a, const void *b) {
const QueryParam *param_a = (const QueryParam *)a;
const QueryParam *param_b = (const QueryParam *)b;
return strcmp(param_a->key, param_b->key);
}
// 规范化查询参数
void canonical_query_string(QueryParam *params, int param_count,
char *result, size_t result_size) {
if (param_count == 0) {
result[0] = '\0';
return;
}
// 排序参数
qsort(params, param\_count, sizeof(QueryParam), compare\_params);
result[0] = '\0';
for (int i = 0; i < param\_count; i++) {
char encoded\_key[512], encoded\_value[1024];
encode\_rfc3986(params[i].key, encoded\_key, sizeof(encoded\_key));
encode\_rfc3986(params[i].value, encoded\_value, sizeof(encoded\_value));
if (i > 0) strcat(result, "&");
strcat(result, encoded\_key);
strcat(result, "=");
strcat(result, encoded\_value);
}
}
// 计算SHA256哈希
void sha256_hash(const unsigned char *data, size_t len, char *output) {
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256(data, len, hash);
for (int i = 0; i < SHA256\_DIGEST\_LENGTH; i++) {
sprintf(output + (i \* 2), "%02x", hash[i]);
}
output[SHA256\_DIGEST\_LENGTH \* 2] = '\0';
}
// 计算请求体哈希
void calculate_body_hash(const unsigned char *body, size_t body_len, char *result) {
if (!body || body_len == 0) {
strcpy(result, EMPTY_BODY_HASH);
return;
}
sha256\_hash(body, body\_len, result);
}
// 构建规范化请求
void build_canonical_request(const char *method, const char *uri,
const char *query, const char *body_hash,
char *result, size_t result_size) {
snprintf(result, result_size, "%s\n%s\n%s\n%s",
method, uri, query, body_hash);
}
// 构建签名字符串
void build_string_to_sign(const char *canonical_request, char *result, size_t result_size) {
char hash[65];
sha256_hash((const unsigned char *)canonical_request, strlen(canonical_request), hash);
snprintf(result, result_size, "ACS3-HMAC-SHA256\n%s", hash);
}
// HMAC-SHA256签名
void hmac_sha256(const char *source, const char *secret, char *result) {
unsigned char digest[EVP_MAX_MD_SIZE];
unsigned int digest_len;
HMAC(EVP\_sha256(), secret, strlen(secret),
(const unsigned char \*)source, strlen(source),
digest, &digest\_len);
for (unsigned int i = 0; i < digest\_len; i++) {
sprintf(result + (i \* 2), "%02x", digest[i]);
}
result[digest\_len \* 2] = '\0';
}
// 解密函数(需要根据实际情况实现)
void decrypt_secret(const char *key, const char *encrypted, char *result, size_t result_size) {
// 简化处理,直接返回原值
strncpy(result, encrypted, result_size - 1);
result[result_size - 1] = '\0';
}
// 生成签名主函数
void generate_signature(SignRequest *req, const char *secret_key,
char *signature, size_t signature_size) {
char canonical_uri_str[MAX_URL_LEN];
char canonical_query_str[MAX_QUERY_LEN];
char body_hash[65];
char canonical_request[MAX_BODY_LEN];
char string_to_sign[MAX_BODY_LEN];
char decoded_secret[256];
// 1. 规范化URI
canonical\_uri(req->uri, canonical\_uri\_str, sizeof(canonical\_uri\_str));
// 2. 规范化查询参数
canonical\_query\_string(req->query\_params, req->param\_count,
canonical\_query\_str, sizeof(canonical\_query\_str));
// 3. 计算请求体哈希
calculate\_body\_hash(req->body, req->body\_len, body\_hash);
// 4. 构建规范化请求
build\_canonical\_request(req->http\_method, canonical\_uri\_str,
canonical\_query\_str, body\_hash,
canonical\_request, sizeof(canonical\_request));
// 5. 构建签名字符串
build\_string\_to\_sign(canonical\_request, string\_to\_sign, sizeof(string\_to\_sign));
// 6. 解密密钥
decrypt\_secret("aqj", secret\_key, decoded\_secret, sizeof(decoded\_secret));
// 7. 生成HMAC签名
hmac\_sha256(string\_to\_sign, decoded\_secret, signature);
}
// 使用示例
int main() {
// 初始化请求
SignRequest req;
strcpy(req.http_method, "POST");
strcpy(req.uri, "/api/v1/users");
// 添加查询参数
req.param\_count = 2;
strcpy(req.query\_params[0].key, "page");
strcpy(req.query\_params[0].value, "1");
strcpy(req.query\_params[1].key, "size");
strcpy(req.query\_params[1].value, "10");
// 设置请求体
const char \*body\_str = "{\"name\":\"test\"}";
req.body = (unsigned char \*)body\_str;
req.body\_len = strlen(body\_str);
// 生成签名
char signature[MAX\_SIGNATURE\_LEN];
generate\_signature(&req, "your\_secret\_key", signature, sizeof(signature));
printf("生成的签名: %s\n", signature);
return 0;
}
4.使用示例
python# Python使用示例
generator = SignatureGenerator()
signature = generator.generate_signature(
"POST",
"/api/v1/users",
{"page": ["1"], "size": ["10"]},
b'{"name":"test"}',
"your_secret_key"
)
print(f"生成的签名: {signature}")
5.注意事项
密钥解密: decrypt函数需要根据实际的加密算法实现
字符编码: 确保所有字符串使用UTF-8编码
参数处理: 查询参数需要按字母顺序排序
空值处理: 空请求体使用固定的SHA-256值
错误处理: 实际使用中需要添加完整的异常处理机制