首批通过分布式安全可靠测评,为关键业务系统打造
AK/SK 签名生成规则
更新时间:2026-04-14 15:11:03
概述
您将 HTTP 请求发送到 OCP 时,OCP 需要对您的请求进行签名计算并生成认证字符串,以便识别请求者身份、保护传输过程数据安全性并防止重复攻击。
系统将使用 OCP 的访问密钥来进行签名计算,该访问密钥包含访问密钥 ID(Access Key Id,简称 AK)和秘密访问密钥(Secret Access Key,简称 SK)。
签名摘要
获取 AK/SK。
前往您的 OCP 管控台,单击右上角 个人设置 ,在页面中选择 一键创建 AccessKey。
创建待签名的字符串。
根据请求信息和额外信息(如算法、请求时间等)创建待签名字符串。
计算签名。
使用签名密钥(Access Key Secret)对待签名字符串执行加密哈希操作来计算签名。使用签名密钥作为此操作的哈希密钥。
将签名添加至请求。
在计算签名后,将其添加到请求的 HTTP 标头。
操作步骤
步骤一:获取 AK/SK
登录 OCP 。
将光标悬停于右上角用户名上,在弹出的菜单栏中单击 个人设置。
进入 个人设置 页面,在 AccessKey 模块单击 一键创建 AccessKey,详情可参考 设置个人信息。
创建成功后请妥善保存 AK/SK。
步骤二:创建待签名的字符串
串联以下由换行符分隔的字符串,创建待签名字符串,待签名字符串格式如下:
<HttpMethod>
<Md5Payload>
<ContentType>
<RequestTime>
<Host>
<OCP-headers>
<PathAndQueryParams>
待签名字符串计算公式如下:
toSignString = HttpMethod + "\n" + Md5Payload + "\n" + ContentType + "\n" + RequestDate + "\n" + Host + "\n" + OCP-Headers + "\n" + PathAndQueryParams
从公式可以看出待签名字符串由 HttpMethod、Md5Payload、ContentType、RequestDateTime、Host、OCP-Headers 和 PathAndQueryParams 7 部分参数组成,它们的具体含义及计算方式如下:
HttpMethod:HTTP 请求方法,如 GET、POST、PUT、DELETE 等。
Md5Payload:请求 payload 的 md5 加密后的信息。如果请求中不含有有效负载信息,则传递空字符("");如果含有有效负载,则传递 md5 后的负载信息(
md5(<payload>))。ContentType:请求类型,headers 信息中的 content-type 内容,对于 OCP 来说大部分请求类型都是
application/json。RequestDateTime:请求的时间,此信息会从请求 headers 中读取 Date 或 x-ocp-date 内容,时间格式遵循 RFC-1123 格式,如
Fri, 12 Apr 2024 07:15:32 GMT。Host:请求的域名,如果是
IP+Port的话则传递 IP:Port,如xxx.xxx.xxx.xxx:8080。OCP-Headers:请求 headers 中以
x-ocp-开头的请求 header,签名算法会将此类请求 header 计算在内,多个 Headers 信息使用字母顺序排序,并使用换行符分隔。HeaderName1+":"+<value>+"\n" HeaderName2+":"+<value>+"\n" ... HeaderNameN+":"+<value>+"\n"PathAndQueryParams:请求路径和请求参数的组合,多个参数则按照字典序排列,如
?a=1&b=2,具体拼接算法如下。path?urlEncode(<param1>)+"="+URLEncode(<value>)+"&"+urlEncode(<param2>)+"="+URLEncode(<value>)+...+"&"+urlEncode(<paramN>)+"="+URLEncode(<value>)
示例
curl 请求示例如下:
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:空
- ContentType:
application/json - RequestDateTime:假设是
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=360拼接后的字符串为:
/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
最终待签名字符串为:
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
步骤三:计算签名
签名算法如下:
BASE64(HMAC-SHA1(<AccessKey Secret>, string-to-sign.getBytes("UTF-8")))
步骤四:将签名添加至请求
在调用 OCP API 时,您需要遵从以下规则:
您将 HTTP 请求发送至 OCP 时,需保证 HTTP Header 中包含 Authorization 字段及 Date 字段,以便 OCP 服务端对请求进行认证。
Authorization:请求的授权信息,包括前缀、签名算法、AK、签名信息,格式示例为
OCP-ACCESS-KEY-HMACSHA1 {AK}:{Signature}。前缀:目前固定为
OCP-ACCESS-KEY-。签名算法:目前仅支持
HMACSHA1,需注意此处HMACSHA1为大写形式。AK:密钥 ID,请注意签名算法与 AK 之间存在空格。
Signature:计算得到的签名,具体计算规则请参考 步骤三:计算签名。
Date:请求发起时间。用来校验当前请求是否在合理的时间范围(距离服务器接收到的请求时间偏差不能超过 15 分钟)内,时间格式遵循 RFC-1123 格式,如
Tue, 17 Jan 2023 03:36:01 GMT。
OCP Server 端在接受到您的请求后,会解析出签名信息及请求信息,并加以校验。如果校验通过,则继续处理请求,否则将报错。
注意
AK/SK 属于某个用户,具备的权限等同于此用户所享有的权限。
具体示例如下:
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"
完整示例
上文介绍了拼接待加密字符串、签名算法、及签名消息体的生成。下面将通过示例演示 AK/SK 签名生成的每一个步骤。
假设当前获取到的 AK/SK 如下:
AccessKey ID:gDCcIqbkJJINjXBn
AccessKey Secret:d75332c5eed8d440a84a35ac6248d397
示例请求复用上文示例内容:
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"
根据上文描述,待加密字符串为:
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
签名计算逻辑为:
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")))
计算出的签名为:
签名:
To11kg1EsB/dPWyDnnpuUzIUoQk=使用 AK/SK 请求 API:
curl -H "Authorization: OCP-ACCESS-KEY-HMACSHA1 gDCcIqbkJJINjXBn: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 签名示例代码
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);
}
}
}