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 pair of OCP. Each AccessKey pair consists of an AccessKey ID (AK) and an AccessKey Secret (SK).
Description
Obtain the AccessKey pair.
Log on to the OCP console and click
Personal Settings in the upper-right corner. On the page that appears, select Create AccessKey Pair.Create a toSignString string.
Create a toSignString string based on the request information and additional information such as the algorithm and request time.
Generate the signature.
Perform a hash operation on the toSignString string to generate the signature by using the SK as the hash key.
Add the signature to the request.
Add the generated signature to the HTTP header of the request.
Procedure
Step 1: Obtain the AccessKey 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 theAccessKey section. For more information, see Configure personal information.After the AccessKey pair is created, keep it properly.
Step 2: Create a toSignString string
Concatenate the following strings separated with line breaks to create a toSignString string:
<HttpMethod>
<Md5Payload>
<ContentType>
<RequestTime>
<Host>
<OCP-headers>
<PathAndQueryParams>
The formula for creating a toSignString string is as follows:
toSignString = HttpMethod + "\n" + Md5Payload + "\n" + ContentType + "\n" + RequestDate + "\n" + Host + "\n" + OCP-Headers + "\n" + PathAndQueryParams
Components of a toSignString string are described as follows:
HttpMethod: the HTTP request method, such as GET, POST, PUT, and DELETE.
Md5Payload: the MD5 hash value of the request payload. If the request does not contain valid payload information, an empty string ("") is passed. If the request contains valid payload information, the MD5 hash value of the payload information (
md5(<payload>) is passed.ContentType: the type of the request, which is the value of
Content-Typein the headers part. Most requests processed by OCP are of theapplication/jsontype.RequestDateTime: the time when the request was initiated, which is obtained by reading the
Dateorx-ocp-datefield in the headers part. The value is in the RFC-1123 format, for example,Fri, 12 Apr 2024 07:15:32 GMT.Host : the domain name of the request. If an IP address and a port number are used, the domain name is passed in theIP address:port numberformat, for example,xxx.xxx.xxx.xxx:8080.OCP-Headers: the request headers that start with
x-ocp-. The signature algorithm will use such request headers, which are sorted in alphabetical order and separated with line breaks.HeaderName1+":"+<value>+"\n" HeaderName2+":"+<value>+"\n" ... HeaderNameN+":"+<value>+"\n"PathAndQueryParams: a string that combines the request path and request parameters, which are sorted in alphabetical order, for example,
?a=1&b=2. The concatenation format is as follows:path?urlEncode(<param1>)+"="+URLEncode(<value>)+"&"+urlEncode(<param2>)+"="+URLEncode(<value>)+...+"&"+urlEncode(<paramN>)+"="+URLEncode(<value>)
Examples
Here is a sample curl request:
curl --user <username>:<password> -H "x-ocp-origin:for-test" -H "Content-Type:application/json" -X GET "http://xxx.xxx.xxx.xxx:8080/api/v2/monitor/top?metrics=host_disk_total&labels=svr_ip:xxx.xxx.xxx.xxx&groupBy=app,svr_ip,device,mount_point&startTime=2024-04-15T14:29:55+08:00&endTime=2024-04-15T14:30:55+08:00&maxPoints=360"
- HttpMethod:
GET - Md5PayLoad: empty
- ContentType:
application/json - RequestDateTime: Assume that the request initiation time is
Mon, 15 Apr 2024 09:25:02 GMT. - Host:
xxx.xxx.xxx.xxx:8080 - OCP-headers:
x-ocp-origin:for-test - PathAndQueryParams:
Path:
/api/v2/monitor/topQueryParams:
metrics=host_disk_total&labels=svr_ip:xxx.xxx.xxx.xxx&groupBy=app,svr_ip,device,mount_point&startTime=2024-04-15T14:29:55+08:00&endTime=2024-04-15T14:30:55+08:00&maxPoints=360The concatenated string is as follows:
/api/v2/monitor/top?endTime=2024-04-15T14%3A30%3A55%2B08%3A00&groupBy=app%2Csvr_ip%2Cdevice%2Cmount_point&labels=svr_ip%3Axxx.xxx.xxx.xxx&maxPoints=360&metrics=host_disk_total&startTime=2024-04-15T14%3A29%3A55%2B08%3A00
The following toSignString string is created:
GET
application/json
Mon, 15 Apr 2024 09:25:02 GMT
xxx.xxx.xxx.xxx:8080
x-ocp-origin:for-test
/api/v2/monitor/top?endTime=2024-04-15T14%3A30%3A55%2B08%3A00&groupBy=app%2Csvr_ip%2Cdevice%2Cmount_point&labels=svr_ip%3Axxx.xxx.xxx.xxx&maxPoints=360&metrics=host_disk_total&startTime=2024-04-15T14%3A29%3A55%2B08%3A00
Step 3: Generate the signature
Use the following algorithm to generate the signature:
BASE64(HMAC-SHA1(<AccessKey Secret>, string-to-sign.getBytes("UTF-8")))
Step 4: Add the signature to the request
When you call OCP APIs, observe 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: the authorization information of the request. This field consists of a prefix and the signature algorithm, AK, and signature, for example,
OCP-ACCESS-KEY-HMACSHA1 {AK}:{Signature}.Prefix: At present, the fixed value
OCP-ACCESS-KEY-is used.Signature algorithm: At present, only
HMACSHA1is supported.HMACSHA1must be written in uppercase.AK: the AccessKey ID. Note that the signature algorithm and the AccessKey ID must be separated with a space.
Signature: the signature obtained in the previous step. For more information about the rules for generating a signature, see the Step 3: Generate the signature section of this topic.
Date: the time when the request was initiated. OCP uses this field to verify that the server receives the request within a reasonable period after the request was initiated, which cannot exceed 15 minutes. The value is in the RFC-1123 format, for example,
Tue, 17 Jan 2023 03:36:01 GMT.
When the OCP server receives the request, it parses the request to obtain and verify the signature and request information. If the verification succeeds, the OCP server continues to process the request. Otherwise, an error is returned.
Notice
The AccessKey pair belongs to a specific user and is granted the same permissions as the user.
Here is an example:
curl -H "Authorization: OCP-ACCESS-KEY-HMACSHA1 <AK>:<signed-string>" -H "Date:Fri, 12 Apr 2024 07:15:32 GMT" -H "x-ocp-origin:for-test" -H "Content-Type:application/json" -X GET "xxx.xxx.xxx.xxx:8080/api/v2/monitor/top?metrics=host_disk_total&labels=svr_ip:xxx.xxx.xxx.xxx&groupBy=app,svr_ip,device,mount_point&startTime=2024-04-15T14:29:55+08:00&endTime=2024-04-15T14:30:55+08:00&maxPoints=360"
Complete sample code
This section uses a sample AccessKey pair to describe each step in the procedure for generating a signature.
Assume that the following AccessKey pair is obtained:
AccessKey ID: gDCcIqbkJJINjXBn
AccessKey Secret: d75332c5eed8d440a84a35ac6248d397
Here is a sample request:
curl --user <username>:<password> -H "x-ocp-origin:for-test" -H "Content-Type:application/json" -X GET "http://xxx.xxx.xxx.xxx:8080/api/v2/monitor/top?metrics=host_disk_total&labels=svr_ip:xxx.xxx.xxx.xxx&groupBy=app,svr_ip,device,mount_point&startTime=2024-04-15T14:29:55+08:00&endTime=2024-04-15T14:30:55+08:00&maxPoints=360"
The following toSignString string is created:
GET
application/json
Mon, 15 Apr 2024 09:25:02 GMT
xxx.xxx.xxx.xxx:8080
x-ocp-origin:for-test
/api/v2/monitor/top?endTime=2024-04-15T14%3A30%3A55%2B08%3A00&groupBy=app%2Csvr_ip%2Cdevice%2Cmount_point&labels=svr_ip%3Axxx.xxx.xxx.xxx&maxPoints=360&metrics=host_disk_total&startTime=2024-04-15T14%3A29%3A55%2B08%3A00
The following algorithm is used to generate the signature:
BASE64(HMAC-SHA1("d75332c5eed8d440a84a35ac6248d397", "GET
application/json
Mon, 15 Apr 2024 09:25:02 GMT
xxx.xxx.xxx.xxx:8080
x-ocp-origin:for-test
/api/v2/monitor/top?endTime=2024-04-15T14%3A30%3A55%2B08%3A00&groupBy=app%2Csvr_ip%2Cdevice%2Cmount_point&labels=svr_ip%3Axxx.xxx.xxx.xxx&maxPoints=360&metrics=host_disk_total&startTime=2024-04-15T14%3A29%3A55%2B08%3A00".getBytes("UTF-8")))
The generated signature and the request that uses the signature are as follows:
Signature:
To11kg1EsB/dPWyDnnpuUzIUoQk=The request that uses the signature to call an operation:
curl -H "Authorization: OCP-ACCESS-KEY-HMACSHA1 VNnZUoOjLBrWjKPu:To11kg1EsB/dPWyDnnpuUzIUoQk=" -H "Date:Mon, 15 Apr 2024 07:49:49 GMT" -H "x-ocp-origin:for-test" -H "Content-Type:application/json" -X GET "xxx.xxx.xxx.xxx:8080/api/v2/monitor/top?metrics=host_disk_total&labels=svr_ip:xxx.xxx.xxx.xxx&groupBy=app,svr_ip,device,mount_point&startTime=2024-04-15T14:29:55+08:00&endTime=2024-04-15T14:30:55+08:00&maxPoints=360"
Java sample code for signing a request
package com.oceanbase.ocp.demo;
import org.junit.Assert;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.stream.Collectors;
public class OcpSignDemo {
public static void main(String[] args) throws Exception {
Map<String, String> params = new HashMap<>();
params.put("startTime", "2024-04-15T14:29:55+08:00");
params.put("endTime", "2024-04-15T14:30:55+08:00");
params.put("maxPoints", "360");
params.put("metrics", "host_disk_total");
params.put("labels", "svr_ip:127.0.0.1");
params.put("groupBy", "app,svr_ip,device,mount_point");
String path = "/api/v2/monitor/top";
Map<String, String> headers = new HashMap<>();
headers.put("x-ocp-origin", "for-test");
headers.put("Content-Type", "application/json");
String accessKeyId = "gDCcIqbkJJINjXBn";
String accessKeySecret = "d75332c5eed8d440a84a35ac6248d397";
String rfcDate = "Mon, 15 Apr 2024 09:25:02 GMT";
String sign = getSign("127.0.0.1:8080", path, "GET", headers, params, null, accessKeySecret, rfcDate);
Assert.assertEquals("To11kg1EsB/dPWyDnnpuUzIUoQk=", sign);
// headers need add to request.
System.out.println("Request Headers:");
System.out.println("Authorization: OCP-ACCESS-KEY-HMACSHA1" + accessKeyId + ":" + sign);
System.out.println("Date:" + rfcDate);
}
public static String getSign(String host, String path, String method, Map<String, String> headers, Map<String,
String> params, byte[] body, String accessKeySecret) throws Exception {
return getSign(host, path, method, headers, params, body, accessKeySecret, getRfcDate());
}
public static String getSign(String host, String path, String method, Map<String, String> headers, Map<String,
String> params, byte[] body, String accessKeySecret, String rfcDate) throws Exception {
StringJoiner strToSign = new StringJoiner("\n");
// Append method.
strToSign.add(method);
// Append payload.
if (body == null) {
strToSign.add("");
} else {
strToSign.add(md5(body));
}
// Append Content-type.
if (headers == null || !headers.containsKey("Content-Type")) {
strToSign.add("");
} else {
strToSign.add(headers.get("Content-Type"));
}
// Append request-time.
strToSign.add(rfcDate);
// Append host.
strToSign.add(host);
// Append ocp-headers.
if (headers == null || headers.keySet().stream().noneMatch(s -> s.startsWith("x-ocp-"))) {
strToSign.add("");
} else {
String ocpHeadersStr = headers.entrySet().stream()
.filter(e -> e.getKey().toLowerCase().startsWith("x-ocp"))
.sorted(Map.Entry.comparingByKey())
.map(e -> String.format("%s:%s", e.getKey(), String.join(",", e.getValue())))
.collect(Collectors.joining("\n"));
strToSign.add(ocpHeadersStr);
}
// Append path and params
if (params == null || params.isEmpty()) {
strToSign.add(path);
} else {
List<Map.Entry<String, String>> toSort = new ArrayList<>(params.entrySet());
toSort.sort(Map.Entry.comparingByKey());
StringJoiner joiner = new StringJoiner("&");
for (Map.Entry<String, String> e : toSort) {
String enc = StandardCharsets.UTF_8.displayName();
String format = String.format("%s=%s", URLEncoder.encode(e.getKey(), enc),
URLEncoder.encode(e.getValue(), enc));
joiner.add(format);
}
String paramsStr = joiner.toString();
strToSign.add(path + "?" + paramsStr);
}
return Base64.getEncoder().encodeToString(hmacSha1(accessKeySecret,
strToSign.toString().getBytes(StandardCharsets.UTF_8)));
}
private static String getRfcDate() {
return Instant.now().atOffset(ZoneOffset.UTC).format(DateTimeFormatter.RFC_1123_DATE_TIME);
}
private static String md5(byte[] bs) throws Exception {
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(bs);
String hex = new BigInteger(1, digest.digest()).toString(16).toUpperCase();
return new String(new char[32 - hex.length()]).replace("\0", "0") + hex;
}
private static byte[] hmacSha1(String accessKeySecret, byte[] content) {
try {
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);
}
}
}