すべてのプロダクト
Search
ドキュメントセンター

Alibaba Cloud SDK:V2 ROA スタイルのリクエストボディと署名メカニズム

最終更新日:Jun 04, 2026

V2 署名メカニズムを使用して、ROA スタイルの HTTP リクエストに署名し、Alibaba Cloud OpenAPI に送信します。

重要

このアクセス方法は現在推奨されていません。代わりに V3 リクエストボディと署名メカニズム を使用してください。

HTTP リクエストの構造

完全な OpenAPI リクエストには、以下の部分が含まれます。

名前

必須

説明

例

プロトコル

はい

リクエストプロトコルです。サポートされているプロトコルについては、API メタデータ をご確認ください。利用可能な場合は HTTPS を使用してください。

https://

エンドポイント

はい

サービスのエンドポイントです。プロダクト固有のエンドポイントドキュメントをご確認ください。

bailian.cn-beijing.aliyuncs.com

resource_URI_parameters

はい

パスとクエリパラメーターを含む API の URL です。

/{WorkspaceId}/datacenter/category

RequestHeader

はい

API バージョン、Host、Authorization を含むリクエストヘッダーです。詳細は後述の RequestHeader で定義されています。

Accept:application/json

Authorization:acs YourAccessKeyId:s8ZMF/eAIvvPJwehLLha0bVNFJ0=

Content-MD5:LP54yxk8n7KqF1PPgbJizw==

Content-Type:application/json

Date:Wed, 16 Apr 2025 03:44:46 GMT

Host:bailian.cn-beijing.aliyuncs.com

x-acs-signature-method:HMAC-SHA1

x-acs-signature-nonce:ef34aae7-7bd2-413d-a541-680cd2c48538

x-acs-signature-version:1.0

x-acs-version:2023-12-29

RequestBody

はい

ビジネスパラメーターを含むリクエストボディです。リクエストのテストは Alibaba Cloud OpenAPI Developer Portal で行えます。

{"CategoryName":"test","CategoryType":"UNSTRUCTURED"}

HTTPMethod

はい

HTTP メソッド (PUT、POST、GET、または DELETE) です。必要なメソッドは API ドキュメントで指定されています。

POST

RequestHeader

RequestHeader には以下のフィールドが含まれます。

名前

タイプ

必須

説明

例

Accept

String

いいえ

応答フォーマット。ROA は application/json のみをサポートします。

application/json

Content-MD5

String

いいえ

RequestBody の 128 ビット MD5 ハッシュを Base64 エンコードしたものです。

LP54yxk8n7KqF1PPgbJizw==

Date

String

はい

HTTP 1.1 の GMT フォーマットでの現在のタイムスタンプです。15 分間有効です。

Wed, 16 Apr 2025 03:44:46 GMT

Host

String

はい

サービスのエンドポイントです。HTTP リクエストの構造 のエンドポイントと一致する必要があります。

bailian.cn-beijing.aliyuncs.com

x-acs-signature-method

String

匿名リクエスト以外では必須

署名アルゴリズム。固定値:HMAC-SHA1。

HMAC-SHA1

x-acs-signature-nonce

String

はい

リプレイ攻撃を防ぐためのユニークな乱数です。リクエストごとに異なる必要があります。

ef34aae7-7bd2-413d-a541-680cd2c48538

x-acs-signature-version

String

いいえ

署名バージョン。固定値:1.0。

1.0

x-acs-version

String

はい

API バージョン。Alibaba Cloud OpenAPI Developer Portal または API メタデータ でバージョンを確認してください。

2023-12-29

Authorization

String

匿名リクエスト以外では必須

AccessKeyId:Signature フォーマットの認証文字列です。AccessKey ID は RAM コンソール で確認できます。AccessKey を作成してください。

Signature は、署名メカニズム に従って計算されたリクエスト署名です。

acs YourAccessKeyId:D9uFJAJgLL+dryjBfQK+YeqGtoY=

署名メカニズム

すべてのリクエストには、認証のための署名を含める必要があります。Alibaba Cloud API Gateway は、リクエストパラメーターとヘッダーから署名を再計算し、送信された署名と比較して ID とデータ整合性を検証します。署名は次のように計算します:

説明

リクエストと応答には UTF-8 エンコーディングを使用します。

ステップ 1:正規化ヘッダーの構築

正規化ヘッダー (CanonicalizedHeaders) は、x-acs- をプレフィックスとする非標準の HTTP ヘッダーです。次のように構築します:

  1. RequestHeader から x-acs- ヘッダー名を小文字に変換し、辞書順にソートします。

  2. ヘッダー名と値の先頭と末尾のスペースをトリミングします。

  3. 各ヘッダー (最後のヘッダーを含む) の後に \n を追加し、すべてのヘッダーを連結して CanonicalizedHeaders を作成します。

