Sign and send ROA-style HTTP requests to Alibaba Cloud OpenAPI using the V2 signature mechanism.
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 |
|
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 |
|
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 |
|
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 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:
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:
-
Convert
x-acs-header names from RequestHeader to lowercase and sort them lexicographically. -
Trim leading and trailing spaces from header names and values.
-
Append
\nafter 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:
-
Sort query string parameters by name lexicographically and join with
&. -
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))
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
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());
}
}
}