ApsaraMQ for RocketMQ は、Authorization ヘッダー内の署名を検証することで、すべての HTTP API リクエストを認証します。以下のセクションでは、有効な署名を構築し、それをリクエストにアタッチする方法について説明します。
認証の仕組み
ApsaraMQ for RocketMQ は、対称暗号化を使用して各リクエスト送信者の ID を検証します。
ご利用の AccessKey Secret を署名キーとして使用し、リクエストコンテンツから署名を構築します。
署名を HTTP リクエストの
Authorizationヘッダーにアタッチします。サーバーは、同じアルゴリズムとご利用の AccessKey Secret を使用して、予期される署名を計算します。
2 つの署名が一致する場合、リクエストは認証されます。そうでない場合、サーバーは HTTP 403 でリクエストを拒否します。
前提条件
開始する前に、以下があることを確認してください。
AccessKey ペア (AccessKey ID と AccessKey Secret) -- 詳細については、「AccessKey ペアの作成」をご参照ください。
Apsara Stack は、各ユーザーに AccessKey ペアを発行します。AccessKey ペアは Apsara Uni-manager 管理コンソールで申請および管理できます。AccessKey ペアは次の 2 つの部分で構成されます。
| コンポーネント | 目的 |
|---|---|
| AccessKey ID | リクエストの送信者を識別します |
| AccessKey Secret | リクエストに署名して、ご利用の ID を証明します。この値は厳重に機密保持してください |
Authorization ヘッダーのフォーマット
Authorization ヘッダーを次のフォーマットで設定します。
MQ <AccessKey ID>:<Signature>プレースホルダー値の例:
MQ LTAI5tExampleKey:dGhpcyBpcyBhbiBleGFtcGxlIHNpZw==署名の構築
有効な署名を構築するには、署名対象文字列の組み立て、HMAC-SHA1 ハッシュの計算、および結果の Base64 エンコードの 3 つのステップが必要です。
Step 1: 署名対象文字列の組み立て
以下のフィールドを改行文字 (\n) で区切り、単一の UTF-8 文字列に連結します。
HTTP_METHOD + "\n"
+ "\n"
+ CONTENT-TYPE + "\n"
+ DATE + "\n"
+ "x-mq-version:" + MQVersion + "\n"
+ CanonicalizedResource次の表は、各フィールドについて説明しています。
| フィールド | 説明 | 値またはフォーマット |
|---|---|---|
| HTTP_METHOD | HTTP メソッド (大文字) | PUT, GET, POST, または DELETE |
| CONTENT-TYPE | リクエストボディのメディアタイプ | text/xml; charset=utf-8 |
| DATE | RFC 2616 フォーマット (協定世界時 (UTC)) のリクエストタイムスタンプ | 例: Thu, 07 Mar 2012 18:49:58 GMT |
| MQVersion | ApsaraMQ for RocketMQ API バージョン | 2015-06-06 |
| CanonicalizedResource | クエリパラメーターを含む、アクセスされるリソースの URI | 例: /topics/abc/messages?consumer=GID_abc |
署名対象文字列の 2 行目は意図的に空です。改行文字を含めますが、コンテンツは含めません。
コンシュームリクエストの署名対象文字列の例:
POST
text/xml; charset=utf-8
Thu, 07 Mar 2012 18:49:58 GMT
x-mq-version:2015-06-06
/topics/abc/messages?consumer=GID_abcStep 2: HMAC-SHA1 ハッシュの計算
ご利用の AccessKey Secret を署名キーとして使用し、署名対象文字列に HMAC-SHA1 アルゴリズム (RFC 2104) を適用します。
Step 3: 結果の Base64 エンコード
バイナリ HMAC-SHA1 出力を Base64 エンコードして、最終署名文字列を生成します。
完全な数式:
Signature = base64(hmac-sha1(AccessKeySecret, StringToSign))関数リファレンス
| 関数 | 説明 |
|---|---|
hmac-sha1(key, message) | SHA-1 ハッシュアルゴリズムを使用して HMAC ダイジェストを計算します。これは、RFC 2104 で定義されています。キー は、AccessKey Secret です。 |
base64(data) | バイナリデータを Base64 文字列にエンコードします。 |
完全な例
この例では、次のパラメーターを持つメッセージ消費リクエストに署名します。
| パラメーター | 値 |
|---|---|
| HTTP メソッド | POST |
| Content-Type | text/xml; charset=utf-8 |
| Date | Thu, 07 Mar 2012 18:49:58 GMT |
| API バージョン | 2015-06-06 |
| リソース URI | /topics/abc/messages?consumer=GID_abc |
| AccessKey ID | LTAI5tExampleKey |
| AccessKey Secret | xXxExampleSecretxXx |
Step 1 -- 署名対象文字列の組み立て:
POST
text/xml; charset=utf-8
Thu, 07 Mar 2012 18:49:58 GMT
x-mq-version:2015-06-06
/topics/abc/messages?consumer=GID_abcPOST の後の空白行は、意図的に空にされた 2 行目です。
Step 2 および 3 -- HMAC-SHA1 の計算と Base64 エンコード:
Signature = base64(hmac-sha1("xXxExampleSecretxXx", StringToSign))結果の Authorization ヘッダー:
Authorization: MQ LTAI5tExampleKey:<computed-signature><computed-signature> を Base64 エンコードされた HMAC-SHA1 出力に置き換えます。
コード例
次の例は、一般的なプログラミング言語で署名を生成する方法を示しています。各例は、署名対象文字列を構築し、HMAC-SHA1 ハッシュを計算し、結果を Base64 エンコードします。
Python
import hmac
import hashlib
import base64
import os
from email.utils import formatdate
# Obtain credentials from environment variables.
access_key_id = os.environ["MQ_ACCESS_KEY_ID"]
access_key_secret = os.environ["MQ_ACCESS_KEY_SECRET"]
# Request parameters
http_method = "POST"
content_type = "text/xml; charset=utf-8"
date = formatdate(usegmt=True) # RFC 2616 format, e.g. "Thu, 07 Mar 2012 18:49:58 GMT"
mq_version = "2015-06-06"
canonicalized_resource = "/topics/abc/messages?consumer=GID_abc"
# Step 1: Assemble the string-to-sign.
string_to_sign = (
http_method + "\n"
+ "\n"
+ content_type + "\n"
+ date + "\n"
+ "x-mq-version:" + mq_version + "\n"
+ canonicalized_resource
)
# Step 2-3: Compute HMAC-SHA1 and Base64-encode.
signature = base64.b64encode(
hmac.new(
access_key_secret.encode("utf-8"),
string_to_sign.encode("utf-8"),
hashlib.sha1,
).digest()
).decode("utf-8")
# Build the Authorization header.
authorization = f"MQ {access_key_id}:{signature}"
print(authorization)Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.Locale;
public class MqSignature {
public static void main(String[] args) throws Exception {
// Obtain credentials from environment variables.
String accessKeyId = System.getenv("MQ_ACCESS_KEY_ID");
String accessKeySecret = System.getenv("MQ_ACCESS_KEY_SECRET");
// Request parameters
String httpMethod = "POST";
String contentType = "text/xml; charset=utf-8";
String date = DateTimeFormatter
.ofPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US)
.format(ZonedDateTime.now(ZoneOffset.UTC));
String mqVersion = "2015-06-06";
String canonicalizedResource = "/topics/abc/messages?consumer=GID_abc";
// Step 1: Assemble the string-to-sign.
String stringToSign = httpMethod + "\n"
+ "\n"
+ contentType + "\n"
+ date + "\n"
+ "x-mq-version:" + mqVersion + "\n"
+ canonicalizedResource;
// Step 2-3: Compute HMAC-SHA1 and Base64-encode.
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec(
accessKeySecret.getBytes(StandardCharsets.UTF_8), "HmacSHA1"));
byte[] rawSignature = mac.doFinal(
stringToSign.getBytes(StandardCharsets.UTF_8));
String signature = Base64.getEncoder().encodeToString(rawSignature);
// Build the Authorization header.
String authorization = "MQ " + accessKeyId + ":" + signature;
System.out.println(authorization);
}
}Go
package main
import (
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"fmt"
"os"
"time"
)
func main() {
// Obtain credentials from environment variables.
accessKeyID := os.Getenv("MQ_ACCESS_KEY_ID")
accessKeySecret := os.Getenv("MQ_ACCESS_KEY_SECRET")
// Request parameters
httpMethod := "POST"
contentType := "text/xml; charset=utf-8"
date := time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT")
mqVersion := "2015-06-06"
canonicalizedResource := "/topics/abc/messages?consumer=GID_abc"
// Step 1: Assemble the string-to-sign.
stringToSign := httpMethod + "\n" +
"\n" +
contentType + "\n" +
date + "\n" +
"x-mq-version:" + mqVersion + "\n" +
canonicalizedResource
// Step 2-3: Compute HMAC-SHA1 and Base64-encode.
mac := hmac.New(sha1.New, []byte(accessKeySecret))
mac.Write([]byte(stringToSign))
signature := base64.StdEncoding.EncodeToString(mac.Sum(nil))
// Build the Authorization header.
authorization := fmt.Sprintf("MQ %s:%s", accessKeyID, signature)
fmt.Println(authorization)
}Node.js
const crypto = require("crypto");
// Obtain credentials from environment variables.
const accessKeyId = process.env.MQ_ACCESS_KEY_ID;
const accessKeySecret = process.env.MQ_ACCESS_KEY_SECRET;
// Request parameters
const httpMethod = "POST";
const contentType = "text/xml; charset=utf-8";
const date = new Date().toUTCString(); // RFC 2616 format
const mqVersion = "2015-06-06";
const canonicalizedResource = "/topics/abc/messages?consumer=GID_abc";
// Step 1: Assemble the string-to-sign.
const stringToSign = [
httpMethod,
"",
contentType,
date,
`x-mq-version:${mqVersion}`,
canonicalizedResource,
].join("\n");
// Step 2-3: Compute HMAC-SHA1 and Base64-encode.
const signature = crypto
.createHmac("sha1", accessKeySecret)
.update(stringToSign, "utf-8")
.digest("base64");
// Build the Authorization header.
const authorization = `MQ ${accessKeyId}:${signature}`;
console.log(authorization);トラブルシューティング
次の表は、一般的な署名エラーとそのソリューションを示しています。
| エラー | 原因 | ソリューション |
|---|---|---|
| HTTP 403: 署名の不一致 | 署名対象文字列がサーバーの期待と一致しません | 署名対象文字列を出力し、各フィールドを確認します。2 行目の空白行が存在し、余分な空白が追加されていないことを確認してください。 |
| HTTP 403: 日付のずれが大きすぎます | DATE ヘッダーの値が、サーバーの時計と大きく異なっています | ご利用のシステム時刻を NTP サーバーと同期し、協定世界時 (UTC) を使用してください。 |
| HTTP 403: 無効な AccessKey ID | AccessKey ID が存在しないか、無効になっています | コンソールで AccessKey ID が正しいか、アクティブな状態かを確認してください。 |
| 実行ごとに署名が異なります | DATEDATE ヘッダーはリクエストごとに変更されるため、毎回異なる署名が生成されます | これは予期される動作です。各リクエストに対して新しい署名を生成してください。 |
| エンコーディングエラー | 署名対象文字列が UTF-8 でエンコードされていません | すべての文字列操作で UTF-8 エンコーディングを使用していることを確認してください。 |
制約事項
署名対象文字列は UTF-8 エンコーディングを使用する必要があります。
DATEヘッダーの値は協定世界時 (UTC) である必要があります。RFC 2616 日付フォーマット:Day, DD Mon YYYY HH:MM:SS GMTを使用してください。Authorizationヘッダーはすべての HTTP リクエストに含める必要があります。