
# API签名认证机制
如果以上的语言签名示例中没有对应的编程语言，请根据API签名认证机制，自主实现请求的签名。
#### 步骤一：构造规范请求
将待发送的请求内容按照与API网关后台约定的规则组装，确保客户端签名、API网关后台认证时使用的请求内容一致，从而完成身份校验。
HTTP请求规范伪代码如下：
```
CanonicalRequest =
      HTTPRequestMethod + '\n' + //HTTP请求方法
      CanonicalURI + '\n' + //规范URI 
      CanonicalQueryString + '\n' + //规范查询字符串
      CanonicalHeaders + '\n' + //规范消息头
      SignedHeaders + '\n' + //签名的消息头声明
      HexEncode(Hash(RequestPayload)) //计算RequestPayload的哈希值
```
- **HTTPRequestMethod**
  HTTP请求方法，如GET、PUT、POST等。以换行符结束。
  
- **CanonicalURI**
  规范URI，即请求资源路径，是URI的绝对路径部分的URI编码。以换行符结束。
  根据RFC 3986规范化URI路径，移除冗余和相对路径部分，路径中每个部分必须为URI编码。**计算签名时，URI必须以"/"结尾。发送请求时，可以不以"/"结尾。**
  
- **CanonicalQueryString**
  查询字符串，即查询参数。以换行符结束。如果没有查询参数，则为空字符串，即规范后的请求为**空行**。
  规范查询字符串需要满足以下内容：
  - 根据以下规则对每个参数名和值进行URI编码：
    - 请勿对RFC 3986定义的任何非预留字符进行URI编码，这些字符包括：A-Z、a-z、0-9、-、_、.和\~。
    
    - 使用%XY对所有非预留字符进行百分比编码，其中X和Y为十六进制字符（0-9和A-F）。例如，空格字符必须编码为%20，扩展UTF-8字符必须采用"%XY%ZA%BC"格式。
     
  
  - 对于每个参数，追加"*URI编码的参数名称* =*URI编码的参数值* "。如果没有参数值，则以空字符串代替，但不能省略"="。
    例如以下含有两个参数，其中第二个参数parm2的值为空。
    ```
    parm1=value1&parm2=
    ```
    
  
  - 按照字符代码以升序顺序对参数名进行排序。例如，以大写字母F开头的参数名排在以小写字母b开头的参数名之前。
  
  - 以排序后的第一个参数名开始，构造规范查询字符串。
   
- **CanonicalHeaders**
  规范消息头，即请求消息头列表。包括签名请求中的所有HTTP消息头列表。消息头必须包含X-Sdk-Date，用于校验签名时间，格式为ISO8601规范的UTC时间格式：YYYYMMDDTHHMMSSZ。以换行符结束。
  ![](https://support.huaweicloud.com/devg-apisign/public_sys-resources/caution_3.0-zh-cn.png)
  - 客户端须注意本地时间与时钟服务器的同步，避免请求消息头X-Sdk-Date的值出现较大误差。
  
  - API网关除了校验时间格式外，还会校验该时间值与网关收到请求的时间差，如果时间差超过15分钟，API网关将拒绝请求。
   
  CanonicalHeaders由多个请求消息头共同组成，CanonicalHeadersEntry0 + CanonicalHeadersEntry1 + ...，其中每个请求消息头（CanonicalHeadersEntry）的格式为Lowercase(HeaderName) + ':' + Trimall(HeaderValue) + '\\n'
  - Lowercase表示将所有字符转换为小写字母的函数。
  
  - Trimall表示删除值前后的多余空格的函数。
  
  - 最后一个请求消息头也会携带一个换行符。叠加规范中CanonicalHeaders自身携带的换行符，因此会出现一个空行。
  
  - 按照字符代码对消息头名称进行升序排序。
   
  例如原始消息头为：
  ```
  Host: service.region.example.com\n
  Content-Type: application/json;charset=utf8\n
  My-header1:    a   b   c  \n
  X-Sdk-Date:20190318T094751Z\n
  My-Header2:    "x   y   \n
  ```
  对消息头名称转小写，按消息头名称字符代码对消息头排序，将消息头的值去掉前导空格与尾随空格。最终得到规范消息头：
  ```
  content-type:application/json;charset=utf8\n
  host:service.region.example.com\n
  my-header1:a   b   c\n
  my-header2:"x   y\n
  x-sdk-date:20190318T094751Z\n
  ```
  
- **SignedHeaders**
  用于签名的请求消息头列表。通过添加此消息头，向API网关告知请求中哪些消息头是签名过程的一部分，以及在验证请求时API网关可以忽略哪些消息头。X-Sdk-date必须作为已签名的消息头。
  SignedHeaders = Lowercase(HeaderName0) + ';' + Lowercase(HeaderName1) + ";" + ...
  - 将已签名的消息头名称转换为小写形式，按照字符代码对消息头进行排序，并使用";"来分隔多个消息头。
  
  - Lowercase表示将所有字符转换为小写字母。
   
- **RequestPayload**
  请求消息体。消息体需要做两层转换：HexEncode(Hash(RequestPayload))，其中Hash表示生成消息摘要的函数，当前支持SHA-256算法。HexEncode表示以小写字母形式返回摘要的Base-16编码的函数。例如，HexEncode("m") 返回值为"6d"而不是"6D"。输入的每一个字节都表示为两个十六进制字符。
  **计算RequestPayload的哈希值时，对于"RequestPayload==null"的场景，直接使用空字符串""来计算。**
  
 
#### 步骤二：创建待签字符串
对HTTP请求进行规范并取得请求的哈希值后，将其与签名算法、签名时间一起组成待签名字符串。
创建待签名字符串伪代码如下：
```
StringToSign =
    Algorithm + \n +
    RequestDateTime + \n +
    HashedCanonicalRequest
```
- **Algorithm**
  签名算法。对于SHA-256算法，函数为SDK-HMAC-SHA256。
  
- **RequestDateTime**
  请求时间戳。与请求消息头X-Sdk-Date的值相同，格式为YYYYMMDDTHHMMSSZ。
  
- **HashedCanonicalRequest**
  对构造好的规范请求CanonicalRequest进行哈希处理，算法与对RequestPayload哈希处理的算法相同。经过哈希处理的规范请求必须以小写十六进制字符串形式表示。
  算法伪代码如下：
  ```
  Lowercase(HexEncode(Hash.SHA256(CanonicalRequest)))
  ```
  
 
#### 步骤三：计算签名
将SK（Secret Access Key）和创建的待签字符串作为加密哈希函数的输入，计算签名，将二进制值转换为十六进制表示形式。
伪代码如下：
```
signature = HexEncode(HMAC(Secret Access Key, string to sign))
```
- **HexEncode**
  HexEncode指转十六进制。
  
- **HMAC**
  HMAC指密钥相关的哈希运算。
  
- **Secret Access Key**
  Secret Access Key指签名密钥SK。
  
- **string to sign**
  **string to sign** 为[步骤二：创建待签字符串]中已创建的字符串。
  
 
#### 步骤四：添加签名信息到请求头
计算签名后，将签名信息添加到Authorization的HTTP消息头中，用于身份验证。
Authorization header创建伪代码如下：
```
Authorization: algorithm Access=Access key, SignedHeaders=SignedHeaders, Signature=signature
```
**需要注意的是算法与Access之间有空格但没有逗号，但是SignedHeaders与Signature之前需要使用逗号隔开。**
得到签名消息头后，将其增加到原始HTTP请求内容中，请求将被发送给API网关，由API网关完成身份认证。身份认证通过后，该请求才会发送给具体的云服务进行业务处理。