例:

x-acs-signature-method:HMAC-SHA1
x-acs-signature-nonce:ef34aae7-7bd2-413d-a541-680cd2c48538
x-acs-signature-version:1.0
x-acs-version:2023-12-29

ステップ 2:正規化リソースの構築

正規化リソース (CanonicalizedResource) を次のように構築します:

  1. クエリ文字列パラメーターを名前の辞書順にソートし、& で結合します。

  2. ソートされたクエリ文字列を ? を付けてリソースパスに追加します。リソースパスは、ホストとクエリ文字列の間の URL セグメントで、/ で始まります。クエリ文字列が存在しない場合は、リソースパスのみを使用します。

例:

/llm-p2e4XXXXXXXXsvtn/datacenter/category

ステップ 3:署名対象文字列の構築

次の擬似コードを使用して、署名対象文字列 (stringToSign) を構築します:

String stringToSign = 
    HTTPMethod + "\n" +
    Accept + "\n" +
    ContentMD5 + "\n" +
    ContentType + "\n" +
    Date + "\n" +
    CanonicalizedHeaders +
    CanonicalizedResource

パラメーター

説明

HTTPMethod

大文字の HTTP メソッド (例:POST、GET)。

Accept

Accept ヘッダーの値。存在しない場合は空文字列。

ContentMD5

Content-MD5 ヘッダーの値。存在しない場合は空文字列。

ContentType

Content-Type ヘッダーの値。存在しない場合は空文字列。

説明

RequestBody の MIME タイプ。

Date

Date ヘッダーの値。

CanonicalizedHeaders

ステップ 1:正規化ヘッダーの構築 で作成した正規化ヘッダー。

CanonicalizedResource

ステップ 2:正規化リソースの構築 で作成した正規化リソース。

例:

POST
application/json
LP54yxk8n7KqF1PPgbJizw==
application/json
Wed, 16 Apr 2025 03:44:46 GMT
x-acs-signature-method:HMAC-SHA1
x-acs-signature-nonce:ef34aae7-7bd2-413d-a541-680cd2c48538
x-acs-signature-version:1.0
x-acs-version:2023-12-29
/llm-p2e4XXXXXXXXsvtn/datacenter/category

ステップ 4:署名の計算

RFC2104 に従い、ステップ 3 の文字列を HMAC-SHA1 で署名し、結果を Base64 エンコードします。

String signature = Base64(HMAC_SHA1(SigningKey, stringToSign))
重要

SigningKey はご利用の AccessKey Secret です。AccessKey を作成してください。

例:

s8ZMF/eAIvvPJwehLLha0bVNFJ0=

ステップ 5:Authorization ヘッダーの構築

Authorization ヘッダーを次の値に設定します。

String Authorization = "acs " + AccessKeyId + ":" + signature

値の例:

acs YourAccessKeyId:s8ZMF/eAIvvPJwehLLha0bVNFJ0=

ステップ 6:リクエストへの署名の追加とリクエストの送信

POST https://bailian.cn-beijing.aliyuncs.com/llm-p2e4XXXXXXXXsvtn/datacenter/category HTTP/1.1
 
Accept:application/json
Authorization:acs YourAccessKeyId:r8Y9ZqVhTrYGl4nieqk7CW0Pwow=
Content-MD5:LP54yxk8n7KqF1PPgbJizw==
Content-Type:application/json
Date:Wed, 16 Apr 2025 06:47:10 GMT
Host:bailian.cn-beijing.aliyuncs.com
x-acs-signature-method:HMAC-SHA1
x-acs-signature-nonce:e3d8efa7-b1d8-42f3-9733-4fe2691e15dc
x-acs-signature-version:1.0
x-acs-version:2023-12-29

{"CategoryName":"test","CategoryType":"UNSTRUCTURED"}

署名サンプルコード

Java の例

説明

JDK 1.8 で動作します。ご利用の環境に合わせて調整してください。

pom.xml に以下の Maven 依存関係を追加します。

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>
<dependency>
     <groupId>com.google.code.gson</groupId>
     <artifactId>gson</artifactId>
     <version>2.9.0</version>
 </dependency>
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;


public class SignatureDemo {
    public static class SignatureRequest {
        private final String httpMethod;
        private final String host;
        private final String version;
        private final String canonicalUri;

