Signature的生成方法
更新时间: 2025-08-01 07:54:09
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值
错误处理: 实际使用中需要添加完整的异常处理机制