Each HTTP request of a Simple Log Service API operation must pass security verification to ensure log data security. Simple Log Service uses Alibaba Cloud AccessKey pairs and asymmetric encryption algorithms to verify API requests.
Usage notes
Simple Log Service SDK encapsulates all API operations of Simple Log Service. You can use Simple Log Service SDK to call Simple Log Service API operations in an efficient manner. Simple Log Service SDK automatically signs requests and supports multiple programming languages. We recommend that you use the SDK to access Simple Log Service. For more information, see Overview of Simple Log Service SDK.
Simple Log Service performs the following operations in the security verification process:
Identify the user who sends an API request.
Before a user sends an API request, the user must specify an AccessKey pair to generate a digital signature for the API request. Simple Log Service uses the AccessKey pair to identify the user and implement access control.
Check whether an API request is tampered with during transmission.
After Simple Log Service receives an API request, Simple Log Service calculates a digital signature for the API request and compares the signature with the signature generated on the client side. If the request is tampered with during transmission, the two signatures do not match, and the verification fails.
To generate a digital signature for an API request, you must use an AccessKey pair. For more information, see AccessKey pair. You can use an existing AccessKey pair or create an AccessKey pair. Make sure that the AccessKey pair is enabled.
The following sections describe the request structure and the process of calculating a signature. The following sections also provide sample code in different programming languages to show how to generate signatures.
Request structure
The following table describes the required HTTP header fields of a valid API request. You must specify key-value pairs for the fields. The values are case-sensitive.
Field | Description | Example |
x-log-signaturemethod | The encryption method of the request. | hmac-sha1 |
x-log-apiversion | The API version of the request. | 0.6.0 |
Date | The standard timestamp header of the HTTP request. The header follows the time format defined in RFC 822 or RFC 1123 and represents a time value in GMT. Format: %a, %d %b %Y %H:%M:%S GMT . | Mon, 3 Jan 2010 08:33:47 GMT |
Content-MD5 | The MD5 hash value of the HTTP request body. You must convert the MD5 hash value into a hexadecimal string. | 72A15D7DE7EE9E7BB86461FFEA9499 |
Authorization | The signature. Format: LOG accessKeyId:Signature . accessKeyId specifies the AccessKey ID. Signature specifies the signature string. For more information about the signature string, see Signature calculation process. | LOG testAccessKeyId:RLETq4u7sWb3cssZIhsun**** |
Content-Type | The type of the HTTP request body. If no HTTP request body exists, you do not need to add this field. | application/json |
The following sample code provides an example of a signed HTTP request:
POST /logstores/test-logstore/shards/0?action=split HTTP/1.1
Host: ali-test-project.cn-hangzhou.log.aliyuncs.com
Date: Tue, 23 Aug 2022 12:12:03 GMT
x-log-apiversion: 0.6.0
x-log-signaturemethod: hmac-sha1
Content-Length: 18
Content-Type: application/json
Content-MD5: 49DFDD54B01CBCD2D2AB5E9E5EE6B9B9
Authorization: LOG testAccessKeyId:RLETq4u7sWb3cssZIhsun****
{"hello": "world"}
Signature calculation process
The Authorization
field is required in the signature structure. The Authorization field is in the LOG accessKeyId:Signature
format that concatenates the AccessKeyId
and Signature
fields. accessKeyId specifies an AccessKey ID, and Signature specifies a signature string. This section describes how to construct a signature string for the Signature
field.
Signature message
A signature string is generated by encrypting and encoding a signature message. The following figure shows how to construct a signature message.
Parameter | Description | Example |
method | The HTTP request method. The value can contain only uppercase letters. Valid values: GET, POST, PUT, and DELETE. | GET |
Content-MD5 | The MD5 hash value of the HTTP request body. You must convert the value into a hexadecimal string that contains uppercase letters. If no HTTP request body exists, the value is an empty string. | 49DFDD54B01CBCD2D2AB5E9E5EE6B9B9 |
Content-Type | The type of the HTTP request body. If no HTTP request body exists, the value can be an empty string. | application/json |
Date | The formatted string of the current time. The string follows the time format that is defined in RFC 822 or RFC 1123 and represents a time value in GMT. Format: %a, %d %b %Y %H:%M:%S GMT . | Mon, 3 Jan 2010 08:33:47 GMT |
Header | The header fields that are prefixed by x-log- or x-acs- . The fields are key-value pairs, and the key-value pairs are sorted in ascending order by key. The key and value in a key-value pair are connected by a colon (:) . Key-value pairs are separated by line feeds. | |
Uri | The request path. The domain name and query-related parameters are excluded. | /logstores/test-logstore |
Query-related parameters | The query-related parameters. The parameters are key-value pairs, and the key-value pairs are sorted in ascending order by key. The key and value in a key-value pair are connected by an equal sign (= ). Key-value pairs are separated by ampersands (& ). | offset=1&size=10 |
The following sample code provides an example of a signature message. Each line ends with a line feed, except for the last line.
POST
1572A15D7DE7EE9E7BB86461FFEA9499
application/json
Tue, 23 Aug 2022 12:12:03 GMT
x-log-apiversion:0.6.0
x-log-bodyrawsize:0
x-log-signaturemethod:hmac-sha1
/logstores?offset=1&size=10
Signature string
The process of obtaining a signature string is as follows:
Obtain a signature message.
Use the HMAC-SHA1 algorithm and an AccessKey secret to encrypt the message, and obtain a hash value of the encryption result.
Encode the hash value in Base64 to generate a signature string.
Examples
This section provides examples on how to sign an API request. The following AccessKey ID and AccessKey secret are used as an example to illustrate the signing process:
AccessKeyId = "bq2sjzesjmo86kq****"
AccessKeySecret = "4fdO2fTDDnZPU/L7CHNd****"
Example 1
Use a GET request to list all Logstores of a project named ali-test-project. The following sample code shows the HTTP request that needs to be signed:
GET /logstores?logstoreName=&offset=0&size=1000 HTTP/1.1
Date: Mon, 09 Nov 2015 06:11:16 GMT
Host: ali-test-project.cn-hangzhou.log.aliyuncs.com
x-log-apiversion: 0.6.0
x-log-bodyrawsize:0
x-log-signaturemethod: hmac-sha1
The following signature message is generated for the request:
GET
Mon, 09 Nov 2015 06:11:16 GMT
x-log-apiversion:0.6.0
x-log-bodyrawsize:0
x-log-signaturemethod:hmac-sha1
/logstores?logstoreName=&offset=0&size=1000
A GET request does not contain an HTTP request body. Therefore, the values of the Content-MD5 and Content-Type fields are empty strings. The following signature string is generated by using the specified AccessKey secret:
jEYOTCJs2e88o+y5F4/S5I****
The following sample code shows the signed HTTP request:
GET /logstores?logstoreName=&offset=0&size=1000 HTTP/1.1
Date: Mon, 09 Nov 2015 06:11:16 GMT
Host: ali-test-project.cn-hangzhou.log.aliyuncs.com
x-log-apiversion: 0.6.0
x-log-bodyrawsize:0
x-log-signaturemethod: hmac-sha1
Authorization: LOG bq2sjzesjmo86kq35behupbq:jEYOTCJs2e88o+y5F4/S5I****
Example 2
Write the following log to a Logstore named test-logstore in a project named ali-test-project:
topic=""
time=1447048976
source="10.10.10.1"
"TestKey": "TestContent"
The following sample code shows the HTTP request that needs to be signed:
POST /logstores/test-logstore HTTP/1.1
Date: Mon, 09 Nov 2015 06:03:03 GMT
Host: ali-test-project.cn-hangzhou.log.aliyuncs.com
x-log-apiversion:0.6.0
x-log-bodyrawsize:50
x-log-signaturemethod:hmac-sha1
Content-MD5: 1DD45FA4A70A9300CC9FE7305AF2C494
Content-Length: 52
<The log is serialized into byte streams in the Protobuf format.>
The log is serialized into byte streams in the Protocol Buffer (Protobuf) format, and then used as the HTTP request body. For more information about the Protobuf format, see Data encoding. In the request, the value of the Content-Type field is application/x-protobuf. The value of the Content-MD5 field is the MD5 hash value of the HTTP request body. The following signature message is generated for the request:
POST
1DD45FA4A70A9300CC9FE7305AF2C494
application/x-protobuf
Mon, 09 Nov 2015 06:03:03 GMT
x-log-apiversion:0.6.0
x-log-bodyrawsize:50
x-log-compresstype:lz4
x-log-signaturemethod:hmac-sha1
/logstores/ali-test-logstore
The following signature string is generated by using the specified AccessKey secret:
XWLGYHGg2F2hcfxWxMLiNk****
The following sample code shows the signed HTTP request:
POST /logstores/ali-test-logstore HTTP/1.1
Date: Mon, 09 Nov 2015 06:03:03 GMT
Host: ali-test-project.cn-hangzhou.log.aliyuncs.com
x-log-apiversion:0.6.0
x-log-bodyrawsize:50
x-log-compresstype:lz4
x-log-signaturemethod:hmac-sha1
Content-MD5: 1DD45FA4A70A9300CC9FE7305AF2C494
Content-Length: 52
Authorization: LOG bq2sjzesjmo86kq35behupbq:XWLGYHGg2F2hcfxWxMLiNk****
<The log is serialized into byte streams in the Protobuf format.>
Sample code
Sample code in Java
The following sample code uses the commons-codec third-party library. Add the following Maven dependency to pom.xml
:
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
The following sample code provides an example on how to sign an API request:
package com.aliyun.openservices.log.http.signer;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
public class v1 {
public static String md5(byte[] bs) throws Exception {
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(bs);
String hex = new BigInteger(1, digest.digest()).toString(16).toUpperCase();
return new String(new char[32 - hex.length()]).replace("\0", "0") + hex;
}
public static String getDateString() {
DateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
df.setTimeZone(new SimpleTimeZone(0, "GMT"));
return df.format(new Date());
}
public static void sign(String method, String uri, String accessKeyId, String accessKeySecret,
Map<String, String> params,
Map<String, String> headers,
byte[] body) throws Exception {
int contentLength = 0;
String contentMD5 = "", message = "";
headers.put("x-log-apiversion", "0.6.0");
headers.put("x-log-signaturemethod", "hmac-sha1");
if (body != null && body.length > 0) {
contentLength = body.length;
contentMD5 = md5(body);
headers.put("Content-MD5", contentMD5);
}
String date = getDateString();
headers.put("Date", date);
headers.put("Content-Length", String.valueOf(contentLength));
message += method + "\n"
+ contentMD5 + "\n"
+ headers.getOrDefault("Content-Type", "") + "\n"
+ date + "\n";
// header
String headerStr = headers.entrySet().stream()
.filter(e -> e.getKey().startsWith("x-log-") || e.getKey().startsWith("x-acs-"))
.sorted(Map.Entry.comparingByKey())
.map(e -> String.format("%s:%s\n", e.getKey(), e.getValue()))
.collect(Collectors.joining(""));
message += headerStr;
// uri & params
message += uri;
if (params.size() > 0) {
message += "?";
}
message += params.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(e -> String.format("%s=%s", e.getKey(), e.getValue()))
.collect(Collectors.joining("&"));
// signature & authorization
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec(accessKeySecret.getBytes(StandardCharsets.UTF_8), "HmacSHA1"));
String signature = new String(Base64.encodeBase64(mac.doFinal(message.getBytes(StandardCharsets.UTF_8))));
String auth = "LOG " + accessKeyId + ":" + signature;
headers.put("Authorization", auth);
}
}
Sample code in Python
The following sample code provides an example on how to sign an API request:
import base64
import hashlib
import hmac
import locale
from datetime import datetime
from typing import Dict, Tuple
def get_date():
try:
locale.setlocale(locale.LC_TIME, "C")
except Exception as ex:
pass
return datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
def sign(method: str, uri: str, access_key_id: str,
access_key_secret: str, params: Dict[str, str],
headers: Dict[str, str], body: bytes):
content_length = 0
content_md5, message = '', ''
headers["x-log-apiversion"] = "0.6.0"
headers["x-log-signaturemethod"] = "hmac-sha1"
if body is not None and len(body) > 0:
content_length = str(len(body))
content_md5 = hashlib.md5(body).hexdigest().upper()
headers['Content-MD5'] = content_md5
date = get_date()
headers['Date'] = date
headers['Content-Length'] = content_length
content_type = headers.get('Content-Type', '')
message += method + "\n" + content_md5 + \
"\n" + content_type + "\n" + date + "\n"
# header
filter_by_prefix = lambda t: t[0].startswith('x-log-') or t[0].startswith('x-acs-')
slsHeaders = list(filter(filter_by_prefix, headers.items()))
sort_by_key = lambda k: k[0]
for [k, v] in sorted(slsHeaders, key=sort_by_key):
message += k + ':' + v + "\n"
# uri and params
message += uri
message += '?' if len(params) > 0 else ''
sep = ''
for [k, v] in sorted(params.items(), key=sort_by_key):
message += sep + k + '=' + v
sep = '&'
# signature and authorization
hashed = hmac.new(access_key_secret.encode('utf8'),
message.encode('utf8'), hashlib.sha1).digest()
signature = base64.encodebytes(hashed).decode('utf8').rstrip()
auth = f'LOG {access_key_id}:{signature}'
headers['Authorization'] = auth
Sample code in PHP
The following sample code provides an example on how to sign an API request:
<?php
// returns new headers array
function sign($method, $uri, $accessKeyId, $accessKeySecret, $params, $headers, $body)
{
$contentLength = 0;
$headers["x-log-apiversion"] = "0.6.0";
$headers["x-log-signaturemethod"] = "hmac-sha1";
if (!is_null($body) && strlen($body) > 0) {
$contentLength = strlen($body);
$contentMd5 = strtoupper(md5($body));
$headers["Content-MD5"] = $contentMd5;
}
// date
setLocale(LC_TIME, 'en_US');
$date = gmdate('D, d M Y H:i:s \G\M\T', time());
$headers["Date"] = $date;
$headers["Content-Length"] = (string)$contentLength;
$contentType = isset($headers['Content-Type']) ? $headers['Content-Type'] : '';
$message = $method . "\n" . $contentMd5 . "\n" . $contentType . "\n" . $date . "\n";
// header
$filterHeaders = [];
foreach ($headers as $key => $val) {
if (str_starts_with($key, 'x-log-') || str_starts_with($key, 'x-acs-')) {
$filterHeaders[$key] = $val;
}
}
ksort($filterHeaders);
foreach ($filterHeaders as $key => $val) {
$message .= $key . ':' . $val . "\n";
}
// uri and params
$message .= $uri;
if (sizeof($params) > 0) {
$message .= '?';
}
ksort($params);
$sep = '';
foreach ($params as $key => $val) {
$message .= $sep . $key . '=' . $val;
$sep = '&';
}
// signature & authorization
$signature = base64_encode(hash_hmac('sha1', $message, $accessKeySecret, TRUE));
$auth = 'LOG ' . $accessKeyId . ':' . $signature;
$headers['Authorization'] = $auth;
return $headers;
};
// example call
$headers = sign(
'POST',
'/logstores/test-logstore',
'testAccessId',
'testAccessKey',
array(
"test" => "test",
"hello" => "world"
),
array(
"x-log-signaturemethod" => "hmac-sha1",
"x-log-bodyrawsize" => "0",
"x-log-apiversion" => "0.6.0"
),
'hello, world'
);
echo ($headers['Authorization']);
foreach ($headers as $key => $val) {
echo ($key . '=' . $val . "\n");
}
?>
Sample code in Go
The following sample code provides an example on how to sign an API request:
package sls
import (
"crypto/hmac"
"crypto/md5"
"crypto/sha1"
"encoding/base64"
"fmt"
"sort"
"strconv"
"strings"
"time"
)
/*
* @param uri The uri of http request, exclude host and query param, eg: /logstores/test-logstore
* @param headers This function modifies headers, headers must not be a nil map
* @param method Http method in uppercase , eg: GET, POST, PUT, DELETE
*/
func Sign(method, uri, accessKeyID, accessKeySecret string, headers, queryParams map[string]string, body []byte) error {
var message, signature string
var contentMD5, contentType, date string
headers["x-log-apiversion"] = "0.6.0";
headers["x-log-signaturemethod"] = "hmac-sha1";
if len(body) > 0 {
contentMD5 = fmt.Sprintf("%X", md5.Sum(body))
headers["Content-MD5"] = contentMD5
}
date = time.Now().In(time.FixedZone("GMT", 0)).Format(time.RFC1123)
headers["Date"] = date
headers["Content-Length"] = strconv.Itoa(len(body))
if val, ok := headers["Content-Type"]; ok {
contentType = val
}
message += method + "\n" + contentMD5 + "\n" + contentType + "\n" + date + "\n"
// header
slsHeaders := make(map[string]string)
for k, v := range headers {
if strings.HasPrefix(k, "x-log-") || strings.HasPrefix(k, "x-acs-") {
slsHeaders[k] = v
}
}
forEachInOrder(slsHeaders, func(k, v string) {
message += k + ":" + v + "\n"
})
message += uri
if len(queryParams) > 0 {
message += "?"
}
sep := ""
forEachInOrder(queryParams, func(k, v string) {
message += sep + k + "=" + v
sep = "&"
})
// Signature = base64(hmac-sha1(UTF8-Encoding-Of(SignString),AccessKeySecret))
mac := hmac.New(sha1.New, []byte(accessKeySecret))
_, err := mac.Write([]byte(message))
if err != nil {
return err
}
signature = base64.StdEncoding.EncodeToString(mac.Sum(nil))
auth := fmt.Sprintf("LOG %v:%v", accessKeyID, signature)
headers["Authorization"] = auth
return nil
}
func forEachInOrder(m map[string]string, f func(k, v string)) {
var ss sort.StringSlice
for k := range m {
ss = append(ss, k)
}
ss.Sort()
for _, k := range ss {
f(k, m[k])
}
}
Sample code in C#
The following sample code provides an example on how to sign an API request:
using System.Security.Cryptography;
using System.Text;
using StringMap = System.Collections.Generic.Dictionary<string, string>;
void sign(string method, string uri,
string accessKeyId, string accessKeySecret,
StringMap queryParams,
StringMap headers,
byte[] body
)
{
int contentLength = 0;
string contentMd5 = "", message = "";
headers["x-log-apiversion"] = "0.6.0";
headers["x-log-signaturemethod"] = "hmac-sha1";
if (body != null && body.Length > 0)
{
contentLength = body.Length;
contentMd5 = BitConverter.ToString(MD5.Create().ComputeHash(body)).Replace("-", "");
Console.WriteLine(contentMd5);
headers["Content-MD5"] = contentMd5;
}
string date = DateTime.Now.ToUniversalTime().ToString("R");
headers["Date"] = date;
headers["Content-Length"] = contentLength.ToString();
message += method + "\n"
+ contentMd5 + "\n"
+ headers.GetValueOrDefault("Content-Type", "") + "\n"
+ date + "\n";
var sortedHeader = from entry in headers orderby entry.Key ascending select entry;
// header
foreach (var entry in sortedHeader)
{
if (entry.Key.StartsWith("x-log-") || entry.Key.StartsWith("x-acs-"))
{
message += entry.Key + ":" + entry.Value + "\n";
}
}
// url & params
message += uri;
if (queryParams.Count() > 0)
{
message += "?";
}
var sortedParam = from entry in queryParams orderby entry.Key ascending select entry;
string sep = "";
foreach (var entry in sortedParam)
{
message += sep + entry.Key + "=" + entry.Value;
sep = "&";
}
var hmac = new HMACSHA1(Encoding.ASCII.GetBytes(accessKeySecret));
var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(message)));
var auth = "LOG " + accessKeyId + ":" + signature;
headers["Authorization"] = auth;
}
Sample code in C++
The following sample code provides an example on how to sign an API request. You must implement the helper functions in the code based on your business requirements. The adapter.h header file in the sample code belongs to Simple Log Service SDK for C++. We recommend that you sign the request based on Simple Log Service SDK for C++. For more information, see Get started with Simple Log Service SDK for C++.
CodecTool::CalcMD5(string): calculates an MD5 hash value and converts the value into a hexadecimal string that contains uppercase letters.
CodecTool::StartWith(string, string): checks whether a string starts with another string.
CodecTool::Base64Encode(string): encodes a string in Base64.
CodecTool::CalcHMACSHA1(string, string): uses the HMAC-SHA1 algorithm to calculate a hash value.
CodecTool::GetDateString(): obtains the formatted string of the current time. Format: %a, %d %b %Y %H:%M:%S GMT
. Example: Mon,3 Jan 2010 08:33:47 GMT.
#include <map>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "adapter.h"
using namespace std;
using aliyun_log_sdk_v6::CodecTool;
void Sign(const string& httpMethod, const string& uri,
map<string, string>& httpHeaders,
const map<string, string>& urlParams, const string& body,
const string& accessKeyId, const string& accessKeySecret)
{
string message;
string contentMd5, signature, contentType;
httpHeaders["x-log-apiversion"] = "0.6.0";
httpHeaders["x-log-signaturemethod"] = "hmac-sha1";
// 1. Content-Md5
if (!body.empty())
{
contentMd5 = CodecTool::CalcMD5(body);
httpHeaders["Content-Md5"] = contentMd5;
}
// 2. Date
string dateTime = CodecTool::GetDateString();
httpHeaders["Date"] = dateTime;
// 3. Content-Length
string contentLength = std::to_string(body.size());
httpHeaders["Content-Length"] = contentLength;
// 4. Content-Type
if (httpHeaders.find("Content-Type") != httpHeaders.end())
{
contentType = httpHeaders["Content-Type"];
}
message.append(httpMethod).append("\n");
message.append(contentMd5).append("\n");
message.append(contentType).append("\n");
message.append(dateTime).append("\n");
// 5. header
map<string, string> filteredHeaders;
for (auto it : httpHeaders)
{
string key = it.first, value = it.second;
if (CodecTool::StartWith(key, "x-log-") ||
CodecTool::StartWith(key, "x-acs-"))
{
filteredHeaders[key] = value;
}
}
for (auto it : filteredHeaders)
{
message.append(it.first).append(":").append(it.second);
message.append("\n");
}
// 6. uri and url params
message.append(uri);
if (urlParams.size() > 0) message.append("?");
for (auto it = urlParams.begin(); it != urlParams.end(); ++it)
{
if (it != urlParams.begin())
{
message.append("&");
}
message.append(it->first).append("=").append(it->second);
}
// 7.Signature
signature = CodecTool::Base64Enconde(
CodecTool::CalcHMACSHA1(message, accessKeySecret));
// 8. authorization
httpHeaders["authorization"] =
"LOG " + accessKeyId + ':' + signature;
}
Sample code in TypeScript
The following sample code uses the crypto-js
third-party library. Run the following command to install the dependency:
npm install crypto-js --save
The following sample code provides an example on how to sign an API request:
import CryptoJS from 'crypto-js'
export function sign(
method: string,
uri: string,
access_key_id: string,
access_key_secret: string,
params: Map<string, string>,
headers: Map<string, string>,
body: string | undefined
) {
let content_length = 0
let content_md5 = '',
message = ''
headers.set("x-log-apiversion", "0.6.0")
headers.set("x-log-signaturemethod", "hmac-sha1")
if (body !== undefined && body.length > 0) {
content_length = body.length
content_md5 = CryptoJS.MD5(body).toString(CryptoJS.enc.Hex).toUpperCase()
headers.set('Content-MD5', content_md5)
}
const date = new Date().toUTCString()
headers.set('Date', date)
headers.set('Content-Length', content_length.toString())
message +=
method +
'\n' +
content_md5 +
'\n' +
(headers.get('Content-Type') ?? '') +
'\n' +
date +
'\n'
// headers
const sort_by_key = (a: [string, string], b: [string, string]) =>
a[0].localeCompare(b[0])
const filter_by_prefix = (e: [string, string]) =>
e[0].startsWith('x-log-') || e[0].startsWith('x-acs-')
const header_str = [...headers.entries()]
.filter(filter_by_prefix)
.sort(sort_by_key)
.map((e) => e[0] + ':' + e[1] + '\n')
.join('')
message += header_str
// uri & query params
message += uri
if (params.size > 0) {
message += '?'
}
message += [...params.entries()]
.sort(sort_by_key)
.map((e) => e[0] + '=' + e[1])
.join('&')
// signature & authorization
const signature = CryptoJS.HmacSHA1(message, access_key_secret).toString(
CryptoJS.enc.Base64
)
const auth = 'LOG ' + access_key_id + ':' + signature
headers.set('Authorization', auth)
}
// example call
sign(
'POST',
'/logstores/test-logstore',
'testAccessId',
'testAccessKey',
new Map<string, string>([
['test', 'test'],
['hello', 'world'],
]),
new Map<string, string>([
['x-log-signaturemethod', 'hmac-sha1'],
['x-log-bodyrawsize', '0'],
['x-log-apiversion', '0.6.0'],
['Content-Type', 'application/json'],
]),
'hello, world'
)
Sample code in Rust
The following sample code provides an example of the names and versions of the dependency libraries:
[dependencies]
chrono = "0.4.19"
md5 = "0.7.0"
base64 = "0.13.0"
hmac-sha1 = "0.1.3"
The following sample code provides an example on how to sign an API request:
extern crate base64;
extern crate hmacsha1;
extern crate md5;
use chrono::Utc;
use std::collections::*;
pub fn sign(
method: &str,
uri: &str,
access_key_id: &str,
access_key_secret: &str,
params: &HashMap<String, String>,
headers: &mut HashMap<String, String>,
body: Option<Vec<u8>>,
) -> String {
let mut content_length = 0;
let mut content_md5 = String::from("");
headers.insert("x-log-apiversion".to_owned(), "0.6.0".to_owned());
headers.insert("x-log-signaturemethod".to_owned(), "hmac-sha1".to_owned());
if let Some(content) = body {
if content.len() > 0 {
content_length = content.len();
content_md5 = format!("{:X}", md5::compute(content));
headers.insert("Content-MD5".to_owned(), content_md5.clone());
}
}
headers.insert("Content-Length".to_owned(), content_length.to_string());
// date
let date = Utc::now().format("%a, %d %b %Y %H:%M:%S GMT").to_string();
headers.insert("Date".to_owned(), date.clone());
let mut message = "".to_owned();
let content_type = headers
.get(&"Content-Type".to_owned())
.cloned()
.unwrap_or(String::from(""));
message += format!("{}\n{}\n{}\n{}\n", method, content_md5, content_type, date).as_str();
// header
let mut sorted_header: Vec<_> = headers.iter().collect();
sorted_header.sort_by_key(|x| x.0);
for (k, v) in sorted_header {
if k.starts_with("x-log-") || k.starts_with("x-acs-") {
message += format!("{}:{}\n", k, v).as_str();
}
}
// url & params
message += uri;
if params.len() > 0 {
message += "?";
}
let mut sorted_params: Vec<_> = params.iter().collect();
sorted_params.sort_by_key(|x| x.0);
let mut sep = "";
for (k, v) in sorted_params {
message += format!("{}{}={}", sep, k, v).as_str();
sep = "&";
}
let signature = base64::encode(hmacsha1::hmac_sha1(
access_key_secret.as_bytes(),
message.as_bytes(),
));
let auth = format!("LOG {}:{}", access_key_id, signature);
headers.insert("Authorization".to_owned(), auth.to_owned());
auth
}