Overview
When you send an HTTP request to OceanBase Cloud Platform (OCP), OCP calculates a signature for your request and generates an authentication string to authenticate your identity, protect data security during transmission, and prevent replay attacks.
The system calculates the signature by using an AccessKey of OCP. Each AccessKey consists of an AccessKey ID (AK) and an AccessKey Secret (SK).
Procedure
Step 1: Obtain the AK/SK pair.
Log on to the OCP console.
Move the pointer over the username in the upper-right corner. In the menu that appears, click Personal Settings.
On the Personal Settings page, click Create AccessKey Pair in the AccessKey section. For more information, see Configure personal information.
After the AK/SK pair is created, keep it properly.
Step 2: Initiate a request by using the AK/SK pair.
Method 1 (recommended): Call the API by using the OCP-Client SDK.
You only need to select the AccessKey authentication mode when you create an OCP-Client. Signature calculation and configuration are encapsulated in the SDK, which avoids manual operations.
Method 2: If the OCP-Client SDK is unavailable, you must manually calculate and configure the signature based on the following rules:
When you send an HTTP request to OCP, make sure that the HTTP header contains the Authorization and Date fields so that the OCP server can authenticate the request.
Authorization: OCP-ACCESS-KEY-{Signature algorithm} {AK}:{Signature}.
Date: Tue, 17 Jan 2023 03:36:01 GMT.
Signature algorithm: Only
HMACSHA1is supported.HMACSHA1must be written in uppercase.AK: The AK. Note that the signature algorithm and the AK must be separated with a space.
Signature: The calculated signature. For more information about the calculation rules, see Generate a signature.
Date: The time when the request was initiated. The value must be in RFC1123 format. A difference between the time when the request was initiated and the time when the request arrives at the server must be less than 15 minutes.**** The time zone and time information must be consistent between the client and the server.
After receiving your request, the OCP server parses out and verifies the Authorization and Date fields. If the verification succeeds, the OCP server continues to handle the request. Otherwise, an error is returned. Only the HTTP header can contain authentication strings.
Notice
The AK/SK pair belongs to a user and is granted the same permissions as the user.
Generate a signature
Process:

Formula: $\mathrm{signature} = Base64(H(K, \mathrm{message}))$
Base64: Indicates that Base64 is used for encoding.
H: A hash function. Only HMACSHA1 is supported.
K: The SK.
message: The message body of the signature. For more information, see Message.
Message
Formula:
message = Http Method + "\n" + Content-md5 + "\n" + Content-Type + "\n" + Date+ "\n" + Host+ "\n" + x-ocp-headers + "\n" + uri + query parameters
Notice
uri and query parameters are concatenated by using a plus sign (+) instead of the escape sequence "\n". When a field such as Content-md5 is empty, "\n" is still required for concatenation.
The formula shows that the message consists of eight parts: Http Method, Content-md5, Content-Type, Date, Host, x-ocp-headers, uri, and query parameters. The following table describes the fields.
| Field | Description | Example |
|---|---|---|
| Http Method | The HTTP request method, in uppercase. Valid values: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, and TRACE. | GET |
| Content-md5 | The MD5 hash value of the HTTP request body, which is converted into a hexadecimal string in uppercase. | 1572A15D7DE7EE9E7BB86461FFEA9499 |
| Content-Type | The type of the HTTP request body. | application/json |
| Date | The time when the request was initiated, in RFC1123 format. | Mon, 3 Jan 2010 08:33:47 GMT |
| Host | The host of the HTTP request. | xxx.xxx.xxx.xxx:8080 |
| x-ocp-headers | The key-value pairs with the x-ocp- prefix in the header, sorted by key in ascending order. A key and a value are connected with a colon (:). Multiple values are separated with commas (,) and sorted in ascending order. Key-value pairs are separated with line breaks. |
x-ocp-date:Tue, 17 Jan 2023 03:36:01 GMT |
| uri | The path of the request, excluding the domain name and request parameters. | /api/v2/iam/users |
| query parameters | The query parameters of the request, which start with a question mark (?) and are sorted by key in ascending order. A key and a value are connected with an equal sign (=). Multiple values are separated with commas (,) and sorted in ascending order. Key-value pairs are separated with ampersands (&). Keys and values must be URL-encoded based on the rules specified in RFC3986. | ?a=1&b=2
Notice |
Notice
Sorting rules for x-ocp-headers and query parameters: Two strings are lexicographically sorted by Unicode value of each character in the strings. If all Unicode values are the same, the shorter string precedes the longer string in lexicographical order. Check special characters in URL-encoded query parameters. For example, the plus sign (+) is escaped as "%20".
Sample message (line breaks are added to the end of all lines except the last one):
POST
186974DB33A090A16D3E2CA35F547B56
application/json
Tue, 17 Jan 2023 03:36:01 GMT
ocp.alibaba.net:8080
x-ocp-data:1,2,A,J,Z
/api/v2/compute/idcs?userId=1001
Examples of signing a request
This topic describes how to sign a request.
Assume that the following AK/SK pair is obtained:
AK: cqammmxBpfGjFlto
SK: 2fc0c299cc94c6be266f2ceece765d4d
Example 1
Call an OCP API to create an IDC. The initial HTTP request (irrelevant information is omitted) is as follows:
POST /api/v2/compute/idcs HTTP/1.1
Content-Type application/json
x-ocp-data A,1
Host ocp.alibaba.net:8080
Request body: {"name":"test01","description":"test","regionId":1} Time when the request was initiated: Tue, 17 Jan 2023 09:13:57 GMT
Notice
The MD5 hash value of the request body is 186974DB33A090A16D3E2CA35F547B56.
The following message is constructed for the preceding API (for more information, see Message):
POST\n + 186974DB33A090A16D3E2CA35F547B56\n + application/json\n + Tue, 17 Jan 2023 09:13:57 GMT\n + ocp.alibaba.net:8080\n + x-ocp-data:A,1\n + /api/v2/compute/idcs
The following signature is obtained based on the SK and message:
XN8P+O+v3vUabB16ZCooq5wMJoY=
The following value of Authorization is obtained:
OCP-ACCESS-KEY-HMACSHA1 cqammmxBpfGjFlto:XN8P+O+v3vUabB16ZCooq5wMJoY=
The Authorization and Date fields are added to the request header to obtain the following signed HTTP request:
POST /api/v2/compute/idcs HTTP/1.1
Content-Type application/json
x-ocp-data A,1
Authorization OCP-ACCESS-KEY-HMACSHA1 cqammmxBpfGjFlto:XN8P+O+v3vUabB16ZCooq5wMJoY=
Date Tue, 17 Jan 2023 09:13:57 GMT
Host ocp.alibaba.net:8080
Example 2
Call an OCP API to query the IDC list in pagination mode. The initial HTTP request (irrelevant information is omitted) is as follows:
GET /api/v2/compute/idcs?size=100 HTTP/1.1
Content-Type application/json;charset=utf-8
Host ocp.alibaba.net:8080
Time when the request was initiated: Tue, 17 Jan 2023 04:14:02 GMT
Notice
The MD5 hash value is an empty string because the request body is empty. The x-ocp-headers field is also an empty string because the header contains no parameters with the x-ocp prefix.
The following message is constructed for the preceding API:
GET\n + \n + application/json;charset=utf-8\n + Tue, 17 Jan 2023 04:14:02 GMT\n + ocp.alibaba.net:8080\n + \n + /api/v2/compute/idcs?size=100
Similarly, the following signature is obtained based on the SK in the preceding example:
TsQD6HDOuZuJ409m0wdnZPmijlc=
The following value of Authorization is obtained:
OCP-ACCESS-KEY-HMACSHA1 cqammmxBpfGjFlto:TsQD6HDOuZuJ409m0wdnZPmijlc=
The Authorization and Date fields are added to the request header to obtain the following signed HTTP request:
GET /api/v2/compute/idcs?size=100 HTTP/1.1
Content-Type application/json;charset=utf-8
Authorization OCP-ACCESS-KEY-HMACSHA1 cqammmxBpfGjFlto:TsQD6HDOuZuJ409m0wdnZPmijlc=
Date Tue, 17 Jan 2023 04:14:02 GMT
Host ocp.alibaba.net:8080
Connection keep-alive
Java sample code for signing a request
import com.google.common.collect.Lists;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.BitSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.stream.Collectors;
public class ExampleTest {
public static void main(String[] args) {
String sk = "2fc0c299cc94c6be266f2ceece765d4d";
String httpMethod = "POST";
byte[] bodyByte = "{\"name\":\"test01\",\"description\":\"test\",\"regionId\":1}".getBytes(StandardCharsets.UTF_8);
String contentType = "application/json";
String rfc1123Date = "Tue, 17 Jan 2023 09:13:57 GMT";
String host = "ocp.alibaba.net:8080";
Map<String, List<String>> xOcpHeaders = new HashMap<>();
ArrayList<String> list = Lists.newArrayList("A", "1");
xOcpHeaders.put("x-ocp-data", list);
String path = "/api/v2/compute/idcs";
Map<String, List<String>> queryParams = new HashMap<>();
String generator = generator(sk, httpMethod, bodyByte, contentType, rfc1123Date, host, xOcpHeaders,
path, queryParams);
// XN8P+O+v3vUabB16ZCooq5wMJoY=
System.out.println(generator);
}
/**
* Generate a signature.
*/
public static String generator(String accessKeySecret,
String httpMethod,
byte[] body,
String contentType,
String rfc1123Date,
String host,
Map<String, List<String>> headers,
String path,
Map<String, List<String>> queryParams) {
String message = buildMessage(
httpMethod,
md5Crypt(body),
contentType,
rfc1123Date,
host,
buildXOcpHeader(headers),
buildUrlAndParam(path, queryParams));
System.out.println(message);
// Encode
return Base64.getEncoder().encodeToString(
hmacSha1(accessKeySecret, message.getBytes(StandardCharsets.UTF_8)));
}
/**
* Sort the parameters and construct a new request URL for signing.
*
* @param requestUri The request URL.
* @param paramMap The request parameters.
* @return The URL and parameters to be signed.
*/
public static String buildUrlAndParam(String requestUri, Map<String, List<String>> paramMap) {
if (MapUtils.isEmpty(paramMap)) {
return requestUri;
}
String urlParam = paramMap.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(e -> {
String value = e.getValue().stream()
.filter(StringUtils::isNotEmpty).sorted().collect(Collectors.joining(","));
return String.format("%s=%s", encoder(e.getKey()), encoder(value));
})
.collect(Collectors.joining("&"));
urlParam = urlParam.replace("%2B", "%20");
return requestUri + "?" + urlParam;
}
/**
* Construct a header to be signed.
*
* @param headers The request header.
* @return The header to be signed.
*/
public static String buildXOcpHeader(Map<String, List<String>> headers) {
return headers.entrySet().stream()
.filter(e -> StringUtils.startsWithIgnoreCase(e.getKey(), "x-ocp"))
.sorted(Map.Entry.comparingByKey())
.map(e -> String.format("%s:%s", e.getKey(), String.join(",", e.getValue())))
.collect(Collectors.joining("\n"));
}
/**
* MD5 encryption
*
* @param content The byte content to be MD5-encrypted.
* @return The MD5 encryption result.
*/
private static String md5Crypt(byte[] content) {
try {
if (content == null || content.length == 0) {
return null;
}
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(content);
String hex = new BigInteger(1, digest.digest()).toString(16).toUpperCase();
return StringUtils.leftPad(hex, 32, "0");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Not supported signature method MD5", e);
}
}
/**
* HMAC-SHA1 encryption
*
* @param accessKeySecret The SK.
* @param content The byte array to be HMAC-SHA1-encrypted.
* @return The encryption result.
*/
private static byte[] hmacSha1(String accessKeySecret, byte[] content) {
try {
Validate.notEmpty(accessKeySecret, "cannot be null or empty.");
Validate.notNull(content, "content");
Validate.isTrue(content.length != 0, "content cannot be empty.");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec(accessKeySecret.getBytes(StandardCharsets.UTF_8), "HmacSHA1"));
return mac.doFinal(content);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Not supported signature method HmacSHA1", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("Failed to calculate the signature", e);
}
}
private static String buildMessage(String... contents) {
StringJoiner joiner = new StringJoiner("\n");
for (String content : contents) {
if (StringUtils.isNotEmpty(content)) {
joiner.add(content);
} else {
joiner.add("");
}
}
return joiner.toString();
}
private static final BitSet URI_UNRESERVED_CHARACTERS = new BitSet();
private static final String[] PERCENT_ENCODED_STRINGS = new String[256];
static {
for (int i = 'a'; i <= 'z'; i++) {
URI_UNRESERVED_CHARACTERS.set(i);
}
for (int i = 'A'; i <= 'Z'; i++) {
URI_UNRESERVED_CHARACTERS.set(i);
}
for (int i = '0'; i <= '9'; i++) {
URI_UNRESERVED_CHARACTERS.set(i);
}
URI_UNRESERVED_CHARACTERS.set('-');
URI_UNRESERVED_CHARACTERS.set('.');
URI_UNRESERVED_CHARACTERS.set('_');
URI_UNRESERVED_CHARACTERS.set('~');
for (int i = 0; i < PERCENT_ENCODED_STRINGS.length; ++i) {
PERCENT_ENCODED_STRINGS[i] = String.format("%%%02X", i);
}
}
/**
* URLEncoder conforms to the rules specified in RFC3986. Unreserved URI characters include letters (A-Z and a-z), digits (0-9), hyphens (-), periods (.), underscores (_), and tildes (~).
* The algorithm works as follows: 1. Convert strings into UTF-8-encoded bytes. 2. Keep all unreserved URI characters unchanged. 3. Perform percent-encoding on all other bytes based on the rules specified in RFC3986. That is, add a percent sign (%) before two hexadecimal letters that represent the byte value. All letters are in uppercase.
*/
public static String encoder(String value) {
if (StringUtils.isEmpty(value)) {
return StringUtils.EMPTY;
}
StringBuilder builder = new StringBuilder();
for (byte b : value.getBytes(StandardCharsets.UTF_8)) {
if (URI_UNRESERVED_CHARACTERS.get(b & 0xFF)) {
builder.append((char) b);
} else {
builder.append(PERCENT_ENCODED_STRINGS[b & 0xFF]);
}
}
return builder.toString();
}
}