All Products
Search
Document Center

Alibaba Cloud SDK:V2 ROA-style request body and signature mechanism

Last Updated:Jul 03, 2026

Sign and send ROA-style HTTP requests to Alibaba Cloud OpenAPI using the V2 signature mechanism.

Important

This access method is no longer recommended. Use the V3 request body and signature mechanism instead.

HTTP request structure

A complete OpenAPI request includes these parts.

Name

Required

Description

Example

Protocol

Yes

The request protocol. Check OpenAPI metadata for supported protocols. Use HTTPS when available.

https://

Endpoint

Yes

The service endpoint. Check the product-specific endpoint documentation.

bailian.cn-beijing.aliyuncs.com

resource_URI_parameters

Yes

The API URL, including the path and query parameters.

/{WorkspaceId}/datacenter/category

RequestHeader

Yes

The request header, including the API version, Host, and Authorization. Defined in RequestHeader below.

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

Yes

The request body with business parameters. Test requests in the Alibaba Cloud OpenAPI Developer Portal.

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

HTTPMethod

Yes

The HTTP method: PUT, POST, GET, or DELETE. The required method is specified in the API document.

POST

RequestHeader

The RequestHeader includes these fields.

Name

Type

Required

Description

Example

Accept

String

No

Response format. ROA only supports application/json.

application/json

Content-MD5

String

No

Base64-encoded 128-bit MD5 hash of the RequestBody.

LP54yxk8n7KqF1PPgbJizw==

Date

String

Yes

Current timestamp in HTTP 1.1 GMT format. Valid for 15 minutes.

Wed, 16 Apr 2025 03:44:46 GMT

Host

String

Yes

The service endpoint. Must match the endpoint in HTTP request structure.

bailian.cn-beijing.aliyuncs.com

x-acs-signature-method

String

Required for non-anonymous requests

The signing algorithm. Fixed value: HMAC-SHA1.

HMAC-SHA1

x-acs-signature-nonce

String

Yes

Unique random number to prevent replay attacks. Must differ per request.

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

x-acs-signature-version

String

No

The signature version. Fixed value: 1.0.

1.0

x-acs-version

String

Yes

The API version. Find the version in the Alibaba Cloud OpenAPI Developer Portal or the OpenAPI metadata.

2023-12-29

Authorization

String

Required for non-anonymous requests

Authentication string in the format AccessKeyId:Signature. Find your AccessKey ID in the RAM console. Create an AccessKey.

Signature is the request signature, calculated per Signature mechanism.

acs YourAccessKeyId:D9uFJAJgLL+dryjBfQK+YeqGtoY=

Signature mechanism

All requests must include a signature for authentication. Alibaba Cloud API Gateway recalculates the signature from request parameters and headers, then compares it with the submitted signature to verify identity and data integrity. Calculate the signature as follows:

Note

Requests and responses use UTF-8 encoding.

Step 1: Construct the canonicalized headers

Canonicalized headers (CanonicalizedHeaders) are non-standard HTTP headers prefixed with x-acs-. Construct them as follows:

  1. Convert x-acs- header names from RequestHeader to lowercase and sort them lexicographically.

  2. Trim leading and trailing spaces from header names and values.

  3. Append \n after each header (including the last), then concatenate all headers into the CanonicalizedHeaders.

Example:

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

Step 2: Construct the canonicalized resource

Construct the canonicalized resource (CanonicalizedResource) as follows:

  1. Sort query string parameters by name lexicographically and join with &.

  2. Append the sorted query string to the resource path with ?. The resource path is the URL segment between the host and the query string, starting with /. Use the resource path alone if no query string exists.

Example:

/llm-p2e4XXXXXXXXsvtn/datacenter/category

Step 3: Construct the string to sign

Construct the string to sign (stringToSign) using this pseudocode:

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

Parameter

Description

HTTPMethod

Uppercase HTTP method, such as POST or GET.

Accept

Accept header value. Empty string if absent.

ContentMD5

Content-MD5 header value. Empty string if absent.

ContentType

Content-Type header value. Empty string if absent.

Note

The MIME type of the RequestBody.

Date

Date header value.

CanonicalizedHeaders

Canonicalized headers from Step 1: Construct the canonicalized headers.

CanonicalizedResource

Canonicalized resource from Step 2: Construct the canonicalized resource.

Example:

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

Step 4: Calculate the signature

Per RFC2104, sign the string from Step 3 using HMAC-SHA1 and Base64-encode the result.

String signature = Base64(HMAC_SHA1(SigningKey, stringToSign))
Important

SigningKey is your AccessKey secret. Create an AccessKey.

Example:

s8ZMF/eAIvvPJwehLLha0bVNFJ0=

Step 5: Construct the Authorization header

Set the Authorization header to the following value.

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

Example value:

acs YourAccessKeyId:s8ZMF/eAIvvPJwehLLha0bVNFJ0=

Step 6: Add the signature to the request and send the request

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"}

Signature example code

Java example

Note

Runs on JDK 1.8. Adjust for your environment.

Add these Maven dependencies to pom.xml.

<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) {
        // Example 1: POST request
        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);

        /*// Example 2: GET request
        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");*/

        /*// Example 3: DELETE request
        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);*/

        // Generate Authorization
        generateSignature(signatureRequest);
        // Send the request using HTTPClient
        callApi(signatureRequest);
    }

    private static void generateSignature(SignatureRequest signatureRequest) {
        try {
            // Step 1: Construct the canonicalized headers
            String canonicalHeaders = buildCanonicalHeaders(signatureRequest);
            // Step 2: Construct the canonicalized resource
            String canonicalQueryString = buildQueryString(signatureRequest);
            // Step 3: Construct the string to sign
            String stringToSign = buildStringToSign(signatureRequest, canonicalQueryString, canonicalHeaders);
            // Step 4: Construct the signature string
            String signature = signString(stringToSign);
            // Step 5: Construct the Authorization header
            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 {
            // Send the request using HttpClient
            String url = "https://" + signatureRequest.host + signatureRequest.canonicalUri;
            URIBuilder uriBuilder = new URIBuilder(url);
            // Add request parameters
            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");
            }
            // Add HTTP request headers
            signatureRequest.headers.forEach(httpRequest::addHeader);
            // Send the request
            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) {
                // Handle exceptions
                System.out.println("Failed to send request");
                throw new IllegalArgumentException(e.toString());
            }
        } catch (URISyntaxException e) {
            // Handle exceptions
            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());
        }
    }
}

References

Differentiate between ROA and RPC styles.