        public TreeMap<String, String> headers = new TreeMap<>();
        public TreeMap<String, Object> queryParams = new TreeMap<>();
        public byte[] bodyByteArray;

        public SignatureRequest(String httpMethod, String host, String version, String canonicalUri) {
            this.httpMethod = httpMethod;
            this.host = host;
            this.version = version;
            this.canonicalUri = canonicalUri;
            initHeaders();
        }

        private void initHeaders() {
            headers.put("Host", host);
            headers.put("x-acs-version", version);
            headers.put("x-acs-signature-version", "1.0");
            headers.put("Accept", "application/json");
            headers.put("x-acs-signature-nonce", java.util.UUID.randomUUID().toString());
            headers.put("Date", getGmtDate());
            headers.put("x-acs-signature-method", "HMAC-SHA1");
        }

        private String getGmtDate() {
            SimpleDateFormat gmtFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.ENGLISH);
            gmtFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
            return gmtFormat.format(new Date());
        }

        public void setBody(Map<String, String> body, ContentType contentType) {
            Gson gson = (new GsonBuilder()).disableHtmlEscaping().create();
            this.bodyByteArray = gson.toJson(body).getBytes(StandardCharsets.UTF_8);
            headers.put("Content-MD5", md5Sum(this.bodyByteArray));
            headers.put("Content-Type", contentType.getMimeType());
        }
    }

    private static final String ACCESS_KEY_ID = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
    private static final String ACCESS_KEY_SECRET = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
    private static final String ALGORITHM_NAME = "HmacSHA1";

    public static void main(String[] args) {
        // 例 1:POST リクエスト
        String method = "POST";
        String host = "bailian.cn-beijing.aliyuncs.com";
        String version = "2023-12-29";
        String canonicalUri = "/llm-p2e4XXXXXXXXsvtn/datacenter/category";
        SignatureRequest signatureRequest = new SignatureRequest(method, host, version, canonicalUri);
        Map<String, String> body = new HashMap<>();
        body.put("CategoryName", "test");
        body.put("CategoryType", "UNSTRUCTURED");
        System.out.println(new Gson().toJson(body));
        signatureRequest.setBody(body, ContentType.APPLICATION_JSON);

        /*// 例 2:GET リクエスト
        String method = "GET";
        String host = "bailian.cn-beijing.aliyuncs.com";
        String version = "2023-12-29";
        String canonicalUri = "/llm-p2e4XXXXXXXXsvtn/datacenter/files";
        SignatureRequest signatureRequest = new SignatureRequest(method, host, version, canonicalUri);
        signatureRequest.queryParams.put("CategoryId", "cate_a946*********************_10045991");*/

        /*// 例 3:DELETE リクエスト
        String method = "DELETE";
        String host = "bailian.cn-beijing.aliyuncs.com";
        String version = "2023-12-29";
        String canonicalUri = "/llm-p2e4XXXXXXXXsvtn/datacenter/category/cate_a946*********************_10045991";
        SignatureRequest signatureRequest = new SignatureRequest(method, host,  version, canonicalUri);*/

        // Authorization の生成
        generateSignature(signatureRequest);
        // HTTPClient を使用してリクエストを送信
        callApi(signatureRequest);
    }

    private static void generateSignature(SignatureRequest signatureRequest) {
        try {
            // ステップ 1:正規化ヘッダーの構築
            String canonicalHeaders = buildCanonicalHeaders(signatureRequest);
            // ステップ 2:正規化リソースの構築
            String canonicalQueryString = buildQueryString(signatureRequest);
            // ステップ 3:署名対象文字列の構築
            String stringToSign = buildStringToSign(signatureRequest, canonicalQueryString, canonicalHeaders);
            // ステップ 4:署名文字列の構築
            String signature = signString(stringToSign);
            // ステップ 5:Authorization ヘッダーの構築
            String authorization = "acs " + ACCESS_KEY_ID + ":" + signature;
            signatureRequest.headers.put("Authorization", authorization);
        } catch (Exception ex) {
            throw new IllegalArgumentException(ex.toString());
        }
    }

    private static void callApi(SignatureRequest signatureRequest) {
        try {
            // HttpClient を使用してリクエストを送信
            String url = "https://" + signatureRequest.host + signatureRequest.canonicalUri;
            URIBuilder uriBuilder = new URIBuilder(url);
            // リクエストパラメーターの追加
            signatureRequest.queryParams.forEach((key, value) -> uriBuilder.addParameter(key, String.valueOf(value)));
            HttpUriRequest httpRequest;
            switch (signatureRequest.httpMethod) {
                case "GET":
                    httpRequest = new HttpGet(uriBuilder.build());
                    break;
                case "POST":
                    HttpPost httpPost = new HttpPost(uriBuilder.build());
                    if (signatureRequest.bodyByteArray != null) {
                        httpPost.setEntity(new ByteArrayEntity(signatureRequest.bodyByteArray, ContentType.create(signatureRequest.headers.get("Content-Type"))));
                    }
                    httpRequest = httpPost;
                    break;
                case "PUT":
                    HttpPut httpPut = new HttpPut(uriBuilder.build());
                    if (signatureRequest.bodyByteArray != null) {
                        httpPut.setEntity(new ByteArrayEntity(signatureRequest.bodyByteArray, ContentType.create(signatureRequest.headers.get("Content-Type"))));
                    }
                    httpRequest = httpPut;
                    break;
                case "DELETE":
                    httpRequest = new HttpDelete(uriBuilder.build());
                    break;
                default:
                    System.out.println("Unsupported HTTP method: " + signatureRequest.httpMethod);
                    throw new IllegalArgumentException("Unsupported HTTP method");
            }
            // HTTP リクエストヘッダーの追加
            signatureRequest.headers.forEach(httpRequest::addHeader);
            // リクエストの送信
            try (CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = httpClient.execute(httpRequest)) {
                String result = EntityUtils.toString(response.getEntity(), "UTF-8");
                System.out.println(result);
            } catch (IOException e) {
                // 例外処理
                System.out.println("Failed to send request");
                throw new IllegalArgumentException(e.toString());
            }
        } catch (URISyntaxException e) {
            // 例外処理
            System.out.println("Invalid URI syntax");
            throw new IllegalArgumentException(e.toString());
        }
    }

    private static String buildCanonicalHeaders(SignatureRequest signatureRequest) {
        StringBuilder canonicalHeaders = new StringBuilder();
        signatureRequest.headers.entrySet().stream()
                .filter(entry -> entry.getKey().startsWith("x-acs-"))
                .forEach(entry -> canonicalHeaders.append(entry.getKey().toLowerCase().trim()).append(":").append(entry.getValue().trim()).append("\n"));
        return canonicalHeaders.toString();
    }

    private static String buildQueryString(SignatureRequest signatureRequest) {
        StringBuilder queryBuilder = new StringBuilder(signatureRequest.canonicalUri);
        if (!signatureRequest.queryParams.isEmpty()) {
            queryBuilder.append("?");
        }

        for (Map.Entry<String, Object> entry : signatureRequest.queryParams.entrySet()) {
            queryBuilder.append(entry.getKey());
            String value = (String) entry.getValue();
            if (value != null && !value.isEmpty()) {
                queryBuilder.append("=").append(value).append("&");
            } else {
                queryBuilder.append("&");
            }
        }
        String queryString = queryBuilder.toString();
        if (queryString.endsWith("&")) {
            queryString = queryString.substring(0, queryString.length() - 1);
        }
        return queryString;
    }

    private static String buildStringToSign(SignatureRequest signatureRequest, String canonicalQueryString, String canonicalHeaders) {
        StringBuilder sb = new StringBuilder();
        sb.append(signatureRequest.httpMethod).append("\n");
        appendIfPresent(sb, signatureRequest.headers.get("Accept"));
        appendIfPresent(sb, signatureRequest.headers.get("Content-MD5"));
        appendIfPresent(sb, signatureRequest.headers.get("Content-Type"));
        appendIfPresent(sb, signatureRequest.headers.get("Date"));
        sb.append(canonicalHeaders);
        sb.append(canonicalQueryString);
        return sb.toString();
    }

    private static void appendIfPresent(StringBuilder sb, String value) {
        if (value != null) {
            sb.append(value).append("\n");
        } else {
            sb.append("\n");
        }
    }

    private static String signString(String stringToSign) {
        try {
            Mac mac = Mac.getInstance(ALGORITHM_NAME);
            mac.init(new SecretKeySpec(ACCESS_KEY_SECRET.getBytes(StandardCharsets.UTF_8), ALGORITHM_NAME));
            byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
            return DatatypeConverter.printBase64Binary(signData);
        } catch (NoSuchAlgorithmException | InvalidKeyException ex) {
            throw new IllegalArgumentException(ex.toString());
        }
    }

    public static String md5Sum(byte[] buff) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] md5Bytes = md.digest(buff);
            return DatatypeConverter.printBase64Binary(md5Bytes);
        } catch (NoSuchAlgorithmException ex) {
            throw new IllegalArgumentException(ex.toString());
        }
    }
}

参考資料

ROA スタイルと RPC スタイルの違い。