文档

V3版本请求体&签名机制

更新时间:

V3版本通过公共请求头设置接口必要的参数信息,在签名机制的实现上屏蔽了接口风格的差异,更标准、更简单。本文提供了详细的指南,用于帮助您了解和实施阿里云SDK V3版的请求结构和签名过程。您会了解到如何构造标准的HTTP请求,以及如何使用正确的签名算法来验证请求的身份,确保传输的数据的完整性和安全性。如果您想要自研阿里云的 OpenAPI SDK,您可以参看本文。

HTTP 请求结构

一个完整的阿里云 OpenAPI 请求,包含以下部分。

名称

是否必选

描述

示例值

协议

您可以查阅不同云产品的 API 参考文档进行配置。支持通过HTTPHTTPS协议进行请求通信。为了获得更高的安全性,推荐您使用HTTPS协议发送请求。取值范围为https://或者 http://

https://

服务地址

即 Endpoint。您可以查阅不同云产品的服务接入地址文档,查阅不同服务区域下的服务地址。

cs.aliyuncs.com

resource_URI_parameters

接口URL,包括接口路径和位置在 path、 query的接口请求参数。

/clusters/{cluster_id}/triggers

RequestHeader

请求头信息,通常包含API的版本、Host、Authorization等信息。后文将详细说明。

x-acs-action

RequestBody

定义在 body 中的业务请求参数,建议您在阿里云 OpenAPI 开发者门户进行试用。

cluster_id

HTTPMethod

请求使用的方法,ROA接口请求方法包括PUT、POST、GET、DELETE。

POST

RequestHeader(公共请求头)

一个完整的阿里云 OpenAPI 请求,包含以下部分。

名称

类型

是否必选

描述

示例值

x-acs-action

String

API的名称。您可以访问阿里云 OpenAPI 开发者门户,搜索您想调用的 OpenAPI

RunInstances

x-acs-version

String

API 版本。您可以访问阿里云 OpenAPI 开发者门户,查看您调用 OpenAPI 对应的 API 版本

2014-05-26

Authorization

String

非匿名请求必须

用于验证请求合法性的认证信息,格式为Authorization: SignatureAlgorithm Credential=AccessKeyId,SignedHeaders=SignedHeaders,Signature=Signature。其中SignatureAlgorithm为签名加密方式,为ACS3-HMAC-SHA256。

Credential 为用户的访问密钥ID。您可以在RAM 控制台查看您的 AccessKeyId。如需创建 AccessKey,请参见创建AccessKeySignedHeaders为请求头中包含的参与签名字段键名,【说明】:除Authorization外的所有公共请求头,只要存在必须被加入签名。

Signature为请求签名,取值参见签名机制。

ACS3-HMAC-SHA256 Credential=YourAccessKeyId,SignedHeaders=host;x-acs-action;x-acs-content-sha256;x-acs-date;x-acs-signature-nonce;x-acs-version,Signature=e521358f7776c97df52e6b2891a8bc73026794a071b50c3323388c4e0df64804

x-acs-signature-nonce

String

签名唯一随机数。用于防止网络重放攻击,建议您每一次请求都使用不同的随机数。

d410180a5abf7fe235dd9b74aca91fc0

x-acs-date

String

按照ISO 8601标准表示的UTC时间,格式为yyyy-MM-ddTHH:mm:ssZ,例如2018-01-01T12:00:00Z。值为请求发出前15分钟内的时间。

2023-10-26T09:01:01Z

host

String

即服务地址,参见HTTP 请求结构

ecs.cn-shanghai.aliyuncs.com

x-acs-content-sha256

String

请求正文Hash摘要后再base-16编码的结果,与HashedRequestPayload一致。

e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

x-acs-security-token

String

STS认证必传

为调用Assumerole接口返回值中SecurityToken的值。

接口请求构造

一个完整的接口请求构造如下:

HTTPMethod /resource_URI_parameters
RequestHeader
RequestBody

