VM Mode API Signature Authentication Developer Documentation (VM4-HMAC-SHA256)
This API utilizes the VM4-HMAC-SHA256 signature algorithm to ensure the authenticity of the request source, data integrity, and protection against replay attacks. All requests must include signature information within the headers.
parameter | value | illustrate |
Header Prefix |
| All custom headers begin with this. |
Service Name |
| Service Identity within the Signing Scope |
Algorithm Identifier Prefix |
| The starting identifier for the Authorization header. |
Scope Suffix |
| The ending character of the derived key calculation |
Header Key | Example Value | illustrate |
x-vm-date |
| The UTC time at which the request was issued, in the format: YYYYMMDDTHHMMSSZ |
x-vm-content-sha256 |
| The SHA256 hash of the request body (lowercase hexadecimal). |
Authorization |
| Complete Signature Credential String |
x-vm-date; if the deviation from the server's time exceeds 15 minutes, the request will be rejected.Concatenate the following strings in order (separated by \n):
HTTPMethod: eg. GET, POST。
CanonicalURI: The encoded path (e.g., /api/v1/instances). If empty, / is used.
CanonicalQueryString: A string consisting of the parameters sorted in ascending ASCII order and URL-encoded. If there are no parameters, the string is empty.
CanonicalHeaders: Must include host, x-vm-content-sha256, and x-vm-date. The format is: key:value\n (Note: keys must be lowercase).。
Blank Line: After the headers have been concatenated, an additional newline character must be appended.
SignedHeaders: A list of header keys included in the signature, lowercase and separated by semicolons.。
PayloadHash: The value of x-vm-content-sha256 (i.e., the SHA256 hexadecimal string of the Body).。
Example
GET
/v1/instances
Limit=10&status=running
host:api.example.com
x-vm-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-vm-date:20260326T113306Z
host;x-vm-content-sha256;x-vm-date
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855Concatenate the metadata and the canonical request hash generated in Step 1, in the following order (separated by \n).:
Algorithm: Fixed Value VM4-HMAC-SHA256.
RequestDateTime: The value of x-vm-date (e.g., 20260326T113306Z).
CredentialScope: A string containing the date, region, and service identifier. The format is: {DateStamp}/{Region}/v3/vm4_request.
DateStamp: The format is YYYYMMDD (e.g., 20260326).
Region: The ID or name of the target region for the request (optional)—e.g., 1965717130594750464 or cn-north-1.
HashedCanonicalRequest: Compute the SHA256 hash of the entire Canonical Request string constructed in Step 1, and convert it into a lowercase hexadecimal string.
Example
VM4-HMAC-SHA256
20260326T113306Z
20260326/cn-north-1/v3/vm4_request
765e92d7b1d12c8a3e8a4d2f9b8c7a6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0aThe derived key is obtained by performing multiple hash iterations on the SecretKey. Please execute the HMAC-SHA256 operations in the following order (pseudocode logic).:
kDate: Hash the DateStamp (YYYYMMDD) using "VM4" + SecretKey as the key. HMAC-SHA256("VM4" + SecretKey, "20260326")
kRegion: Use the kDate obtained in the previous step as the key to hash the Region (e.g., cn-north-1). HMAC-SHA256(kDate, "cn-north-1")
kService: Use the kRegion obtained in the previous step as the key to hash the ServiceName (fixed as "v3"). HMAC-SHA256(kRegion, "v3")
kSigning: Using the kService obtained in the previous step as the key, hash the Suffix (fixed as "vm4_request"). HMAC-SHA256(kService, "vm4_request")
Perform HMAC-SHA256 calculations sequentially using your SecretKey.:
// Note: VM4 is used as a prefix for the SecretKey.
kDate := hmacSha256([]byte("VM4" + secretKey), "20260326")
kRegion := hmacSha256(kDate, "cn-north-1")
kService := hmacSha256(kRegion, "v3")
kSigning := hmacSha256(kService, "vm4_request") // The resulting kSigning is used in Step 4.Use the generated derived key (Signing Key) to perform the final integrity signature on the offset and request metadata.
Key: Use the final binary byte array, kSigning, calculated in Step 3.
Data: Use the complete "String to Sign" string constructed in Step 2.
HMAC Calculation: Use the HMAC-SHA256 algorithm to hash the "String To Sign," using kSigning as the key.
Hexadecimal Conversion: Convert the binary result obtained in the previous step into a lowercase hexadecimal string. This constitutes the final Signature.
// Use the result kSigning from Step 3 and the result stringToSign from Step 2.
signatureBytes := hmacSha256(kSigning, stringToSign)
signature := hex.EncodeToString(signatureBytes) Authorization: VM4-HMAC-SHA256 Credential=AK_EXAMPLE/20260326/region-1/v3/vm4_request, SignedHeaders=host;x-vm-content-sha256;x-vm-date, Signature=fe5f...The following example demonstrates how to manually calculate and add a VM mode signature to an HTTP request without relying on external frameworks.
package main
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"net/http"
"time"
)
// HmacSha256 tool
func hmacSha256(key []byte, data string) []byte {
h := hmac.New(sha256.New, key)
h.Write([]byte(data))
return h.Sum(nil)
}
// (Signing Key)
func getVM4SigningKey(secretKey, dateStamp, regionName string) []byte {
kDate := hmacSha256([]byte("VM4"+secretKey), dateStamp)
kRegion := hmacSha256(kDate, regionName)
kService := hmacSha256(kRegion, "v3") // ServiceName is v3
kSigning := hmacSha256(kService, "vm4_request")
return kSigning
}
func signVMRequest(req *http.Request, ak, sk, region, payloadHash string) {
now := time.Now().UTC()
amzDate := now.Format("20060102T150405Z")
dateStamp := now.Format("20060102")
// 1. set Header
req.Header.Set("x-vm-date", amzDate)
// Calculate Payload Hash (Assuming no Body, or Body has already been processed)
if payloadHash == "" {
payloadHash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
req.Header.Set("x-vm-content-sha256", payloadHash)
// 2. Canonical Request
signedHeaders := "host;x-vm-content-sha256;x-vm-date"
canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s",
req.Method,
req.URL.Path,
req.URL.RawQuery,
"host:"+req.Host,
"x-vm-content-sha256:"+payloadHash,
"x-vm-date:"+amzDate+"\n",
signedHeaders,
payloadHash,
)
// 3. StringToSign
credentialScope := fmt.Sprintf("%s/%s/v3/vm4_request", dateStamp, region)
hash := sha256.Sum256([]byte(canonicalRequest))
stringToSign := fmt.Sprintf("VM4-HMAC-SHA256\n%s\n%s\n%s",
amzDate,
credentialScope,
hex.EncodeToString(hash[:]),
)
fmt.Println("StringToSign:", stringToSign)
// 4. sign
signingKey := getVM4SigningKey(sk, dateStamp, region)
signature := hex.EncodeToString(hmacSha256(signingKey, stringToSign))
// 5. set Authorization Header
authHeader := fmt.Sprintf("VM4-HMAC-SHA256 Credential=%s/%s, SignedHeaders=%s, Signature=%s",
ak, credentialScope, signedHeaders, signature)
req.Header.Set("Authorization", authHeader)
}
func main() {
var body = []byte(`{"offset":1,"limit":1}`)
// Payload Hash
hash := sha256.Sum256(body)
payloadHash := hex.EncodeToString(hash[:])
req, _ := http.NewRequest("GET", "http://127.0.0.1:8080/aaa-2100002392/?delimiter=%2F&encoding-type=url&fetch-owner=true&list-type=2&prefix=", bytes.NewReader(body))
req.Host = "127.0.0.1:8080"
ak := "CUS1****SVTK"
sk := "OPD3****E27G"
region := "1965717130594750464"
signVMRequest(req, ak, sk, region, payloadHash)
fmt.Println("Authorization Header:", req.Header.Get("Authorization"))
}To ensure successful integration, please check in the following order.:
Path Encoding:The CanonicalURI must start with a '/'. If the path is empty, use '/'.
Parameter Sorting:The parameters in the CanonicalQueryString must be arranged in ascending ASCII order of their key names.
Lowercase Conversion:All key names in SignedHeaders must be converted to lowercase.
Hash Consistency:x-vm-content-sha256 must exactly match the SHA256 value of the actual content in the request body.
Error Code | illustrate |
UNAUTHORIZED: Signature mismatch | The signature calculation is inconsistent. Please check your keys and the logic for constructing the Canonical Request. |
UNAUTHORIZED: x-amz-date skew too large | The client time deviation exceeds 15 minutes. Please synchronize the NTP time. |
UNAUTHORIZED: Missing x-vm-date | x-vm-date is missing or has an incorrect format. |