Request signatures (V4)
The V4 signature mechanism of the Simple Log Service API uses the HMAC-SHA256 algorithm to sign requests, which provides higher security than V1 signatures (HMAC-SHA1). All API requests must be authenticated by using an enabled AccessKey pair.
Input parameters
The V4 signing process requires the following input parameters:
|
Parameter |
Type |
Required |
Description |
|
|
String |
Yes |
The HTTP request method, such as GET, POST, PUT, or DELETE. |
|
|
String |
Yes |
The requested resource path, such as /logstores. |
|
|
Map |
Yes |
A collection of key-value pairs for the HTTP request headers. |
|
|
Bytes |
No |
The content of the HTTP request body. |
|
|
String |
Yes |
The Alibaba Cloud AccessKey ID. |
|
|
String |
Yes |
The Alibaba Cloud AccessKey secret. |
|
|
String |
Yes |
The region ID, such as cn-hangzhou or cn-shanghai. |
|
|
String |
Yes |
The UTC date in YYYYMMDD format, such as 20240901. |
Signed request headers
The following HTTP request headers must be carried in an API request after V4 signing. The header keys are case-sensitive.
|
Header name |
Description |
|
|
The fixed value |
|
|
The API version number. The fixed value is |
|
|
The HTTP request date, in RFC 1123 format and GMT time. For example: |
|
|
The Simple Log Service request timestamp, in ISO 8601 format, accurate to the second, and in UTC time. Format: |
|
|
The type of the request body. Required when the request has a body. For example: |
|
|
The MD5 value of the request body, as an uppercase hexadecimal string. Required when the request has a body. |
|
|
The signature authentication information. For the format, see Step 4 of the signing process below. |
Signature algorithm workflow
V4 signing consists of four steps: build the canonical request, build the string to sign, calculate the signature, and build the Authorization header.
Canonical request construction
The following figure shows how the components of an HTTP request are processed and concatenated into the canonical request string (Canonical Request):

Signature calculation
The following figure shows the complete signature calculation process from the canonical request to the final Authorization header, including the four-layer HMAC derivation of the signing key:

