
# Header中携带签名
#### 功能介绍
OBS的所有API接口都可以通过在header中携带签名方式来进行身份认证，也是最常用的身份认证方式。如[图1]所示，在Header中携带签名是指将通过HTTP消息中Authorization header头域携带签名信息。
图1Header中携带签名示例   
![](https://support.huaweicloud.com/api-obs/figure/zh-cn_image_0000001990185765.png "点击放大")
Authorization header头域的格式为：
```
Authorization: OBS AccessKeyID:Signature
```
Authorization头域的值，由字符串"OBS"、用户访问密钥ID（AK）和签名Signature三部分组成。字符串"OBS"与AK之间用空格分隔，AK与签名之间用英文冒号分隔。
- 获取访问密钥ID（AK）请参见：[管理访问密钥](https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html)。
- 计算签名Signature的3种方式如[表1]所示：
   表1计算Header签名的三种方式 
  | **计算方式** | **描述**                                               | **更多参考**                                                            |
  |:---|:---|:---|
  | SDK      | OBS所有语言的SDK都已实现签名，无需手动进行签名计算。推荐您直接使用SDK进行接口调用，更方便快捷。 | [使用SDK计算签名]    |
  | 可视化签名工具  | OBS提供可视化签名计算工具，帮助您轻松完成签名计算。                          | [使用可视化签名工具计算签名] |
  | 手动编码计算   | 按照签名算法手动编码计算签名。                                      | [手动编码计算签名]      |
     
  
 
 #### 使用SDK计算签名
OBS SDK内部已实现签名，因此您在使用SDK访问OBS时，无需关注签名问题。各语言SDK实现签名计算的源码如[表2]所示：
 表2OBS SDK Header携带签名的实现 
| SDK        | 签名实现源文件                                                                                                                                                                                                   |
|:---|:---|
| Java       | [RestStorageService.java](https://github.com/huaweicloud/huaweicloud-sdk-java-obs/blob/5af1b31a130417b28bfab1579e5d7211e3f84dae/app/src/main/java/com/obs/services/internal/RestStorageService.java#L835) |
| Python     | [auth.py](https://github.com/huaweicloud/huaweicloud-sdk-python-obs/blob/master/src/obs/auth.py#L32)                                                                                                      |
| Go         | [auth.go](https://github.com/huaweicloud/huaweicloud-sdk-go-obs/blob/master/obs/auth.go#L144)                                                                                                             |
| C          | [request.c](https://github.com/huaweicloud/huaweicloud-sdk-c-obs/blob/5df228a0c83187ae95a80cca759baacb9325583c/source/eSDK_OBS_API/eSDK_OBS_API_C++/src/request.c#L819)                                   |
| Node.Js    | [utils.js](https://github.com/huaweicloud/huaweicloud-sdk-nodejs-obs/blob/master/lib/utils.js#L1238)                                                                                                      |
| Browser.Js | [utils.js](https://github.com/huaweicloud/huaweicloud-sdk-browserjs-obs/blob/master/src/utils.js#L1898)                                                                                                   |
| PHP        | [DefaultSignature.php](https://github.com/huaweicloud/huaweicloud-sdk-php-obs/blob/51272ef0c4415607f0f68460ae7706be330b8a82/Obs/Internal/Signature/DefaultSignature.php#L33)                              |
| .NET       | [Signer.cs](https://github.com/huaweicloud/huaweicloud-sdk-dotnet-obs/blob/master/Internal/Auth/Signer.cs)                                                                                                |
   
 #### 使用可视化签名工具计算签名
OBS提供可视化签名计算工具，帮助您高效计算Header中携带的签名，工具地址：[可视化签名计算工具](https://obs-community.obs.cn-north-1.myhuaweicloud.com/sign/header_signature.html)，如何使用工具详见[签名计算工具](https://support.huaweicloud.com/api-obs/obs_04_0171.html)。
 #### 手动编码计算签名
Header中携带签名的计算公式如下：
```
Signature = Base64( HMAC-SHA1( Your_SK, UTF-8-Encoding-Of( StringToSign ) ) )
```
OBS API签名的计算过程如下图所示：
图2计算Header中携带的签名   
![](https://support.huaweicloud.com/api-obs/figure/zh-cn_image_0000001995015484.png "点击放大")
1. 构造请求字符串(StringToSign)。StringToSign构成和参数说明如下。更多StringToSign构造示例参见[StringToSign构造示例]。
   
   ```
   StringToSign = 
       HTTP-Verb + "\n" + 
       Content-MD5 + "\n" + 
       Content-Type + "\n" + 
       Date + "\n" + 
       CanonicalizedHeaders + "\n" +
       CanonicalizedResource
   ```
   表3构造StringToSign所需参数说明 
   | **参数名称**              | **参数类型** | **是否必选** | **描述**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
   |:---|:---|:---|:---|
   | HTTP-Verb             | String   | 必选       | **参数解释** **：** HTTP方法（也称为操作或动词），对REST接口而言，即为http请求操作的VERB，如："PUT"，"GET"，"DELETE"等字符串，您可根据要调用的API选择。 **约束限制：** 不涉及 **取值范围：** - GET：请求服务器返回指定资源，如获取桶列表、下载对象等。  - PUT：请求服务器更新指定资源，如创建桶、上传对象等。  - POST：请求服务器新增资源或执行特殊操作，如初始化上传段任务、合并段等。  - DELETE：请求服务器删除指定资源，如删除对象等。  - HEAD：请求服务器返回指定资源的概要，如获取对象元数据等。  - OPTIONS（暂不支持工具生成签名）：请求服务器检查是否具有某个资源的操作权限，需要桶配置CORS。   **默认取值：** 无，此参数必选。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
   | Content-MD5           | String   | 可选       | **参数解释** **：** 消息体（body体）的MD5摘要字符串。Content-MD5是按照RFC 1864标准计算出的消息体（body体）的128bit MD5值经过base64编码后得到的字符串。 **约束限制：** 不涉及 **取值范围：** 长度为22的字符串。 **默认取值：** 不设置默认为空。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
   | Content-Type          | String   | 可选       | **参数解释** **：** Content-Type用于指定Object的文件类型，例如： text/plain。文件类型决定浏览器将以什么形式、什么编码读取文件。 **约束限制：** 不涉及 **取值范围：** Content-type的常见取值参见[常见的Content-Type类型](https://support.huaweicloud.com/usermanual-obs/obs_03_0324.html)。 **默认取值：** 当请求中不带该头域时，该参数按照空字符串处理，例如[表4]。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
   | Date                  | String   | 必选       | **参数解释** **：** 请求生成的时间。如果Date与当前服务器的时间相差超过15分钟时，则服务端返回403，认为请求已失效。 **约束限制：** - Date必须为RFC 1123格式的GMT时间。  - 该值来自于Date头域或者x-obs-date头域。当两头域同时存在时，以x-obs-date为准。   **取值范围：** 不涉及 **默认取值：** 不涉及                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
   | CanonicalizedHeaders  | String   | 可选       | **参数解释** **：** OBS服务自定义的附加头域，即以"x-obs-"作为前缀的头域，如"x-obs-date，x-obs-acl，x-obs-meta-\*"等。每个头域包含头域名称和头域值两部分，如"x-obs-storage-class:STANDARD"中"x-obs-storage-class"是头域名称，"STANDARD"是头域值。 上传对象和上传段支持携带x-obs-content-sha256头域，该头域值为请求消息体（body体）的SHA256值转十六进制值，服务端会对携带此头域的请求计算其消息体的sha256值做校验（性能会有部分下降，在安全上推荐该算法）。 **约束限制：** 1. 头域名称要转为小写，但头域值要区分大小写，如"x-obs-storage-class:STANDARD"。需要添加多个头域时，所有头域要按照头域名称的字典序从小到大进行排序。[使用工具计算签名](https://support.huaweicloud.com/api-obs/obs_04_0171.html)时，无需关注头域名称的大小写转换和字典排序，工具会自动完成。  2. 头域名称不允许含有非ASCII码或不可识别字符，头域值也不建议使用非ASCII码或不可识别字符。如果一定要使用非ASCII码或不可识别字符，需要客户端自行做编解码处理，可以采用URL编码或者Base64编码，服务端不会做解码处理。  3. 头域中不能含有无意义空格或Tab键。例如：x-obs-meta-name: name（name前带有一个无意义空格），需要转换为：x-obs-meta-name:name  4. 如果头域名称重复，则需要将头域值进行合并，多个头域值以逗号分隔。如：x-obs-meta-name:name1和x-obs-meta-name:name2，合并成x-obs-meta-name:name1,name2。  5. 每一个头域字段都需要另起新行，以换行符"\\n"结尾。**如果没有头域字段时，不需要以换行符"\\n"结尾。**   **取值范围：** 调用API时，从API支持的头域中选取。 **默认取值：** 不设置默认为空。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
   | CanonicalizedResource | String   | 必选       | **参数解释** **：** HTTP请求所指定的OBS资源，结构如下： ``` CanonicalizedResource = "/" + 桶名 +  "/" + 对象名 + "?" + 子资源 ``` 以获取对象（GetObject）接口为例，假设桶名为bucket-test，对象名为object-test，对象版本号为xxx，获取时需要重写Content-Type为text/plain，那么CanonicalizedResource为： ``` /bucket-test/object-test?response-content-type=text/plain&versionId=xxx ``` - 桶名： 如果桶未绑定自定义域名，则直接使用原始桶名。 如果桶绑定了自定义域名，则桶名由自定义域名表示，如"/obs.ccc.com/object"，其中"obs.ccc.com"为桶绑定的自定义域名。 如果API操作不指定桶，例如列举账号下所有桶操作，则桶名字段和对象名字段都不写。如"/"。   - 对象名： 要操作对象的对象名。需要遵循对象的命名规则。   - 子资源（多个子资源需按照字典序从小到大排列，并使用"\&"拼接）： 资源标识：CDNNotifyConfiguration, acl, append, attname, backtosource, cors, customdomain, delete, deletebucket, directcoldaccess, encryption, inventory, length, lifecycle, location, logging, metadata, modify, name, notification, partNumber, policy, position, quota, rename, replication, restore, storageClass, storagePolicy, storageinfo, tagging, torrent, truncate, uploadId, uploads, versionId, versioning, versions, website,x-obs-security-token, object-lock, retention 用于指定响应头域的子资源：response-cache-control, response-content-disposition, response-content-encoding, response-content-language, response-content-type, response-expires 图片处理相关资源标识： x-image-process, x-image-save-bucket, x-image-save-object    **约束限制：** 子资源通常是唯一的，不建议包含多个相同关键字的子资源（例如，key=value1\&key=value2），如果存在这种情况，OBS服务端签名时只会计算第一个子资源且也只有第一个子资源的值会对实际业务产生作用。 **取值范围：** 不涉及。 **默认取值：** 不写默认为"/"。 |
      
   
   
2. 将StringToSign转为UTF-8编码的字节流。
3. 使用SK对第二步的结果进行HMAC-SHA1签名计算。
4. 对第三步的结果进行Base64编码，得到签名。
 
#### StringToSign构造示例
以下是生成StringToSign的示例：
 表4获取对象 
| **请求消息头**                                                                                                                                                                                     | **StringToSign**                                                                                                                                                                                                                                  |
|:---|:---|
| GET /object.txt HTTP/1.1 Host: bucket.obs.cn-north-4.myhuaweicloud.com Date: Sat, 12 Oct 2015 08:12:38 GMT | GET \\n \\n \\n Sat, 12 Oct 2015 08:12:38 GMT\\n /bucket/object.txt |
   
表5使用临时AK/SK和securitytoken上传对象 
| **请求消息头**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | **StringToSign**                                                                                                                                                                                                                                                                                                                                                                                                                                         |
|:---|:---|
| PUT /object.txt HTTP/1.1 User-Agent: curl/7.15.5 Host: bucket.obs.cn-north-4.myhuaweicloud.com x-obs-date:Tue, 15 Oct 2015 07:20:09 GMT x-obs-security-token: YwkaRTbdY8g7q.... content-type: text/plain Content-Length: 5913339 | PUT\\n \\n text/plain\\n \\n x-obs-date:Tue, 15 Oct 2015 07:20:09 GMT\\n x-obs-security-token:YwkaRTbdY8g7q....\\n /bucket/object.txt  |
   
表6带请求头字段上传对象 
| **请求消息头**                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | **StringToSign**                                                                                                                                                                                                                                                                                                        |
|:---|:---|
| PUT /object.txt HTTP/1.1 User-Agent: curl/7.15.5 Host: bucket.obs.cn-north-4.myhuaweicloud.com Date: Mon, 14 Oct 2015 12:08:34 GMT x-obs-acl: public-read content-type: text/plain Content-Length: 5913339 | PUT\\n \\n text/plain\\n Mon, 14 Oct 2015 12:08:34 GMT\\n x-obs-acl:public-read\\n /bucket/object.txt |
   
表7获取对象ACL 
| 请求消息头                                                                                                                                                                                                  | StringToSign                                                                                                                                                                                                                                         |
|:---|:---|
| GET /object.txt?acl HTTP/1.1 Host: bucket.obs.cn-north-4.myhuaweicloud.com Date: Sat, 12 Oct 2015 08:12:38 GMT | GET \\n \\n \\n Sat, 12 Oct 2015 08:12:38 GMT\\n /bucket/object.txt?acl |
   
表8上传对象且携带Content-MD5头域 
| 请求消息头                                                                                                                                                                                                                                                                                                                                                                 | StringToSign                                                                                                                                                                                                                                                                                                                                          |
|:---|:---|
| PUT /object.txt HTTP/1.1 Host: bucket.obs.cn-north-4.myhuaweicloud.com x-obs-date:Tue, 15 Oct 2015 07:20:09 GMT Content-MD5: I5pU0r4+sgO9Emgl1KMQUg== Content-Length: 5913339 | PUT\\n I5pU0r4+sgO9Emgl1KMQUg==\\n \\n \\n x-obs-date:Tue, 15 Oct 2015 07:20:09 GMT\\n /bucket/object.txt |
   
表9使用自定义域名方式上传对象 
| 请求消息头                                                                                                                                                                                                                                                                                                                                         | StringToSign                                                                                                                                                                                                                                                                                                                                              |
|:---|:---|
| PUT /object.txt HTTP/1.1 Host: obs.ccc.com x-obs-date:Tue, 15 Oct 2015 07:20:09 GMT Content-MD5: I5pU0r4+sgO9Emgl1KMQUg== Content-Length: 5913339 | PUT\\n I5pU0r4+sgO9Emgl1KMQUg==\\n \\n \\n x-obs-date:Tue, 15 Oct 2015 07:20:09 GMT\\n /obs.ccc.com/object.txt |
   
 #### Java中Content-MD5的计算方法示例
您可以选择在构造StringToSign时加入Content-MD5的头域和头域值，以下为计算Content-MD5头域值的代码示例：
```
import java.security.MessageDigest;
import java.util.Base64;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
public class Md5{
     public static void main(String[] args) {
          try {
                 String exampleString = "blog";
                 MessageDigest messageDigest = MessageDigest.getInstance("MD5"); 
                 Base64.Encoder encoder =Base64.getEncoder();
                 //计算字符串的MD5值，然后将MD5值进行Base64编码
                 String contentMd5 = encoder.encode(messageDigest.digest(exampleString.getBytes("utf-8")));
                 System.out.println("Content-MD5:" + contentMd5);    
          } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) 
          {
                 e.printStackTrace();
          }
     }
}
```
根据请求字符串(StringToSign)和用户SK使用如下算法生成Signature，生成过程使用HMAC算法(hash-based authentication code algorithm)。
```
Signature = Base64( HMAC-SHA1( Your_SK, UTF-8-Encoding-Of( StringToSign ) ) )
```
例如在华北-北京四区域创建桶名为newbucketname2的私有桶，客户端请求格式为：
```
PUT / HTTP/1.1 
Host: newbucketname2.obs.cn-north-4.myhuaweicloud.com
Content-Length: length
Date: Fri, 06 Jul 2018 03:45:51 GMT
x-obs-acl:private
x-obs-storage-class:STANDARD
Authorization: OBS UDSIAMSTUBTEST000254:ydH8ffpcbS6YpeOMcEZfn0wE90c=
<CreateBucketConfiguration xmlns="http://obs.cn-north-4.myhuaweicloud.com/doc/2015-06-30/"> 
    <Location>cn-north-4</Location>
</CreateBucketConfiguration>
```
#### 签名示例代码
以下是各语言计算签名的示例代码：
- [Java中签名的计算方法]
  ```
  import java.io.UnsupportedEncodingException;
  import java.net.URLEncoder;
  import java.security.InvalidKeyException;
  import java.security.NoSuchAlgorithmException;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Base64;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Locale;
  import java.util.Map;
  import java.util.TreeMap;
  import javax.crypto.Mac;
  import javax.crypto.spec.SecretKeySpec;
  public class SignDemo {
  
      private static final String SIGN_SEP = "\n";
      private static final String OBS_PREFIX = "x-obs-";
      private static final String DEFAULT_ENCODING = "UTF-8";
      private static final List<String> SUB_RESOURCES = Collections.unmodifiableList(Arrays.asList(
          "CDNNotifyConfiguration", "acl", "append", "attname", "backtosource", "cors", "customdomain", "delete",
     "deletebucket", "directcoldaccess", "encryption", "inventory", "length", "lifecycle", "location", "logging",
     "metadata", "mirrorBackToSource", "modify", "name", "notification", "obscompresspolicy",  "orchestration", 
     "partNumber", "policy", "position", "quota","rename", "replication", "response-cache-control", 
     "response-content-disposition","response-content-encoding", "response-content-language", "response-content-type", 
     "response-expires","restore", "storageClass", "storagePolicy", "storageinfo", "tagging", "torrent", "truncate",
     "uploadId", "uploads", "versionId", "versioning", "versions", "website", "x-image-process",
     "x-image-save-bucket", "x-image-save-object", "x-obs-security-token", "object-lock", "retention"));
   
      private String ak;
      private String sk;
      // 对字符串进行UTF8编码
      public String urlEncode(String input) throws UnsupportedEncodingException {
          return URLEncoder.encode(input, DEFAULT_ENCODING)
              .replaceAll("%7E", "~") //for browser
              .replaceAll("%2F", "/")
              .replaceAll("%20", "+");
      }
   
      private String join(List<?> items, String delimiter) {
          StringBuilder sb = new StringBuilder();
          for (int i = 0; i < items.size(); i++) {
              String item = items.get(i).toString();
              sb.append(item);
              if (i < items.size() - 1) {
                  sb.append(delimiter);
              }
          }
          return sb.toString();
      }
  
      private boolean isValid(String input) {
          return input != null && !input.equals("");
      }
      // 使用访问密钥SK计算HmacSHA1值
      public String hmacSha1(String input) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
          SecretKeySpec signingKey = new SecretKeySpec(this.sk.getBytes(DEFAULT_ENCODING), "HmacSHA1");
          // 获取Mac实例，并通过getInstance方法指定使用HMAC-SHA1算法
          Mac mac = Mac.getInstance("HmacSHA1");
          // 使用访问密钥SK初始化Mac对象
          mac.init(signingKey);
          return Base64.getEncoder().encodeToString(mac.doFinal(input.getBytes(DEFAULT_ENCODING)));
      }
      // 构造StringToSign
      private String stringToSign(String httpMethod, Map<String, String[]> headers, Map<String, String> queries,
          String bucketName, String objectName) throws Exception{
          String contentMd5 = "";
          String contentType = "";
          String date = "";
  
          TreeMap<String, String> canonicalizedHeaders = new TreeMap<String, String>();
  
          String key;
          List<String> temp = new ArrayList<String>();
          for(Map.Entry<String, String[]> entry : headers.entrySet()) {
              key = entry.getKey();
              if(key == null || entry.getValue() == null || entry.getValue().length == 0) {
                  continue;
              }
  
              key = key.trim().toLowerCase(Locale.ENGLISH);
              if(key.equals("content-md5")) {
                  contentMd5 = entry.getValue()[0];
                  continue;
              }
  
              if(key.equals("content-type")) {
                  contentType = entry.getValue()[0];
                  continue;
              }
  
              if(key.equals("date")) {
                  date = entry.getValue()[0];
                  continue;
              }
  
              if(key.startsWith(OBS_PREFIX)) {
                  for(String value : entry.getValue()) {
                      if(value != null) {
                          temp.add(value.trim());
                      }
                  }
                  canonicalizedHeaders.put(key, this.join(temp, ","));
                  temp.clear();
              }
          }
  // 如果header头域中包含x-obs-date，Date参数置空
          if(canonicalizedHeaders.containsKey("x-obs-date")) {
              date = "";
          }
  
          // 构造StringToSign，拼接HTTP-Verb、Content-MD5、Content-Type、Date
          StringBuilder stringToSign = new StringBuilder();
          stringToSign.append(httpMethod).append(SIGN_SEP)
              .append(contentMd5).append(SIGN_SEP)
              .append(contentType).append(SIGN_SEP)
              .append(date).append(SIGN_SEP);
  
          // 构造StringToSign，拼接CanonicalizedHeaders
          for(Map.Entry<String, String> entry : canonicalizedHeaders.entrySet()) {
              stringToSign.append(entry.getKey()).append(":").append(entry.getValue()).append(SIGN_SEP);
          }
  
          // 构造StringToSign，拼接CanonicalizedResource
          stringToSign.append("/");
          if(this.isValid(bucketName)) {
              stringToSign.append(bucketName).append("/");
              if(this.isValid(objectName)) {
                  stringToSign.append(this.urlEncode(objectName));
              }
          }
  
          TreeMap<String, String> canonicalizedResource = new TreeMap<String, String>();
          for(Map.Entry<String, String> entry : queries.entrySet()) {
              key = entry.getKey();
              if(key == null) {
                  continue;
              }
  
              if(SUB_RESOURCES.contains(key)) {
                  canonicalizedResource.put(key, entry.getValue());
              }
          }
  
          if(canonicalizedResource.size() > 0) {
              stringToSign.append("?");
              for(Map.Entry<String, String> entry : canonicalizedResource.entrySet()) {
                  stringToSign.append(entry.getKey());
                  if(this.isValid(entry.getValue())) {
                      stringToSign.append("=").append(entry.getValue());
                  }
                  stringToSign.append("&");
              }
              stringToSign.deleteCharAt(stringToSign.length()-1);
          }
          // 403报错的情况下，可以打印stringtosign定位问题
          // System.out.println(String.format("StringToSign:%s%s", SIGN_SEP, stringToSign.toString()));
  
          return stringToSign.toString();
      }
  
      public String headerSignature(String httpMethod, Map<String, String[]> headers, Map<String, String> queries, String bucketName, String objectName) throws Exception {
          // 构造StringToSign
          String stringToSign = this.stringToSign(httpMethod, headers, queries, bucketName, objectName);
  
          // 计算签名
          return String.format("OBS %s:%s", this.ak, this.hmacSha1(stringToSign));
      }
  
      public static void main(String[] args) throws Exception {
          SignDemo demo = new SignDemo();
          /* 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险，建议在配置文件或者环境变量中密文存放，使用时解密，确保安全；
          本示例以ak和sk保存在环境变量中为例，运行本示例前请先在本地环境中设置环境变量HUAWEICLOUD_SDK_AK和HUAWEICLOUD_SDK_SK。*/
          demo.ak = System.getenv("HUAWEICLOUD_SDK_AK");
          demo.sk = System.getenv("HUAWEICLOUD_SDK_SK");
  
          String bucketName = "bucket-test";
          String objectName = "hello.jpg";
          Map<String, String[]> headers = new HashMap<String, String[]>();
          headers.put("date", new String[] {"Sat, 12 Oct 2015 08:12:38 GMT"});
          headers.put("x-obs-acl", new String[] {"public-read"});
          headers.put("x-obs-meta-key1", new String[] {"value1"});
          headers.put("x-obs-meta-key2", new String[] {"value2", "value3"});
          Map<String, String> queries = new HashMap<String, String>();
          queries.put("acl", null);
  
          //计算Header中携带的签名
          System.out.println(demo.headerSignature("PUT", headers, queries, bucketName, objectName));
      }
  
  }
  ```
- [Python中签名的计算方法]
  ```
  import os
  import sys
  import hashlib
  import hmac
  import binascii
  from datetime import datetime
  IS_PYTHON2 = sys.version_info.major == 2 or sys.version < '3'
  # 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险，建议在配置文件或者环境变量中密文存放，使用时解密，确保安全；
  # 本示例以ak和sk保存在环境变量中为例，运行本示例前请先在本地环境中设置环境变量HUAWEICLOUD_SDK_AK和HUAWEICLOUD_SDK_SK。
  Your_SK = os.getenv('HUAWEICLOUD_SDK_SK')
  httpMethod = "PUT"
  contentType = "application/xml"
  # "date" 是请求生成的时间
  date = datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
  canonicalizedHeaders = "x-obs-acl:private\n"
  CanonicalizedResource = "/newbucketname2"
  # 构造StringToSign
  canonical_string = httpMethod + "\n" + "\n" + contentType + "\n" + date + "\n" + canonicalizedHeaders + CanonicalizedResource
  # 计算签名，签名公式公式为Signature = Base64( HMAC-SHA1( Your_SK, UTF-8-Encoding-Of( StringToSign ) ) )
  if IS_PYTHON2:    
      hashed = hmac.new(Your_SK, canonical_string, hashlib.sha1)    
      encode_canonical = binascii.b2a_base64(hashed.digest())[:-1]
  else:    
      hashed = hmac.new(Your_SK.encode('UTF-8'), canonical_string.encode('UTF-8'), hashlib.sha1)
      encode_canonical = binascii.b2a_base64(hashed.digest())[:-1].decode('UTF-8')
  print(encode_canonical)
  ```
- [C语言中签名的计算方法]
  请单击[此处](https://obs-community.obs.cn-north-1.myhuaweicloud.com/sign/signature_c.zip)，下载C语言签名计算代码样例，其中：
  1. 计算签名的接口包含在sign.h头文件中。
  
  2. 计算签名的示例代码在main.c文件中。
   
#### 签名不匹配报错处理
如果调用OBS API报如下错误：
状态码：403 Forbidden
错误码：SignatureDoesNotMatch
错误信息：The request signature we calculated does not match the signature you provided. Check your key and signing method.
请参考以下案例进行排查处理：[签名不匹配（SignatureDoesNotMatch）如何处理](https://support.huaweicloud.com/obs_faq/obs_faq_0173.html)