请求参数由公共请求头和API自定义参数组成。公共请求头中包含API版本号、身份验证等信息。

  • HTTPMethod :请求使用的方法,包括PUT、POST、GET、DELETE。详情请参看HTTP 请求结构

  • resource_URI_parameters:请求要调用的资源标识符,如/cluster。详情请参看HTTP 请求结构

  • RequestHeader:请求头信息,通常包含API的版本、Host、Authorization等信息。详情请参看HTTP 请求结构

  • RequestBody:在 body 中的业务请求参数。详情请参看HTTP 请求结构

请求RunInstances接口示例:

POST /?ImageId=win2019_1809_x64_dtc_zh-cn_40G_alibase_20230811.vhd&RegionId=cn-shanghai HTTP/1.1
Authorization: ACS3-HMAC-SHA256 Credential=YourAccessKeyId,SignedHeaders=host;x-acs-action;x-acs-content-sha256;x-acs-date;x-acs-signature-nonce;x-acs-version,Signature=e521358f7776c97df52e6b2891a8bc73026794a071b50c3323388c4e0df64804
x-acs-action: RunInstances
host: ecs.cn-shanghai.aliyuncs.com
x-acs-date: 2023-10-26T09:01:01Z
x-acs-version: 2014-05-26
x-acs-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-acs-signature-nonce: d410180a5abf7fe235dd9b74aca91fc0
user-agent: AlibabaCloud (Mac OS X; x86_64) Java/1.8.0_352-b08 tea-util/0.2.6 TeaDSL/1
accept: application/json

请求编码

请求及返回结果都使用UTF-8字符集进行编码。

接口返回结果

每次接口调用请求,无论成功与否,系统都会返回一个唯一识别码RequestId。调用API服务后返回数据采用统一格式。接口调用成功后,会返回接口的返回参数请求 ID,HTTP 状态码为 2xx(不显示在响应正文中)。响应正文示例如下:

{
    "RequestId": "4C467B38-3910-447D-87BC-AC049166F216"/* 返回结果数据 */
}

接口调用出错后,会返回请求ID、服务节点、错误码和错误信息,HTTP 状态码为 4xx 或者 5xx。示例如下:

{
    "code": "400", /* 错误码 */
    "message": "Cluster permission denied", /* 错误信息 */
    "requestId": "A026BC61-0523-5A6D-A5F3-314A3D92FD50", /* 请求 ID */
    "status": 400 /* 业务字段 */
}

接口调用出错后,您可以根据返回的RequestId,在阿里云OpenAPI开发者门户-诊断中排查。此外,您还可以查阅公共错误码以及API 错误中心。当您无法排查错误时,可以提交工单,并在工单中注明Host(服务地址,参见HTTP 请求结构)和RequestId

签名机制

为保证API的安全调用,在调用API时阿里云会对每个API请求通过签名(Signature)进行身份验证。无论使用HTTP还是HTTPS协议提交请求,都需要在请求中包含签名信息。本文指导您如何进行签名处理。

对于每一次HTTP或者HTTPS协议请求,阿里云会根据访问中的签名信息验证访问请求者身份。您在访问时签名信息时,请按照以下方法对请求进行签名处理:

步骤一:构造规范化请求

使用AK/SK方式进行签名与认证,首先需要规范请求内容,然后再进行签名。客户端与云服务API网关使用相同的请求规范,可以确保同一个HTTP请求的前后端得到相同的签名结果,从而完成身份校验。

构造规范化请求(CanonicalRequest)的伪代码如下:

