すべてのプロダクト
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 スタイルの違い