---
title: "AK/SK 签名生成规则 - 云平台 OCP V4.3.5 | OceanBase 文档中心"
description: AK/SK 签名生成规则 概述 您将 HTTP 请求发送到 OCP 时，OCP 需要对您的请求进行签名计算并生成认证字符串，以便识别请求者身份、保护传输过程数据安全性并防止重复攻击。 系统将使用 OCP 的访问密钥来进行签名计算，该访问密钥包含访问密钥 ID（Access Key Id，简称 AK）和秘密访问密钥（S…
---
切换语言

- 中文站 - 简体中文
- International - English
- 日本站 - 日本語

文档反馈![](https://mdn.alipayobjects.com/huamei_22khvb/afts/img/A*lJTmRZ61jSUAAAAAAAAAAAAADiGDAQ/original) 云平台 OCPV 4.3.5

# AK/SK 签名生成规则

更新时间：2026-04-14 15:11:03

[编辑](https://github.com/oceanbase/ocp-doc/edit/V4.3.5/zh-CN/1900.reference-guide/200.api-reference/300.ak-sk-sign-roles.md)  

## 概述

您将 HTTP 请求发送到 OCP 时，OCP 需要对您的请求进行签名计算并生成认证字符串，以便识别请求者身份、保护传输过程数据安全性并防止重复攻击。

系统将使用 OCP 的访问密钥来进行签名计算，该访问密钥包含访问密钥 ID（Access Key Id，简称 AK）和秘密访问密钥（Secret Access Key，简称 SK）。

## 签名摘要

1. 获取 AK/SK。

   前往您的 OCP 管控台，单击右上角 **个人设置** ，在页面中选择 **一键创建 AccessKey**。
 2. 创建待签名的字符串。

   根据请求信息和额外信息（如算法、请求时间等）创建待签名字符串。
 3. 计算签名。

   使用签名密钥（Access Key Secret）对待签名字符串执行加密哈希操作来计算签名。使用签名密钥作为此操作的哈希密钥。
 4. 将签名添加至请求。

   在计算签名后，将其添加到请求的 HTTP 标头。

## 操作步骤

### 步骤一：获取 AK/SK

1. 登录 OCP 。
 2. 将光标悬停于右上角用户名上，在弹出的菜单栏中单击 **个人设置**。
 3. 进入 **个人设置** 页面，在 **AccessKey** 模块单击 **一键创建 AccessKey**，详情可参考 [设置个人信息](https://www.oceanbase.com/docs/common-ocp-1000000002381163)。

   创建成功后请妥善保存 AK/SK。

### 步骤二：创建待签名的字符串

串联以下由换行符分隔的字符串，创建待签名字符串，待签名字符串格式如下：

```shell
<HttpMethod>
<Md5Payload>
<ContentType>
<RequestTime>
<Host>
<OCP-headers>
<PathAndQueryParams>

```

待签名字符串计算公式如下：

```java
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 信息使用字母顺序排序，并使用换行符分隔。

  ```java
  HeaderName1+":"+<value>+"\n"
  HeaderName2+":"+<value>+"\n"
  ...
  HeaderNameN+":"+<value>+"\n"

  ```
 - **PathAndQueryParams**：请求路径和请求参数的组合，多个参数则按照字典序排列，如 `?a=1&b=2` ，具体拼接算法如下。

  ```java
  path?urlEncode(<param1>)+"="+URLEncode(<value>)+"&"+urlEncode(<param2>)+"="+URLEncode(<value>)+...+"&"+urlEncode(<paramN>)+"="+URLEncode(<value>)

  ```

#### 示例

curl 请求示例如下：

```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/top`
     - QueryParams：`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
      /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

      ```

最终待签名字符串为：

```java
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

```

### 步骤三：计算签名

签名算法如下：

```java
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 属于某个用户，具备的权限等同于此用户所享有的权限。

具体示例如下：

```java
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 如下：

```java
AccessKey ID：gDCcIqbkJJINjXBn
AccessKey Secret：d75332c5eed8d440a84a35ac6248d397

```

示例请求复用上文示例内容：

```java
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"

```

根据上文描述，待加密字符串为：

```

签名计算逻辑为：

```java
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")))

```

计算出的签名为：

- 签名：

  ```java
  To11kg1EsB/dPWyDnnpuUzIUoQk=

  ```
 - 使用 AK/SK 请求 API：

  ```java
  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 签名示例代码

```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);
        }
    }

}

```

 上一篇 下一篇 ![有帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*y6ocSqN8cqsAAAAAAAAAAAAAARQnAQ)![无帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*BG9IQJyLHF8AAAAAAAAAAAAAARQnAQ)![反馈](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*eTWdQKCRKHwAAAAAAAAAAAAAARQnAQ)[AI](https://www.oceanbase.com/obi) 咨询热线
