Simple Log Service API操作の各HTTPリクエストは、ログデータセキュリティを確保するためにセキュリティ検証に合格する必要があります。 Simple Log Serviceは、Alibaba Cloud AccessKeyペアと非対称暗号化アルゴリズムを使用してAPIリクエストを検証します。
使用上の注意
Simple Log Service SDKは、Simple Log ServiceのすべてのAPI操作をカプセル化します。 Simple Log Service SDKを使用して、Simple Log Service API操作を効率的に呼び出すことができます。 Simple Log Service SDKは自動的にリクエストに署名し、複数のプログラミング言語をサポートします。 SDKを使用してSimple Log Serviceにアクセスすることを推奨します。 詳細については、「Simple Log Service SDKの概要」をご参照ください。
Simple Log Serviceは、セキュリティ検証プロセスで次の操作を実行します。
APIリクエストを送信するユーザーを特定します。
ユーザーがAPIリクエストを送信する前に、APIリクエストのデジタル署名を生成するためにAccessKeyペアを指定する必要があります。 Simple Log Serviceは、AccessKeyペアを使用してユーザーを識別し、アクセス制御を実装します。
送信中にAPIリクエストが改ざんされていないか確認してください。
Simple Log ServiceがAPIリクエストを受信すると、Simple Log ServiceはAPIリクエストのデジタル署名を計算し、その署名をクライアント側で生成された署名と比較します。 送信中に要求が改ざんされた場合、2つの署名は一致せず、検証は失敗します。
APIリクエストのデジタル署名を生成するには、AccessKeyペアを使用する必要があります。 詳細は、「AccessKeyペア」をご参照ください。 既存のAccessKeyペアを使用するか、AccessKeyペアを作成できます。 AccessKeyペアが有効であることを確認します。
以下のセクションでは、リクエスト構造と署名の計算プロセスについて説明します。 次のセクションでは、署名を生成する方法を示すさまざまなプログラミング言語のサンプルコードについても説明します。
リクエストの構造
次の表に、有効なAPIリクエストに必要なHTTPヘッダーフィールドを示します。 フィールドのキーと値のペアを指定する必要があります。 値は大文字と小文字を区別します。
項目 | 説明 | 例 |
x-log-signaturemethod | リクエストの暗号化方法。 | hmac-sha1 |
x-log-apiversion | リクエストのAPIバージョン。 | 0.6.0 |
日付 | HTTPリクエストの標準タイムスタンプヘッダー。 ヘッダーは、RFC 822またはRFC 1123で定義されている時間形式に従い、GMTの時間値を表します。 フォーマット: % a, % d % b % Y % H:% M:% S GMT | Mon, 3 Jan 2010 08:33:47 GMT |
Content-MD5 | HTTPリクエストボディのMD5ハッシュ値。 MD5ハッシュ値を16進文字列に変換する必要があります。 | 72A15D7DE7EE9E7BB86461FFEA9499 |
承認 | 署名。 形式: LOG accessKeyId:Signature accessKeyIdはAccessKey IDを指定します。 Signatureは署名文字列を指定します。 署名文字列の詳細については、「署名計算プロセス」をご参照ください。 | ログtestAccessKeyId:RLETq4u7sWb3cssZIhsun **** |
Content-Type | HTTP リクエストボディの型 HTTPリクエスト本文が存在しない場合は、このフィールドを追加する必要はありません。 | application/json |
次のサンプルコードは、署名付きHTTPリクエストの例を示しています。
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"}
署名計算プロセス
Authorization
フィールドは、署名構造に必要です。 許可フィールドは、accessKeyId
フィールドとSignature
フィールドを連結するLOG AccessKeyId: Signature
形式です。 accessKeyIdはAccessKey IDを指定し、Signatureは署名文字列を指定します。 このセクションでは、signature
フィールドの署名文字列を作成する方法について説明します。
署名メッセージ
署名文字列は、署名メッセージを暗号化および符号化することによって生成される。 次の図は、署名メッセージの作成方法を示しています。
パラメーター | 説明 | 例 |
method | HTTP リクエストの方式。 値には大文字のみを使用できます。 有効な値: GET、POST、PUT、およびDELETE。 | GET |
Content-MD5 | HTTPリクエストボディのMD5ハッシュ値。 値を大文字を含む16進文字列に変換する必要があります。 HTTPリクエスト本文が存在しない場合、値は空の文字列です。 | 49DFDD54B01CBCD2D2AB5E9E5EE6B9B9 |
Content-Type | HTTP リクエストボディの型 HTTPリクエスト本文が存在しない場合、値は空の文字列になります。 | application/json |
日付 | 現在の時刻の書式付き文字列。 この文字列は、RFC 822またはRFC 1123で定義されている時間形式に従い、GMTの時間値を表します。 フォーマット: % a, % d % b % Y % H:% M:% S GMT | Mon, 3 Jan 2010 08:33:47 GMT |
ヘッダー | 先頭にx-log- またはx-acs- が付いているヘッダーフィールド。 フィールドはキーと値のペアで、キーと値のペアはキーごとに昇順にソートされます。 キーと値のペアのキーと値は、コロン (:) で接続されています。 キーと値のペアは改行で区切られます。 | |
ウリ | リクエストパス。 ドメイン名とクエリ関連のパラメーターは除外されます。 | /logstores/test-logstore |
クエリ関連のパラメーター | クエリ関連のパラメーター。 パラメーターはキーと値のペアで、キーと値のペアはキーごとに昇順にソートされます。 キーと値のペアのキーと値は、等号 (= ) で接続されています。 キーと値のペアはアンパサンド (& ) で区切ります。 | オフセット=1&サイズ=10 |
次のサンプルコードは、署名メッセージの例を示しています。 最后の行を除いて、各行は改行で终わります。
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
署名文字列
署名文字列を取得するプロセスは次のとおりです。
署名メッセージを取得します。
HMAC-SHA1アルゴリズムとAccessKeyシークレットを使用してメッセージを暗号化し、暗号化結果のハッシュ値を取得します。
Base64でハッシュ値をエンコードして署名文字列を生成します。
例
このセクションでは、APIリクエストに署名する方法の例を示します。 署名プロセスの例として、次のAccessKey IDとAccessKey secretを使用します。
AccessKeyId = "bq2sjzesjmo86kq****"
AccessKeySecret = "4fdO2fTDDnZPU/L7CHNd****"
例 1
GETリクエストを使用して、ali-test-projectという名前のプロジェクトのすべてのLogstoreを一覧表示します。 次のサンプルコードは、署名が必要なHTTPリクエストを示しています。
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
リクエストに対して次の署名メッセージが生成されます。
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
GETリクエストにはHTTPリクエストボディは含まれません。 したがって、Content-MD5およびContent-Typeフィールドの値は空の文字列になります。 次の署名文字列は、指定されたAccessKeyシークレットを使用して生成されます。
jEYOTCJs2e88o+y5F4/S5I****
次のサンプルコードは、署名付きHTTPリクエストを示しています。
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****
例 2
ali-test-projectという名前のプロジェクトのtest-Logstoreという名前のlogstoreに次のログを書き込みます。
topic=""
time=1447048976
source="10.10.10.1"
"TestKey": "TestContent"
次のサンプルコードは、署名が必要なHTTPリクエストを示しています。
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.>
ログはプロトコルバッファ (Protobuf) 形式のバイトストリームにシリアル化され、HTTPリクエストボディとして使用されます。 Protobuf形式の詳細については、「データエンコーディング」をご参照ください。 リクエストでは、Content-Typeフィールドの値はapplication/x-protobufです。 Content-MD5フィールドの値は、HTTPリクエストボディのMD5ハッシュ値です。 リクエストに対して次の署名メッセージが生成されます。
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
次の署名文字列は、指定されたAccessKeyシークレットを使用して生成されます。
XWLGYHGg2F2hcfxWxMLiNk****
次のサンプルコードは、署名付きHTTPリクエストを示しています。
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.>
サンプルコード
サンプルコードin Java
次のサンプルコードでは、commons-codecサードパーティ製ライブラリを使用しています。 次のMaven依存関係をpom.xml
に追加します。
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
次のサンプルコードは、APIリクエストに署名する方法の例を示しています。
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);
}
}
Pythonのサンプルコード
次のサンプルコードは、APIリクエストに署名する方法の例を示しています。
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
PHPのサンプルコード
次のサンプルコードは、APIリクエストに署名する方法の例を示しています。
<?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");
}
?>
Goのサンプルコード
次のサンプルコードは、APIリクエストに署名する方法の例を示しています。
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])
}
}
C# のサンプルコード
次のサンプルコードは、APIリクエストに署名する方法の例を示しています。
System.Security.Cryptographyを使用した
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;
}
C ++ のサンプルコード
次のサンプルコードは、APIリクエストに署名する方法の例を示しています。 ビジネス要件に基づいて、コードにヘルパー関数を実装する必要があります。 サンプルコードのadapter.hヘッダーファイルは、Simple Log Service SDK for C ++ に属しています。 Simple Log Service SDK for C ++ に基づいてリクエストに署名することを推奨します。 詳細については、「Simple Log Service SDK For C ++ の使用を開始する」をご参照ください。
CodecTool::CalcMD5 (文字列): MD5ハッシュ値を計算し、その値を大文字を含む16進文字列に変換します。
CodecTool::StartWith(string, string): 文字列が別の文字列で始まるかどうかを確認します。
CodecTool::Base64Encode(string): Base64で文字列をエンコードします。
CodecTool::CalcHMACSHA1(string, string): HMAC-SHA1アルゴリズムを使用してハッシュ値を計算します。
CodecTool::GetDateString(): 現在時刻の書式付き文字列を取得します。 フォーマット: % a, % d % b % Y % H:% M:% S GMT
例: 1月3日月曜日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;
}
TypeScriptのサンプルコード
次のサンプルコードでは、crypto-js
のサードパーティライブラリを使用しています。 以下のコマンドを実行して、依存関係をインストールします。
npm install crypto-js -- save
次のサンプルコードは、APIリクエストに署名する方法の例を示しています。
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'
)
Rustのサンプルコード
次のサンプルコードは、依存関係ライブラリの名前とバージョンの例を示しています。
[dependencies]
chrono = "0.4.19"
md5 = "0.7.0"
base64 = "0.13.0"
hmac-sha1 = "0.1.3"
次のサンプルコードは、APIリクエストに署名する方法の例を示しています。
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
}