CanonicalRequest =
  HTTPRequestMethod + '\n' +    //http方法名,全大写
  CanonicalURI + '\n' +         //规范化URI
  CanonicalQueryString + '\n' + //规范化查询字符串
  CanonicalHeaders + '\n' +     //规范化消息头
  SignedHeaders + '\n' +        //已签名消息头
  HashedRequestPayload			
  • 请求方法(HTTPRequestMethod)即大写的http方法名,如GET、POST。

  • 规范化URI(CanonicalURI)

    即URL的资源路径部分经过编码得到,资源路径部分指URL中host与查询字符串之间的部分,包含host之后的/但不包含查询字符串前的?。用户发起请求时的URI应使用规范化URI,编码方式使用UTF-8字符集按照RFC3986的规则对URI中的每一部分(即被/分割开的字符串)进行编码:

    • 字符A~Z、a~z、0~9以及字符-_.~不编码。

    • 其他字符编码成%XY的格式,其中XY是字符对应ASCII码的16进制。示例:半角双引号(")对应%22

    • 扩展的UTF-8字符,编码成%XY%ZA…的格式。

    • 空格( )编码成%20,而不是加号(+)。该编码方式与application/x-www-form-urlencodedMIME格式编码算法相似,但又有所不同。

      如果您使用的是Java标准库中的java.net.URLEncoder,可以先用标准库中percentEncode编码,随后将编码后的字符中加号(+)替换为%20、星号(*)替换为%2A%7E替换为波浪号(~),即可得到上述规则描述的编码字符串

    重要

    当资源路径为空时,使用正斜杠(/)作为CanonicalURI。

  • 规范化查询字符串(CanonicalQueryString)构造方法如下:

    1. 将查询字符串中的参数按照参数名的字符代码升序排列,具有重复名称的参数应按值进行排序。

    2. 使用UTF-8字符集按照RFC3986的规则对每个参数的参数名和参数值分别进行URI编码,具体规则与上一节中CanonicalURI的编码规则相同。

    3. 使用等号(=)连接编码后的请求参数名和参数值,对于没有值的参数使用空字符串。

    4. 将请求参数按编码后的参数名的字符代码顺序以升序排列,具有相同名称的参数按编码后的参数值进行排序。

    5. 按照步骤4中的顺序使用与号(&)连接编码后的请求参数。

    重要

    当请求的查询字符串为空时,使用空字符串作为规范化查询字符串,实际上就是空白行。

  • 规范化请求头(CanonicalizedHeaders)是非标准HTTP头部信息。是指请求中出现的以x-acs-为前缀的参数信息,构造方法如下:

    1. 将所有需要签名的消息头的名称转换为小写。

    2. 将消息头的值除去首尾空格。对于有多个值的消息头,将多个值分别除去首尾空格后按值升序排列,然后用逗号(,)连接。

    3. 将步骤1、2的结果以英文冒号(:)连接,并在尾部添加换行符,组成一个规范化消息头(CanonicalHeaderEntry)。

    4. 多个规范化消息头,按照消息头名称(小写)的字符代码顺序以升序排列后拼接在一起。

    5. 如果没有需要签名的消息头,使用空字符串作为规范化消息头列表。

    重要

    除Authorization外的所有公共请求头,只要存在必须被加入签名。

  • 已签名消息头列表(SignedHeaders)用于说明此次请求有哪些消息头参与了签名,与CanonicalHeaders中包含的消息头是一一对应的,构造方法如下:

    • 将CanonicalHeaders中包含的请求头的名称转为小写

    • 多个请求头名称(小写)按首字母升序排列并以英文分号(;)分隔,例如 content-type;host;x-acs-date

    • 伪代码如下:

      CanonicalHeaderEntry = Lowercase(HeaderName) + ':' + Trim(HeaderValue) + '\n'
      
      CanonicalHeaders = 
          CanonicalHeaderEntry0 + CanonicalHeaderEntry1 + ... + CanonicalHeaderEntryN
  • 使用哈希函数对请求正文(RequestPayload)转换的值(HashedRequestPayload),转换规则用伪代码可表示为 HashedRequestPayload = HexEncode(Hash(RequestPayload))

    • Hash表示消息摘要函数,目前支持SHA256算法,例如,当签名协议使用ACS3-HMAC-SHA256时,应使用SHA256作为Hash函数。

    • HexEncode表示以小写的十六进制的形式返回摘要的编码函数(即Base16编码)

    重要

    当请求正文为空时,比如GET请求,RequestPayload固定为空字符串。所有阿里云OpenAPI的请求参数都定义在query,所以该参数可以默认为空字符串。

    表1:签名协议与签名算法、摘要函数的对应关系

    签名协议(SignatureAlgorithm)

    处理RequestPayload以及CanonicalRequest时使用的摘要函数(Hash)

    计算签名时实际使用的签名算法

    (SignatureMethod)

    ACS3-HMAC-SHA256

    SHA256

    HMAC-SHA256

步骤二:构造待签名字符串

按照以下伪代码构造待签名字符串(stringToSign):

StringToSign =
    SignatureAlgorithm + '\n' +
    HashedCanonicalRequest
  • SignatureAlgorithm

    签名协议,目前支持ACS3-HMAC-SHA256,不再支持基于MD5或SHA1的算法。

  • HashedCanonicalRequest

    规范化请求摘要串,计算方法伪代码如下:

    HashedCanonicalRequest = HexEncode(Hash(CanonicalRequest))
  1. 使用哈希函数(Hash)对步骤一中得到的规范化请求(CanonicalRequest)进行摘要处理,具体使用的Hash函数取决于签名协议(SignatureAlgorithm),参见表1,例如,当签名协议为ACS3-HMAC-SHA256时,应使用SHA256作为Hash函数。

  2. 将上一步得到的摘要结果以小写的十六进制形式编码。

步骤三:计算签名

按照以下伪代码计算签名值(Signature)。

Signature = HexEncode(SignatureMethod(Secret, StringToSign))
  • StringToSign:步骤二中构造的待签名字符串,UTF-8编码。

  • SignatureMethod:签名算法,具体使用的算法取决于签名协议(SignatureAlgorithm),其对应关系如表1。

  • Secret:用户的签名密钥,为二进制数据。

  • HexEncode:以小写的十六进制的形式返回摘要的编码函数(即Base16编码)。

步骤四:将签名添加到请求中

计算完签名后,构造Authorization请求头,格式为:Authorization:<SignatureAlgorithm> Credential=<AccessKeyId>,SignedHeaders=<SignedHeaders>,Signature=<Signature>示例如下:

Authorization:ACS3-HMAC-SHA256 Credential=YourAccessKeyId,SignedHeaders=content-type;host;x-acs-timestamp,Signature=6b595d672d79c15b18edb4ccfba6789a24a6f2b82c400e03162d9279b08555d7

签名示例

固定参数示例

本示例以调用云服务器 ECS RunInstances为例。假设您获得了AccessKeyID 为 YourAccessKeyId

, AccessKeySecret 为 YourAccessKeySecret,x-acs-signature-nonce 为 3156853299f313e23d1673dc12e1703d,接口请求时间为 2023-10-26T10:22:32Z。签名流程如下:

  1. 构造规范化请求。

POST
/
ImageId=win2019_1809_x64_dtc_zh-cn_40G_alibase_20230811.vhd&RegionId=cn-shanghai
host:ecs.cn-shanghai.aliyuncs.com
x-acs-action:RunInstances
x-acs-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-acs-date:2023-10-26T10:22:32Z
x-acs-signature-nonce:3156853299f313e23d1673dc12e1703d
x-acs-version:2014-05-26

host;x-acs-action;x-acs-content-sha256;x-acs-date;x-acs-signature-nonce;x-acs-version
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
  1. 构造待签名字符串。

ACS3-HMAC-SHA256
7ea06492da5221eba5297e897ce16e55f964061054b7695beedaac1145b1e259
  1. 计算签名。

06563a9e1b43f5dfe96b81484da74bceab24a1d853912eee15083a6f0f3283c0
  1. 将签名添加到请求中。

POST /?ImageId=win2019_1809_x64_dtc_zh-cn_40G_alibase_20230811.vhd&RegionId=cn-shanghai HTTP/1.1
Authorization: ACS3-HMAC-SHA256 Credential=YourAccessKeyId,SignedHeaders=host;x-acs-action;x-acs-content-sha256;x-acs-date;x-acs-signature-nonce;x-acs-version,Signature=06563a9e1b43f5dfe96b81484da74bceab24a1d853912eee15083a6f0f3283c0
x-acs-action: RunInstances
host: ecs.cn-shanghai.aliyuncs.com
x-acs-date: 2023-10-26T09:01:01Z
x-acs-version: 2014-05-26
x-acs-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-acs-signature-nonce: d410180a5abf7fe235dd9b74aca91fc0
user-agent: AlibabaCloud (Mac OS X; x86_64) Java/1.8.0_352-b08 tea-util/0.2.6 TeaDSL/1
accept: application/json
说明

为了让您更清晰地理解上述签名机制,下面以主流编程语言为例,将签名机制完整实现。代码只是让您更好地理解签名机制,存在不通用性,实际开发请尽量使用 SDK。

Java示例

使用Java示例,需要您添加Maven依赖。

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
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.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.SimpleTimeZone;

public class SignatureDemo {
    /**
     * 阿里云访问密钥ID和密钥 secret的定义。
     * 这里通过环境变量获取Access Key ID和Access Key Secret,
     */
    private final static String ACCESS_KEY_ID = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
    private final static String ACCESS_KEY_SECRET = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");

    /**
     * 日期格式化工具,用于将日期时间字符串格式化为"yyyy-MM-dd'T'HH:mm:ss'Z'"的格式。
     * 这种格式是阿里云API在处理日期时间时常用的格式。
     */
    private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");

    /**
     * 使用HmacSHA256算法生成签名(MAC)。
     *
     * @param key 密钥,用于生成MAC的密钥,必须保密。
     * @param msg 需要进行MAC认证的待签名内容。
     * @return 返回使用HmacSHA256算法计算出的签名。
     * @throws Exception 如果初始化MAC或计算MAC过程中遇到错误,则抛出异常。
     */
    public static byte[] hmac256(byte[] key, String msg) throws Exception {
        // 实例化HmacSHA256
        Mac mac = Mac.getInstance("HmacSHA256");
        // 创建密钥规范,用于初始化MAC生成器
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
        // 初始化MAC生成器
        mac.init(secretKeySpec);
        // 计算签名结果并返回
        return mac.doFinal(msg.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 使用SHA-256算法计算字符串的哈希值并以十六进制字符串形式返回。
     *
     * @param str 需要进行SHA-256哈希计算的字符串。
     * @return 计算结果为小写十六进制字符串。
     * @throws Exception 如果在获取SHA-256摘要实例时发生错误。
     */
    public static String sha256Hex(String str) throws Exception {
        // 获取SHA-256消息摘要实例
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        // 计算字符串str的SHA-256哈希值
        byte[] d = md.digest(str.getBytes(StandardCharsets.UTF_8));
        // 将哈希值转换为小写十六进制字符串并返回
        return DatatypeConverter.printHexBinary(d).toLowerCase();
    }

    /**
     * 主函数示例,演示如何使用阿里云的SDK进行API请求。
     * 通过给定的参数构建请求,并进行签名认证。
     *
     * @param args args
     * @throws Exception 可能抛出的异常包括但不限于网络错误、格式化日期错误等。
     */
    public static void main(String[] args) throws Exception {
        // 初始化基本请求参数
        String host = "ecs.cn-beijing.aliyuncs.com";  // endpoint
        String regionId = "cn-beijing";  // 地域ID
        String action = "DescribeInstances";  // API名称
        String version = "2014-05-26"; // API版本号
        String algorithm = "ACS3-HMAC-SHA256";  // 签名协议
        SDF.setTimeZone(new SimpleTimeZone(0, "GMT")); // 设置日期格式化时区为GMT
        String xAcsDate = SDF.format(new Date()); // 获取当前时间的GMT字符串
        System.out.println("xAcsDate=========>" + xAcsDate);
        String signatureNonce = java.util.UUID.randomUUID().toString(); // 生成唯一的签名随机数
        System.out.println("signatureNonce=========>" + signatureNonce);

        // 步骤 1:拼接规范请求
        String httpRequestMethod = "GET";
        // 请求路径,当资源路径为空时,使用正斜杠(/)作为CanonicalURI
        String canonicalUri = "/";
        // 请求参数,当请求的查询字符串为空时,使用空字符串作为规范化查询字符串,实际上就是空白行
        String canonicalQueryString = "RegionId=" + regionId;
        // 请求体,当请求正文为空时,比如GET请求,RequestPayload固定为空字符串
        String requestPayload = "";
        // 计算请求体的哈希值
        String hashedRequestPayload = sha256Hex(requestPayload);
        // 构造请求头,多个规范化消息头,按照消息头名称(小写)的字符代码顺序以升序排列后拼接在一起
        String canonicalHeaders = "host:" + host.trim() + "\n"
                + "x-acs-action:" + action.trim() + "\n"
                + "x-acs-content-sha256:" + hashedRequestPayload.trim() + "\n"
                + "x-acs-date:" + xAcsDate.trim() + "\n"
                + "x-acs-signature-nonce:" + signatureNonce.trim() + "\n"
                + "x-acs-version:" + version.trim() + "\n";
        // 已签名消息头列表,多个请求头名称(小写)按首字母升序排列并以英文分号(;)分隔
        String signedHeaders = "host;x-acs-action;x-acs-content-sha256;x-acs-date;x-acs-signature-nonce;x-acs-version";
        String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
                + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;
        System.out.println("canonicalRequest=========>" + canonicalRequest);

        // 步骤 2:拼接待签名字符串
        String hashedCanonicalRequest = sha256Hex(canonicalRequest); // 计算规范化请求的哈希值
        String stringToSign = algorithm + "\n" + hashedCanonicalRequest;
        System.out.println("stringToSign=========>" + stringToSign);

        // 步骤 3:计算签名
        String signature = DatatypeConverter.printHexBinary(hmac256(ACCESS_KEY_SECRET.getBytes(StandardCharsets.UTF_8), stringToSign)).toLowerCase();
        System.out.println("signature=========>" + signature);

        // 步骤 4:拼接 Authorization
        String authorization = algorithm + " " + "Credential=" + ACCESS_KEY_ID + ",SignedHeaders=" + signedHeaders + ",Signature=" + signature;
        System.out.println("authorization=========>" + authorization);


        // 通过HttpClient发送请求
        CloseableHttpClient httpClient = HttpClients.createDefault();
        String url = "https://ecs.cn-beijing.aliyuncs.com";
        URIBuilder uriBuilder = new URIBuilder(url);
        // 添加请求参数
        uriBuilder.addParameter("RegionId", regionId);
        HttpGet httpGet = new HttpGet(uriBuilder.build());
        // 添加请求头
        httpGet.addHeader("Authorization", authorization);
        httpGet.addHeader("host", host);
        httpGet.addHeader("x-acs-action", action);
        httpGet.addHeader("x-acs-content-sha256", hashedRequestPayload);
        httpGet.addHeader("x-acs-date", xAcsDate);
        httpGet.addHeader("x-acs-version", version);
        httpGet.addHeader("x-acs-signature-nonce", signatureNonce);
        // 发送请求
        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            String result = EntityUtils.toString(response.getEntity(), "UTF-8");
            System.out.println(result);
        } catch (IOException e) {
            // 异常处理
            e.printStackTrace();
        }
    }
}

Python示例

需要您手动安装pytz和requests,请根据您所使用的Python版本在终端执行以下命令。

Python3

pip3 install pytz
pip3 install requests

Python2

pip install pytz
pip install requests
import binascii
import hashlib
import hmac
import os
import uuid
from datetime import datetime
from urllib.parse import urlencode

import pytz
import requests


def hmac256(key: bytes, msg: str) -> bytes:
    # 使用HMAC-SHA256算法计算签名结果
    mac = hmac.new(key, msg.encode('utf-8'), hashlib.sha256)
    return mac.digest()


def sha256_hex(str: str) -> str:
    # 计算字符串的SHA-256哈希值
    hash_value = hashlib.sha256(str.encode('utf-8')).digest()
    # 将哈希值转换为小写十六进制字符串并返回
    return binascii.hexlify(hash_value).decode().lower()


if __name__ == '__main__':
    access_key_id = os.environ['ALIBABA_CLOUD_ACCESS_KEY_ID']
    access_key_secret = os.environ['ALIBABA_CLOUD_ACCESS_KEY_SECRET']
    host = 'ecs.cn-beijing.aliyuncs.com'
    region_id = 'cn-beijing'
    action = 'DescribeInstances'
    version = '2014-05-26'
    algorithm = 'ACS3-HMAC-SHA256'
    now = datetime.now()
    # 将当前时间转换为格林尼治标准时间(GMT)
    gmt = now.astimezone(pytz.utc)
    # 格式化为符合ISO 8601标准的字符串,包含'Z'表示UTC/GMT
    x_acs_date = gmt.strftime("%Y-%m-%dT%H:%M:%SZ")
    signature_nonce = str(uuid.uuid4())

    # 步骤 1:拼接规范请求
    http_request_method = "GET"
    canonical_uri = "/"
    canonical_query_string = "RegionId=" + region_id
    request_payload = ""
    hashed_request_payload = sha256_hex(request_payload)
    canonical_headers = (
            "host:" + host.strip() + "\n"
            + "x-acs-action:" + action.strip() + "\n"
            + "x-acs-content-sha256:" + hashed_request_payload.strip() + "\n"
            + "x-acs-date:" + x_acs_date.strip() + "\n"
            + "x-acs-signature-nonce:" + signature_nonce.strip() + "\n"
            + "x-acs-version:" + version.strip() + "\n"
    )
    signed_headers = "host;x-acs-action;x-acs-content-sha256;x-acs-date;x-acs-signature-nonce;x-acs-version"
    canonical_request = (http_request_method + "\n" +
                         canonical_uri + "\n" +
                         canonical_query_string + "\n" +
                         canonical_headers + "\n" +
                         signed_headers + "\n" +
                         hashed_request_payload)
    print(canonical_request)
    # 步骤 2:拼接待签名字符串
    hashed_canonical_request = sha256_hex(canonical_request)
    string_to_sign = algorithm + "\n" + hashed_canonical_request
    print(string_to_sign)

    # 步骤 3:计算签名
    signature = hmac256(access_key_secret.encode('utf-8'), string_to_sign).hex().lower()
    print(signature)

    # 步骤 4:拼接 Authorization
    authorization = f"{algorithm} Credential={access_key_id},SignedHeaders={signed_headers},Signature={signature}"
    print(authorization)

    # 通过requests发送请求
    url = "https://ecs.cn-beijing.aliyuncs.com"
    # 构建请求URL
    params = {"RegionId": region_id}
    encoded_params = urlencode(params)
    full_url = f"{url}?{encoded_params}"
    # 构建请求头
    headers = {
        "Authorization": authorization,
        "host": host,
        "x-acs-action": action,
        "x-acs-content-sha256": hashed_request_payload,
        "x-acs-date": x_acs_date,
        "x-acs-version": version,
        "x-acs-signature-nonce": signature_nonce,
    }
    # 发送GET请求并处理响应
    try:
        response = requests.get(full_url, headers=headers)
        response.raise_for_status()  # 如果状态码不是2xx,抛出HTTPError异常
        result = response.text
        print(result)
    except requests.exceptions.RequestException as e:
        # 异常处理
        print(f"An error occurred: {e}")

Go示例

需要您手动执行以下命令:

go get github.com/google/uuid
package main

import (
    "bytes"
    "context"
    "crypto/hmac"
    "crypto/sha256"
    "crypto/tls"
    "encoding/hex"
    "fmt"
    "github.com/google/uuid"
    "io"
    "net/http"
    "os"
    "strings"
    "time"
)

func hmac256(key []byte, toSignString string) ([]byte, error) {
    // 实例化HMAC-SHA256哈希
    h := hmac.New(sha256.New, key)

    // 写入待签名的字符串
    _, err := h.Write([]byte(toSignString))
    if err != nil {
       return nil, err
    }

    // 计算签名并返回
    return h.Sum(nil), nil
}

func sha256Hex(str string) string {
    // 实例化SHA-256哈希函数
    hash := sha256.New()

    // 将字符串写入哈希函数
    _, _ = hash.Write([]byte(str))

    // 计算SHA-256哈希值并转换为小写的十六进制字符串
    hexString := hex.EncodeToString(hash.Sum(nil))

    return hexString
}

func main() {
    accessKeyId := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
    accessKeySecret := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
    host := "ecs.cn-beijing.aliyuncs.com" // endpoint
    regionId := "cn-beijing"              // 地域ID
    action := "DescribeInstances"         // API名称
    version := "2014-05-26"               // API版本号
    algorithm := "ACS3-HMAC-SHA256"       // 签名协议
    // 创建一个固定在UTC时区的Location对象,相当于Java中的GMT
    gmt := time.FixedZone("GMT", 0)
    // 获取当前时间,并将其设置到UTC时区
    now := time.Now().In(gmt)
    // 格式化当前时间
    xAcsDate := now.Format("2006-01-02T15:04:05Z")
    signatureNonce := uuid.New().String()

    // 步骤 1:拼接规范请求串
    httpRequestMethod := "GET"
    canonicalUri := "/"
    canonicalQueryString := "RegionId=" + regionId
    requestPayload := ""
    hashedRequestPayload := sha256Hex(requestPayload)
    canonicalHeaders := "host:" + strings.Trim(host, " ") + "\n" +
       "x-acs-action:" + strings.Trim(action, " ") + "\n" +
       "x-acs-content-sha256:" + strings.Trim(hashedRequestPayload, " ") + "\n" +
       "x-acs-date:" + strings.Trim(xAcsDate, " ") + "\n" +
       "x-acs-signature-nonce:" + strings.Trim(signatureNonce, " ") + "\n" +
       "x-acs-version:" + strings.Trim(version, " ") + "\n"
    signedHeaders := "host;x-acs-action;x-acs-content-sha256;x-acs-date;x-acs-signature-nonce;x-acs-version"
    canonicalRequest := httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n" + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload

    // 步骤 2:拼接待签名字符串
    hashedCanonicalRequest := sha256Hex(canonicalRequest)
    stringToSign := algorithm + "\n" + hashedCanonicalRequest

    // 步骤 3:计算签名
    byteData, err := hmac256([]byte(accessKeySecret), stringToSign)
    if err != nil {
       fmt.Println(err)
    }
    signature := strings.ToLower(hex.EncodeToString(byteData))

    // 步骤 4:拼接 Authorization
    authorization := algorithm + " " + "Credential=" + accessKeyId + ",SignedHeaders=" + signedHeaders + ",Signature=" + signature

    // 构建请求URL
    url := fmt.Sprintf("https://%s/?RegionId=%s", host, regionId)

    // 创建HTTP客户端
    tr := &http.Transport{
       TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 如果需要跳过SSL验证,请保留此行;否则请删除或设置为false
    }
    client := &http.Client{Transport: tr}

    // 构建请求
    req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
    if err != nil {
       panic(err)
    }

    // 添加请求头
    req.Header.Set("Authorization", authorization)
    req.Header.Set("Host", host)
    req.Header.Set("x-acs-action", action)
    req.Header.Set("x-acs-content-sha256", hashedRequestPayload)
    req.Header.Set("x-acs-date", xAcsDate)
    req.Header.Set("x-acs-version", version)
    req.Header.Set("x-acs-signature-nonce", signatureNonce)

    // 发送请求并处理响应
    resp, err := client.Do(req)
    if err != nil {
       panic(err)
    }
    defer func(Body io.ReadCloser) {
       err := Body.Close()
       if err != nil {

       }
    }(resp.Body)
    var respBuffer bytes.Buffer
    _, err = io.Copy(&respBuffer, resp.Body)
    if err != nil {
       panic(err)
    }
    respBytes := respBuffer.Bytes()
    fmt.Println(string(respBytes))
}
  • 本页导读 (1)
文档反馈