Signing process
Step 1: Build the canonical request (Canonical Request)
Concatenate the key elements of the HTTP request into a single string in the canonical format. This string is called the canonical request (Canonical Request). The concatenation format is as follows:
CanonicalRequest =
HTTPRequestMethod + '\n' +
CanonicalURI + '\n' +
CanonicalQueryString + '\n' +
CanonicalHeaders + '\n' +
SignedHeaders + '\n' +
HashedPayload
The fields are described as follows:
|
Field |
Description |
|
|
The HTTP request method, in uppercase. Such as GET, POST, PUT, or DELETE. |
|
|
The URI-encoded resource path. It starts with |
|
|
The query parameters, sorted by key in lexicographic order. Each parameter is in the format |
|
|
The HTTP request headers that participate in signing. Convert the header names to lowercase, sort them in lexicographic order, and format each line as |
|
|
The list of header names that participate in signing, all in lowercase, sorted in lexicographic order, and separated by |
|
|
The SHA-256 hash of the request body, in lowercase hexadecimal. If there is no body, use the SHA-256 value of an empty string: |
About SignedHeaders: The following headers must participate in signing: host, x-log-date, and content-type (when a body exists). Headers that start with x-log- or x-acs- should also be included in the signature.
Step 2: Build the string to sign (String to Sign)
Concatenate the signature algorithm identifier, the timestamp, the signature scope (Scope), and the hash of the canonical request into the string to sign:
StringToSign =
"SLS4-HMAC-SHA256" + '\n' +
TimeStamp + '\n' +
Scope + '\n' +
Hex(SHA256(CanonicalRequest))
The fields are described as follows:
|
Field |
Description |
|
|
The fixed signature algorithm identifier string. |
|
|
The timestamp, consistent with the |
|
|
The signature scope, in the format |
|
|
The SHA-256 hash of the CanonicalRequest built in Step 1, in lowercase hexadecimal. |
Step 3: Calculate the signature
Derive the signing key (Signing Key) step by step, and then perform HMAC-SHA256 on the string to sign by using this key to obtain the final signature.
Derive the signing key (Signing Key):
SigningKey = HMAC-SHA256(
HMAC-SHA256(
HMAC-SHA256(
HMAC-SHA256("aliyun_v4" + AccessKeySecret, Date),
Region
),
"sls"
),
"aliyun_v4_request"
)
The derivation is a four-layer nested HMAC:
-
Use
"aliyun_v4" + AccessKeySecretas the initial key and perform HMAC-SHA256 on the date (YYYYMMDD). -
Use the result of the previous step as the key and perform HMAC-SHA256 on the region (such as
cn-hangzhou). -
Use the result of the previous step as the key and perform HMAC-SHA256 on the service name
"sls". -
Use the result of the previous step as the key and perform HMAC-SHA256 on
"aliyun_v4_request".
Calculate the final signature:
Signature = Hex(HMAC-SHA256(SigningKey, StringToSign))
Convert the result of the HMAC-SHA256 operation to a lowercase hexadecimal string to obtain the final signature.
Step 4: Build the Authorization header
Assemble the signature result into the Authorization request header in the following format:
Authorization: SLS4-HMAC-SHA256 Credential={AccessKeyId}/{Scope}, SignedHeaders={SignedHeaders}, Signature={Signature}
The fields are described as follows:
|
Field |
Description |
|
|
The fixed signature algorithm identifier. |
|
|
The format is |
|
|
The list of header names that participate in signing, the same as SignedHeaders in Step 1. |
|
|
The signature string calculated in Step 3. |
Signature examples
Example 1: GET request (list all Logstores)
HTTP request:
GET /logstores HTTP/1.1
Host: ali-test-project.cn-hangzhou.log.aliyuncs.com
x-log-apiversion: 0.6.0
x-log-signaturemethod: hmac-sha256
x-log-date: 20240902T060000Z
Date: Mon, 02 Sep 2024 06:00:00 GMT
Canonical Request:
GET
/logstores
host:ali-test-project.cn-hangzhou.log.aliyuncs.com
x-log-apiversion:0.6.0
x-log-date:20240902T060000Z
x-log-signaturemethod:hmac-sha256
host;x-log-apiversion;x-log-date;x-log-signaturemethod
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
String to Sign:
SLS4-HMAC-SHA256
20240902T060000Z
20240902/cn-hangzhou/sls/aliyun_v4_request
<Hex(SHA256(CanonicalRequest))>
Authorization header:
SLS4-HMAC-SHA256 Credential=LTAI5tXxx/20240902/cn-hangzhou/sls/aliyun_v4_request, SignedHeaders=host;x-log-apiversion;x-log-date;x-log-signaturemethod, Signature=<calculated signature>
Example 2: POST request (write log data)
HTTP request:
POST /logstores/my-logstore/shards/lb HTTP/1.1
Host: ali-test-project.cn-hangzhou.log.aliyuncs.com
Content-Type: application/x-protobuf
Content-MD5: 1DD45FA557BO4A6899BE4CC3D0B43B20
Content-Length: 208
x-log-apiversion: 0.6.0
x-log-signaturemethod: hmac-sha256
x-log-date: 20240902T060000Z
x-log-compresstype: lz4
x-log-bodyrawsize: 1024
Date: Mon, 02 Sep 2024 06:00:00 GMT
Canonical Request:
POST
/logstores/my-logstore/shards/lb
content-md5:1DD45FA557BO4A6899BE4CC3D0B43B20
content-type:application/x-protobuf
host:ali-test-project.cn-hangzhou.log.aliyuncs.com
x-log-apiversion:0.6.0
x-log-bodyrawsize:1024
x-log-compresstype:lz4
x-log-date:20240902T060000Z
x-log-signaturemethod:hmac-sha256
content-md5;content-type;host;x-log-apiversion;x-log-bodyrawsize;x-log-compresstype;x-log-date;x-log-signaturemethod
<Hex(SHA256(Body))>
Code examples
The following provides reference V4 signing implementations in eight languages: Java, Python, PHP, Go, C#, C++, TypeScript, and Rust. The core process is the same.
Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.*;
public class SlsV4Signer {
private static byte[] hmacSha256(byte[] key, String msg) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(key, "HmacSHA256"));
return mac.doFinal(msg.getBytes(StandardCharsets.UTF_8));
}
private static String sha256Hex(byte[] data) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(data);
return bytesToHex(hash);
}
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
private static byte[] getSigningKey(String secret, String date,
String region) throws Exception {
byte[] kDate = hmacSha256(
("aliyun_v4" + secret).getBytes(StandardCharsets.UTF_8), date);
byte[] kRegion = hmacSha256(kDate, region);
byte[] kService = hmacSha256(kRegion, "sls");
return hmacSha256(kService, "aliyun_v4_request");
}
public static void signRequest(String method, String resource,
Map<String, String> headers, byte[] body,
String accessKeyId, String accessKeySecret,
String region) throws Exception {
// 1. Prepare time parameters
SimpleDateFormat rfc1123 = new SimpleDateFormat(
"EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
rfc1123.setTimeZone(TimeZone.getTimeZone("GMT"));
SimpleDateFormat iso8601 = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
iso8601.setTimeZone(TimeZone.getTimeZone("UTC"));
SimpleDateFormat dateOnly = new SimpleDateFormat("yyyyMMdd");
dateOnly.setTimeZone(TimeZone.getTimeZone("UTC"));
Date now = new Date();
String dateStamp = dateOnly.format(now);
String timestamp = iso8601.format(now);
String scope = dateStamp + "/" + region + "/sls/aliyun_v4_request";
// 2. Set required request headers
headers.put("x-log-date", timestamp);
headers.put("x-log-signaturemethod", "hmac-sha256");
headers.put("x-log-apiversion", "0.6.0");
headers.put("Date", rfc1123.format(now));
// 3. Build the CanonicalRequest
TreeMap<String, String> sortedHeaders = new TreeMap<>();
for (Map.Entry<String, String> e : headers.entrySet()) {
sortedHeaders.put(e.getKey().toLowerCase(), e.getValue().trim());
}
StringBuilder canonicalHeadersSb = new StringBuilder();
StringBuilder signedHeadersSb = new StringBuilder();
for (Map.Entry<String, String> e : sortedHeaders.entrySet()) {
canonicalHeadersSb.append(e.getKey()).append(":")
.append(e.getValue()).append("\n");
if (signedHeadersSb.length() > 0) signedHeadersSb.append(";");
signedHeadersSb.append(e.getKey());
}
String signedHeaders = signedHeadersSb.toString();
String payloadHash = sha256Hex(body != null ? body : new byte[0]);
String canonicalRequest = String.join("\n",
method.toUpperCase(), resource, "",
canonicalHeadersSb.toString(), signedHeaders, payloadHash);
// 4. Build the StringToSign
String stringToSign = String.join("\n",
"SLS4-HMAC-SHA256", timestamp, scope,
sha256Hex(canonicalRequest.getBytes(StandardCharsets.UTF_8)));
// 5. Calculate the signature
byte[] signingKey = getSigningKey(accessKeySecret, dateStamp, region);
String signature = bytesToHex(hmacSha256(signingKey, stringToSign));
// 6. Build the Authorization header
String auth = "SLS4-HMAC-SHA256 Credential=" + accessKeyId + "/" + scope
+ ", SignedHeaders=" + signedHeaders
+ ", Signature=" + signature;
headers.put("Authorization", auth);
}
}
Python
import hashlib
import hmac
import datetime
def sha256_hex(data):
if isinstance(data, str):
data = data.encode("utf-8")
return hashlib.sha256(data).hexdigest()
def hmac_sha256(key, msg):
if isinstance(key, str):
key = key.encode("utf-8")
if isinstance(msg, str):
msg = msg.encode("utf-8")
return hmac.new(key, msg, hashlib.sha256).digest()
def get_signing_key(secret, date, region, service="sls"):
k_date = hmac_sha256(("aliyun_v4" + secret), date)
k_region = hmac_sha256(k_date, region)
k_service = hmac_sha256(k_region, service)
return hmac_sha256(k_service, "aliyun_v4_request")
def sign_v4(method, resource, headers, body,
access_key_id, access_key_secret, region):
# 1. Prepare time parameters
now = datetime.datetime.utcnow()
date_stamp = now.strftime("%Y%m%d")
timestamp = now.strftime("%Y%m%dT%H%M%SZ")
scope = f"{date_stamp}/{region}/sls/aliyun_v4_request"
# 2. Set required request headers
headers["x-log-date"] = timestamp
headers["x-log-signaturemethod"] = "hmac-sha256"
headers["x-log-apiversion"] = "0.6.0"
headers["Date"] = now.strftime("%a, %d %b %Y %H:%M:%S GMT")
# 3. Build the CanonicalRequest
signed_header_keys = sorted(k.lower() for k in headers)
canonical_headers = "".join(
f"{k}:{headers[k].strip()}\n" for k in signed_header_keys
)
signed_headers = ";".join(signed_header_keys)
payload_hash = sha256_hex(body if body else b"")
canonical_request = "\n".join([
method.upper(), resource, "",
canonical_headers, signed_headers, payload_hash,
])
# 4. Build the StringToSign
string_to_sign = "\n".join([
"SLS4-HMAC-SHA256", timestamp, scope,
sha256_hex(canonical_request),
])
# 5. Calculate the signature
signing_key = get_signing_key(access_key_secret, date_stamp, region)
signature = hmac.new(
signing_key, string_to_sign.encode("utf-8"), hashlib.sha256
).hexdigest()
# 6. Build the Authorization header
auth = (
f"SLS4-HMAC-SHA256 "
f"Credential={access_key_id}/{scope}, "
f"SignedHeaders={signed_headers}, "
f"Signature={signature}"
)
headers["Authorization"] = auth
return headers
PHP
<?php
function sha256Hex($data) {
return hash('sha256', $data);
}
function hmacSha256($key, $msg) {
return hash_hmac('sha256', $msg, $key, true);
}
function getSigningKey($secret, $date, $region, $service = 'sls') {
$kDate = hmacSha256('aliyun_v4' . $secret, $date);
$kRegion = hmacSha256($kDate, $region);
$kService = hmacSha256($kRegion, $service);
return hmacSha256($kService, 'aliyun_v4_request');
}
function signV4($method, $resource, &$headers, $body,
$accessKeyId, $accessKeySecret, $region) {
// 1. Prepare time parameters
$now = gmdate('Ymd\THis\Z');
$dateStamp = gmdate('Ymd');
$scope = "{$dateStamp}/{$region}/sls/aliyun_v4_request";
// 2. Set required request headers
$headers['x-log-date'] = $now;
$headers['x-log-signaturemethod'] = 'hmac-sha256';
$headers['x-log-apiversion'] = '0.6.0';
$headers['Date'] = gmdate('D, d M Y H:i:s') . ' GMT';
// 3. Build the CanonicalRequest
$sortedHeaders = [];
foreach ($headers as $k => $v) {
$sortedHeaders[strtolower($k)] = trim($v);
}
ksort($sortedHeaders);
$canonicalHeaders = '';
$signedHeaderKeys = [];
foreach ($sortedHeaders as $k => $v) {
$canonicalHeaders .= "{$k}:{$v}\n";
$signedHeaderKeys[] = $k;
}
$signedHeaders = implode(';', $signedHeaderKeys);
$payloadHash = sha256Hex($body ?: '');
$canonicalRequest = implode("\n", [
strtoupper($method), $resource, '',
$canonicalHeaders, $signedHeaders, $payloadHash,
]);
// 4. Build the StringToSign
$stringToSign = implode("\n", [
'SLS4-HMAC-SHA256', $now, $scope,
sha256Hex($canonicalRequest),
]);
// 5. Calculate the signature
$signingKey = getSigningKey($accessKeySecret, $dateStamp, $region);
$signature = hash_hmac('sha256', $stringToSign, $signingKey);
// 6. Build the Authorization header
$headers['Authorization'] =
"SLS4-HMAC-SHA256 Credential={$accessKeyId}/{$scope}, "
. "SignedHeaders={$signedHeaders}, Signature={$signature}";
}
?>
Go
package sls
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"net/http"
"sort"
"strings"
"time"
)
func sha256Hex(data []byte) string {
h := sha256.Sum256(data)
return hex.EncodeToString(h[:])
}
func hmacSHA256(key []byte, msg string) []byte {
mac := hmac.New(sha256.New, key)
mac.Write([]byte(msg))
return mac.Sum(nil)
}
func getSigningKey(secret, date, region string) []byte {
kDate := hmacSHA256([]byte("aliyun_v4"+secret), date)
kRegion := hmacSHA256(kDate, region)
kService := hmacSHA256(kRegion, "sls")
return hmacSHA256(kService, "aliyun_v4_request")
}
func SignV4(method, resource string, header http.Header, body []byte,
accessKeyID, accessKeySecret, region string) {
// 1. Prepare time parameters
now := time.Now().UTC()
dateStamp := now.Format("20060102")
timestamp := now.Format("20060102T150405Z")
scope := fmt.Sprintf("%s/%s/sls/aliyun_v4_request", dateStamp, region)
// 2. Set required request headers
header.Set("x-log-date", timestamp)
header.Set("x-log-signaturemethod", "hmac-sha256")
header.Set("x-log-apiversion", "0.6.0")
header.Set("Date", now.Format(http.TimeFormat))
// 3. Build the CanonicalRequest
var keys []string
for k := range header {
keys = append(keys, strings.ToLower(k))
}
sort.Strings(keys)
var canonicalHeaders, signedHeadersList []string
for _, k := range keys {
v := strings.TrimSpace(header.Get(k))
canonicalHeaders = append(canonicalHeaders, k+":"+v)
signedHeadersList = append(signedHeadersList, k)
}
canonicalHeaderStr := strings.Join(canonicalHeaders, "\n") + "\n"
signedHeaders := strings.Join(signedHeadersList, ";")
payloadHash := sha256Hex(body)
canonicalRequest := strings.Join([]string{
strings.ToUpper(method), resource, "",
canonicalHeaderStr, signedHeaders, payloadHash,
}, "\n")
// 4. Build the StringToSign
stringToSign := strings.Join([]string{
"SLS4-HMAC-SHA256", timestamp, scope,
sha256Hex([]byte(canonicalRequest)),
}, "\n")
// 5. Calculate the signature
signingKey := getSigningKey(accessKeySecret, dateStamp, region)
signature := hex.EncodeToString(hmacSHA256(signingKey, stringToSign))
// 6. Build the Authorization header
auth := fmt.Sprintf(
"SLS4-HMAC-SHA256 Credential=%s/%s, SignedHeaders=%s, Signature=%s",
accessKeyID, scope, signedHeaders, signature)
header.Set("Authorization", auth)
}
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
public class SlsV4Signer
{
private static byte[] HmacSha256(byte[] key, string msg)
{
using var hmac = new HMACSHA256(key);
return hmac.ComputeHash(Encoding.UTF8.GetBytes(msg));
}
private static string Sha256Hex(byte[] data)
{
using var sha = SHA256.Create();
var hash = sha.ComputeHash(data);
return BitConverter.ToString(hash).Replace("-", "").ToLower();
}
private static byte[] GetSigningKey(string secret, string date,
string region)
{
var kDate = HmacSha256(
Encoding.UTF8.GetBytes("aliyun_v4" + secret), date);
var kRegion = HmacSha256(kDate, region);
var kService = HmacSha256(kRegion, "sls");
return HmacSha256(kService, "aliyun_v4_request");
}
public static void Sign(string method, string resource,
Dictionary<string, string> headers, byte[] body,
string accessKeyId, string accessKeySecret, string region)
{
// 1. Prepare time parameters
var now = DateTime.UtcNow;
var dateStamp = now.ToString("yyyyMMdd");
var timestamp = now.ToString("yyyyMMdd'T'HHmmss'Z'");
var scope = $"{dateStamp}/{region}/sls/aliyun_v4_request";
// 2. Set required request headers
headers["x-log-date"] = timestamp;
headers["x-log-signaturemethod"] = "hmac-sha256";
headers["x-log-apiversion"] = "0.6.0";
headers["Date"] = now.ToString("R");
// 3. Build the CanonicalRequest
var sorted = headers
.OrderBy(h => h.Key.ToLower())
.Select(h => new { Key = h.Key.ToLower(), Val = h.Value.Trim() })
.ToList();
var canonicalHeaders = string.Join("",
sorted.Select(h => $"{h.Key}:{h.Val}\n"));
var signedHeaders = string.Join(";", sorted.Select(h => h.Key));
var payloadHash = Sha256Hex(body ?? Array.Empty<byte>());
var canonicalRequest = string.Join("\n",
method.ToUpper(), resource, "",
canonicalHeaders, signedHeaders, payloadHash);
// 4. Build the StringToSign
var stringToSign = string.Join("\n",
"SLS4-HMAC-SHA256", timestamp, scope,
Sha256Hex(Encoding.UTF8.GetBytes(canonicalRequest)));
// 5. Calculate the signature
var signingKey = GetSigningKey(accessKeySecret, dateStamp, region);
var sigBytes = HmacSha256(signingKey, stringToSign);
var signature = BitConverter.ToString(sigBytes)
.Replace("-", "").ToLower();
// 6. Build the Authorization header
headers["Authorization"] =
$"SLS4-HMAC-SHA256 Credential={accessKeyId}/{scope}, "
+ $"SignedHeaders={signedHeaders}, Signature={signature}";
}
}
C++
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <algorithm>
#include <ctime>
#include <iomanip>
#include <map>
#include <sstream>
#include <string>
#include <vector>
static std::string toHex(const unsigned char* data, size_t len) {
std::ostringstream oss;
for (size_t i = 0; i < len; ++i)
oss << std::hex << std::setfill('0') << std::setw(2) << (int)data[i];
return oss.str();
}
static std::string sha256Hex(const std::string& data) {
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256(reinterpret_cast<const unsigned char*>(data.c_str()),
data.size(), hash);
return toHex(hash, SHA256_DIGEST_LENGTH);
}
static std::vector<unsigned char> hmacSha256(
const std::vector<unsigned char>& key, const std::string& msg) {
unsigned char out[EVP_MAX_MD_SIZE];
unsigned int outLen = 0;
HMAC(EVP_sha256(), key.data(), key.size(),
reinterpret_cast<const unsigned char*>(msg.c_str()),
msg.size(), out, &outLen);
return std::vector<unsigned char>(out, out + outLen);
}
static std::vector<unsigned char> hmacSha256(
const std::string& key, const std::string& msg) {
return hmacSha256(
std::vector<unsigned char>(key.begin(), key.end()), msg);
}
static std::vector<unsigned char> getSigningKey(
const std::string& secret,
const std::string& date, const std::string& region) {
auto kDate = hmacSha256("aliyun_v4" + secret, date);
auto kRegion = hmacSha256(kDate, region);
auto kService = hmacSha256(kRegion, "sls");
return hmacSha256(kService, "aliyun_v4_request");
}
void signV4(const std::string& method, const std::string& resource,
std::map<std::string, std::string>& headers,
const std::string& body,
const std::string& accessKeyId,
const std::string& accessKeySecret,
const std::string& region) {
// 1. Prepare time parameters
std::time_t now = std::time(nullptr);
std::tm* gmt = std::gmtime(&now);
char dateStamp[16], timestamp[32], rfc1123[64];
std::strftime(dateStamp, sizeof(dateStamp), "%Y%m%d", gmt);
std::strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", gmt);
std::strftime(rfc1123, sizeof(rfc1123), "%a, %d %b %Y %H:%M:%S GMT", gmt);
std::string scope = std::string(dateStamp) + "/" + region
+ "/sls/aliyun_v4_request";
// 2. Set required request headers
headers["x-log-date"] = timestamp;
headers["x-log-signaturemethod"] = "hmac-sha256";
headers["x-log-apiversion"] = "0.6.0";
headers["Date"] = rfc1123;
// 3. Build the CanonicalRequest
std::map<std::string, std::string> sorted;
for (auto& h : headers) {
std::string k = h.first;
std::transform(k.begin(), k.end(), k.begin(), ::tolower);
sorted[k] = h.second;
}
std::string canonicalHeaders, signedHeaders;
for (auto& h : sorted) {
canonicalHeaders += h.first + ":" + h.second + "\n";
if (!signedHeaders.empty()) signedHeaders += ";";
signedHeaders += h.first;
}
std::string payloadHash = sha256Hex(body);
std::string canonicalRequest =
method + "\n" + resource + "\n\n"
+ canonicalHeaders + "\n" + signedHeaders + "\n" + payloadHash;
// 4. Build the StringToSign
std::string stringToSign =
"SLS4-HMAC-SHA256\n" + std::string(timestamp) + "\n"
+ scope + "\n" + sha256Hex(canonicalRequest);
// 5. Calculate the signature
auto signingKey = getSigningKey(accessKeySecret, dateStamp, region);
auto sig = hmacSha256(signingKey, stringToSign);
std::string signature = toHex(sig.data(), sig.size());
// 6. Build the Authorization header
headers["Authorization"] =
"SLS4-HMAC-SHA256 Credential=" + accessKeyId + "/" + scope
+ ", SignedHeaders=" + signedHeaders
+ ", Signature=" + signature;
}
TypeScript
import * as crypto from "crypto";
function sha256Hex(data: string | Buffer): string {
return crypto.createHash("sha256").update(data).digest("hex");
}
function hmacSha256(key: string | Buffer, msg: string): Buffer {
return crypto.createHmac("sha256", key).update(msg).digest();
}
function getSigningKey(
secret: string, date: string, region: string
): Buffer {
const kDate = hmacSha256("aliyun_v4" + secret, date);
const kRegion = hmacSha256(kDate, region);
const kService = hmacSha256(kRegion, "sls");
return hmacSha256(kService, "aliyun_v4_request");
}
export function signV4(
method: string,
resource: string,
headers: Record<string, string>,
body: Buffer | null,
accessKeyId: string,
accessKeySecret: string,
region: string
): void {
// 1. Prepare time parameters
const now = new Date();
const dateStamp = now.toISOString().slice(0, 10).replace(/-/g, "");
const timestamp = dateStamp + "T" +
now.toISOString().slice(11, 19).replace(/:/g, "") + "Z";
const scope = `${dateStamp}/${region}/sls/aliyun_v4_request`;
// 2. Set required request headers
headers["x-log-date"] = timestamp;
headers["x-log-signaturemethod"] = "hmac-sha256";
headers["x-log-apiversion"] = "0.6.0";
headers["Date"] = now.toUTCString();
// 3. Build the CanonicalRequest
const sortedKeys = Object.keys(headers)
.map((k) => k.toLowerCase())
.sort();
const canonicalHeaders = sortedKeys
.map((k) => `${k}:${headers[k]?.trim() ?? ""}`)
.join("\n") + "\n";
const signedHeaders = sortedKeys.join(";");
const payloadHash = sha256Hex(body ?? Buffer.alloc(0));
const canonicalRequest = [
method.toUpperCase(), resource, "",
canonicalHeaders, signedHeaders, payloadHash,
].join("\n");
// 4. Build the StringToSign
const stringToSign = [
"SLS4-HMAC-SHA256", timestamp, scope,
sha256Hex(canonicalRequest),
].join("\n");
// 5. Calculate the signature
const signingKey = getSigningKey(accessKeySecret, dateStamp, region);
const signature = crypto
.createHmac("sha256", signingKey)
.update(stringToSign)
.digest("hex");
// 6. Build the Authorization header
headers["Authorization"] =
`SLS4-HMAC-SHA256 Credential=${accessKeyId}/${scope}, ` +
`SignedHeaders=${signedHeaders}, Signature=${signature}`;
}
Rust
use chrono::Utc;
use hmac::{Hmac, Mac};
use sha2::{Digest, Sha256};
use std::collections::BTreeMap;
type HmacSha256 = Hmac<Sha256>;
fn sha256_hex(data: &[u8]) -> String {
let mut hasher = Sha256::new();
hasher.update(data);
hex::encode(hasher.finalize())
}
fn hmac_sha256(key: &[u8], msg: &str) -> Vec<u8> {
let mut mac = HmacSha256::new_from_slice(key)
.expect("HMAC key error");
mac.update(msg.as_bytes());
mac.finalize().into_bytes().to_vec()
}
fn get_signing_key(secret: &str, date: &str, region: &str) -> Vec<u8> {
let initial = format!("aliyun_v4{}", secret);
let k_date = hmac_sha256(initial.as_bytes(), date);
let k_region = hmac_sha256(&k_date, region);
let k_service = hmac_sha256(&k_region, "sls");
hmac_sha256(&k_service, "aliyun_v4_request")
}
pub fn sign_v4(
method: &str,
resource: &str,
headers: &mut BTreeMap<String, String>,
body: &[u8],
access_key_id: &str,
access_key_secret: &str,
region: &str,
) {
// 1. Prepare time parameters
let now = Utc::now();
let date_stamp = now.format("%Y%m%d").to_string();
let timestamp = now.format("%Y%m%dT%H%M%SZ").to_string();
let scope = format!("{}/{}/sls/aliyun_v4_request", date_stamp, region);
// 2. Set required request headers
headers.insert("x-log-date".into(), timestamp.clone());
headers.insert("x-log-signaturemethod".into(), "hmac-sha256".into());
headers.insert("x-log-apiversion".into(), "0.6.0".into());
headers.insert("Date".into(),
now.format("%a, %d %b %Y %H:%M:%S GMT").to_string());
// 3. Build the CanonicalRequest
let sorted: BTreeMap<String, String> = headers
.iter()
.map(|(k, v)| (k.to_lowercase(), v.trim().to_string()))
.collect();
let canonical_headers: String = sorted
.iter()
.map(|(k, v)| format!("{}:{}\n", k, v))
.collect();
let signed_headers: String = sorted.keys()
.cloned()
.collect::<Vec<_>>()
.join(";");
let payload_hash = sha256_hex(body);
let canonical_request = format!(
"{}\n{}\n\n{}\n{}\n{}",
method.to_uppercase(), resource,
canonical_headers, signed_headers, payload_hash
);
// 4. Build the StringToSign
let string_to_sign = format!(
"SLS4-HMAC-SHA256\n{}\n{}\n{}",
timestamp, scope, sha256_hex(canonical_request.as_bytes())
);
// 5. Calculate the signature
let signing_key = get_signing_key(access_key_secret, &date_stamp, region);
let signature = hex::encode(
hmac_sha256(&signing_key, &string_to_sign));
// 6. Build the Authorization header
headers.insert("Authorization".into(), format!(
"SLS4-HMAC-SHA256 Credential={}/{}, SignedHeaders={}, Signature={}",
access_key_id, scope, signed_headers, signature
));
}
Comparison between V1 and V4 signatures
The following are the main differences between V1 (default signature) and V4 signatures:
|
Item |
V1 signature (default) |
V4 signature |
|
Signature algorithm |
HMAC-SHA1 |
HMAC-SHA256 |
|
Algorithm identifier |
|
|
|
Authorization format |
|
|
|
Signing key |
Uses the AccessKeySecret directly |
Derives the signing key step by step through four-layer HMAC |
|
Signature scope |
No Scope concept |
Includes |
|
Timestamp header |
|
|
|
Payload hash |
Not included |
Includes SHA-256(Body) in the canonical request |
|
Security |
Standard |
Higher; supports key isolation at the region and service dimensions |
Notes
Time synchronization: The deviation between the timestamp in the request and the server time cannot exceed 15 minutes; otherwise, the server rejects the request. We recommend that you use NTP to synchronize the local time.
Encoding rules: Special characters in the URI and query parameters must be URI-encoded (RFC 3986). Note that / in the CanonicalURI does not need to be encoded.
Header handling: Header names must be converted to lowercase during signing. Leading and trailing spaces must be removed from header values, and consecutive spaces must be merged into a single space.
STS token support: When you use STS temporary credentials, you must add the x-acs-security-token field to the request headers and include it in the signature calculation.