Each HTTP request of a Log Service API operation must pass security verification to ensure log data security. Log Service uses the asymmetric encryption algorithm to verify API requests based on Alibaba Cloud AccessKey pairs.

Usage notes

Log Service SDKs encapsulate all API operations of Log Service. You can use a Log Service SDK to call all API operations of Log Service in an efficient manner. Log Service provides SDKs for multiple programming languages. The SDKs automatically sign requests. We recommend that you use a Log Service SDK to access Log Service. For more information, see Overview of Log Service SDKs.

The verification process is used to perform the following operations:
  • Identify who sends the API request.

    A user must specify an AccessKey pair to generate the digital signature for an API request. When an AccessKey pair is specified, Log Service uses the AccessKey pair to identify the user and implement access control.

  • Check whether a request header is tampered during transmission.

    The server calculates the digital signature of an API request and compares the digital signature with the signature calculated by the client. If the request header is tampered during transmission, the two signatures are different and the verification fails.

For more information, see AccessKey pair. You can use an existing AccessKey pair or create an AccessKey pair. The AccessKey pair must be in the Active state.

The following sections describe the signature structure and the process of calculating a signature, and provide sample code in different programming languages. The sample code shows how signature algorithms are used to calculate signatures.

Signature structure

The following table describes the key-value pairs that are required in the HTTP request header of a valid API request. The values are case-sensitive.
FieldDescriptionExample
x-log-signaturemethodThe encryption method of the request. hmac-sha1
x-log-apiversionThe version of the API. 0.6.0
DateThe standard timestamp header of the HTTP request. The timestamp header follows the time format that is defined in RFC 822 or RFC 1123 and uses the UTC standard time. Format: %a, %d %b %Y %H:%M:%S GMT. Mon, 3 Jan 2010 08:33:47 GMT
Content-MD5The MD5 hash value of the HTTP request body. The value must be converted to a hexadecimal string that contains only uppercase letters. 72A15D7DE7EE9E7BB86461FFEA9499
AuthorizationThe signature. Format: LOG accessKeyId:Signature. accessKeyId specifies the AccessKey ID. Signature specifies the signature string. For information about the signature string, see Signature structure string. LOG testAccessKeyId:RLETq4u7sWb3cssZIhsun****
Content-TypeThe type of the HTTP request body. If no HTTP request body exists, you do not need to add this field. application/json
The following section 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: 1572A15D7DE7EE9E7BB86461FFEA9499
Authorization: LOG testAccessKeyId:RLETq4u7sWb3cssZIhsun****


{"hello": "world"}

Calculate a signature

The Authorization field is an important component of 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 encrypted and encoded based on a signature message. The following figure shows how to construct a signature message. The following table describes the parameters.signature message
ParameterDescriptionExample
methodThe HTTP request method. The value contains only uppercase letters. Valid values: GET, POST, PUT, and DELETE. GET
Content-MD5The MD5 hash value of the HTTP request body. The value must be converted to a hexadecimal string that contains only uppercase letters. If the HTTP request body is left empty, the value is an empty string. 1572A15D7DE7EE9E7BB86461FFEA9499
Content-TypeThe type of the HTTP request body. If the HTTP request body is left empty, the value can be an empty string. application/json
DateThe formatted string of the current time. The string follows the time format that is defined in RFC 822 or RFC 1123 and represents a UTC standard time. Format: %a, %d %b %Y %H:%M:%S GMT. Mon, 3 Jan 2010 08:33:47 GMT
headerThe request header that contains key-value pairs prefixed by x-log- or x-acs-. 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.
  • x-log-apiversion:0.6.0
  • x-log-bodyrawsize:0
  • x-log-signaturemethod:hmac-sha1
uriThe path of the request. The domain name and the query parameters are excluded. /logstores/test-logstore
query parametersThe query parameters of the request. The query parameters are in the key-value pair format and 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 section provides an example of a signature message. Except the last line, each line ends with a line feed.
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 following figure shows how to obtain a signature string.encrypted hash value
  1. Obtain a signature message.
  2. Use the HMAC-SHA1 algorithm and an AccessKey secret to encrypt the message and obtain an encrypted hash value.
  3. Encode the hash value in Base64 to generate a signature string.

Examples

This section provides examples on how to sign an API request. In the examples, the following AccessKey ID and AccessKey secret are used in an API request:
AccessKeyId = "bq2sjzesjmo86kq****"
AccessKeySecret = "4fdO2fTDDnZPU/L7CHNd****"
  • Example 1
    You want to list all Logstores of a project named ali-test-project. In this case, you can send the following GET 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
    The following signature string is generated for the preceding API request:
    GET\n\n\nMon, 09 Nov 2015 06:11:16 GMT\nx-log-apiversion:0.6.0\x-log-bodyrawsize:0\nx-log-signaturemethod:hmac-sha1\n/logstores?logstoreName=&offset=0&size=1000
    The GET request does not contain an HTTP request body. The values of the Content-MD5 and Content-Type fields are empty strings. The following string is the digital signature that is calculated by using the specified AccessKey secret:
    jEYOTCJs2e88o+y5F4/S5I****
    The following request is a signed API 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
    You want to write the following log to a Logstore named test-logstore, which belongs to the ali-test-project project:
    topic=""
    time=1447048976
    source="10.10.10.1"
    "TestKey": "TestContent"
    In this case, you can send the following HTTP request:
    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 to byte streams in the ProtoBuffer format.>
    The log that you want to write to Log Service is serialized into byte streams in the ProtoBuffer format, and then used as the HTTP request body. For more information about the ProtoBuffer format, see Data encoding. In this case, the value of the Content-Type header field is application/x-protobuf. The value of the Content-MD5 header field is the MD5 hash of the HTTP request body. The following string is the string to be signed that is generated for the API request:
    POST\n1DD45FA4A70A9300CC9FE7305AF2C494\napplication/x-protobuf\nMon, 09 Nov 2015 06:03:03 GMT\nx-log-apiversion:0.6.0\nx-log-bodyrawsize:50\nx-log-compresstype:lz4\nx-log-signaturemethod:hmac-sha1\n/logstores/ali-test-logstore
    The following string is the digital signature that is calculated by using the specified AccessKey secret:
    XWLGYHGg2F2hcfxWxMLiNk****
    The following request is a signed API 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 ProtoBuffer format.>

Sample code

  • Sample code of a Java signature
    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 is used to sign an API request and is for reference only:
    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 of a Python signature
    The following sample code is used to sign an API request and is for reference only:
    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 = 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 of a PHP signature
    The following sample code is used to sign an API request and is for reference only:
    <?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 of a Go signature
    The following sample code is used to sign an API request and is for reference only:
    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 of a C# signature
    The following sample code is used to sign an API request and is for reference only:
    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 of a C++ signature
    The following sample code is used 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 Log Service SDK for C++. We recommend that you sign the request based on Log Service SDK for C++. For more information, see Get started with Log Service SDK for C++.
    • CodecTool::CalcMD5(string): calculates the MD5 hash value and converts the value to a hexadecimal string that contains only uppercase letters.
    • CodecTool::StartWith(string, string): checks whether the string starts with another string.
    • CodecTool::Base64Enconde(string): encodes the 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 of a TypeScript signature
    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 is used to sign an API request and is for reference only:
    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 of a Rust signature
    The following section shows 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 is used to sign an API request and is for reference only:
    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
    }