有奖捉虫:办公协同&微信生态&物联网文档专题 HOT

使用数据保险箱服务时,可通过 RESTful API 对数据保险箱服务发起 HTTP 签名请求,数据保险箱服务的服务器端将会对请求发起者的身份进行验证。

数据保险箱服务基于密钥 HMAC(Hash Message Authentication Code)的自定义方案进行身份验证。

可对 API 请求进行多方面的安全防护:

  • 请求者身份验证:通过访问者唯一 ID 和密钥确定请求者身份。
  • 防止传输数据篡改:对数据签名并检验,保障传输内容完整性。
  • 防止签名被盗用:对签名设置时效,避免签名盗用并重复使用。

准备工作

  1. 在访问管理控制台的 API 密钥管理 页面中获取 AppID、SecretId 和 SecretKey。
  2. 确定开发语言:支持但不限于 Java、PHP、C#、C++、Node.js、Python,根据不同的开发语言,确定对应的 HMAC-SHA1、SHA1 和 UrlEncode 函数。其中,HMAC-SHA1 和 SHA1 函数以 UTF-8 编码字符串为输入,以16进制小写字符串为输出。UrlEncode 基于 UTF-8 编码,包括 ASCII 范围内的可打印字符,下列特殊符号也应被编码:
字符 十进制 十六进制 字符 十进制 十六进制
(空格) 32 20 ; 59 3B
! 33 21 < 60 3C
" 34 22 = 61 3D
# 35 23 > 62 3E
$ 36 24 ? 63 3F
% 37 25 @ 64 40
& 38 26 [ 91 5B
' 39 27 \ 92 5C
( 40 28 ] 93 5D
) 41 29 ^ 94 5E
* 42 2A ` 96 60
+ 43 2B { 123 7B
, 44 2C | 124 7C
/ 47 2F } 125 7D
: 58 3A --- --- ---
注意:

  • 建议用户使用子账号密钥 + 环境变量的方式调用 SDK,提高 SDK 使用的安全性。为子账号授权时,请遵循 最小权限指引原则,防止泄漏目标存储桶或对象之外的资源。
  • 如果您一定要使用永久密钥,建议遵循 最小权限指引原则 对永久密钥的权限范围进行限制。

签名步骤

步骤1:生成 KeyTime

  1. 获取当前时间对应的 Unix 时间戳 StartTimestamp,Unix 时间戳是从 UTC(协调世界时,或 GMT 格林威治时间)1970年1月1日0时0分0秒(北京时间 1970年1月1日8时0分0秒)起至现在的总秒数。
  2. 根据上述时间戳和期望的签名有效时长算出签名过期时间对应的 Unix 时间戳 EndTimestamp。
  3. 拼接签名有效时间,格式为StartTimestamp;EndTimestamp,即为 KeyTime。

示例:1557902800;1557910000

步骤2:生成 SignKey

使用 HMAC-SHA1SecretKey 为密钥,以 KeyTime 为消息,计算消息摘要(哈希值),即为 SignKey。

示例:36bcd76dbb8c9f066472fec403df8a34cab34c77

步骤3:生成 UrlParamList 和 HttpParameters

  1. 遍历 HTTP 请求参数,生成 key 到 value 的映射 Map 及 key 的列表 KeyList,其中 key 转换为小写形式,value 使用 UrlEncode 编码,没有 value 的参数,则认为 value 为空字符串。例如请求路径为/?acl,则认为是/?acl=
    说明:

    HTTP 请求参数,即请求路径中?以后的部分,例如请求路径为/?versions%2F&delimiter=%2F&maxCount=10,则请求参数为versions%2F&delimiter=%2F&maxCount=10

  2. 将 KeyList 按照字典序排序。
  3. 将 Map 和 KeyList 中的 key 使用 UrlEncode 编码,并再次转换为小写形式。
  4. 按照 KeyList 的顺序拼接 Map 中的每一个键值对,格式为key1=value1&key2=value2&key3=value3,即为 HttpParameters。
  5. 按照 KeyList 的顺序拼接 KeyList 中的每一项,格式为key1;key2;key3,即为 UrlParamList。

示例:

  • 示例一:
    请求路径:/example-coffer/?delimiter=%2F&maxCount=10
    UrlParamList: delimiter;maxCount
    HttpParameters: delimiter=%2F&maxCount=10
    注意:

    请求路径中的请求参数在实际发送请求时也会进行 UrlEncode,因此要注意不要重复执行 UrlEncode。

  • 示例二:
    请求路径:/example-coffer?replications
    UrlParamList: replications
    HttpParameters: replications=

步骤4:生成 HeaderList 和 HttpHeaders

  1. 遍历 HTTP 请求头部,生成 key 到 value 的映射 Map 及 key 的列表 KeyList,其中 key 转换为小写形式,value 使用 UrlEncode 编码。
  2. 将 KeyList 按照字典序排序。
  3. 将 Map 和 KeyList 中的 key 使用 UrlEncode 编码,并再次转换为小写形式。
  4. 按照 KeyList 的顺序拼接 Map 中的每一个键值对,格式为key1=value1&key2=value2&key3=value3,即为 HttpHeaders。
  5. 按照 KeyList 的顺序拼接 KeyList 中的每一项,格式为key1;key2;key3,即为 HeaderList。

示例:
请求头:

Host: cdcs.ap-shanghai.myqcloud.com
Date: Thu, 16 May 2019 03:15:06 GMT
Content-Type: application/json
Content-Length: 65535

计算得到 HeaderList 为date;content-length;content-type;host,HttpHeaders 为date=Thu%2C%2016%20May%202019%2003%3A15%3A06%20GMT&content-length=65535&content-type=application%2Fjson&host=cdcs.ap-shanghai.myqcloud.com

步骤5:生成 HttpString

根据 HTTP 方法、HTTP 请求路径、HttpParametersHttpHeaders 生成 HttpString,格式为HttpMethod\nUriPathname\nHttpParameters\nHttpHeaders\n

其中:

  • HttpMethod 转换为小写,例如 get 或 put。
  • UriPathname 为请求路径,例如//examplecoffer
  • \n为换行符。如果其中有字符串为空,前后的换行符需要保留,例如get\n/examplecoffer\n\n\n

步骤6:生成 StringToSign

根据 KeyTimeHttpString 生成 StringToSign,格式为sha1\nKeyTime\nSHA1(HttpString)\n
其中:

  • sha1 为固定字符串。
  • \n为换行符。
  • SHA1(HttpString) 为使用 SHA1HttpString 计算的消息摘要。

步骤7:生成 Signature

使用 HMAC-SHA1SignKey 为密钥,以 StringToSign 为消息,计算消息摘要,即为 Signature。

步骤8:生成签名

根据 SecretIdKeyTimeHeaderListUrlParamListSignature 生成签名,格式为:

q-sign-algorithm=sha1&q-ak=SecretId&q-sign-time=KeyTime&q-key-time=KeyTime&q-header-list=HeaderList&q-url-param-list=UrlParamList&q-signature=Signature

签名使用

通过 RESTful API 对数据保险箱服务发起的 HTTP 签名请求,需要讲签名放在 HTTP Authorization 头,例如Authorization: q-sign-algorithm=sha1&q-ak=...&q-sign-time=1557989753;1557996953&...&q-signature=...

说明:

上述示例中使用...省略了部分具体签名内容。

代码示例

伪代码

KeyTime = [Now];[Expires]
SignKey = HMAC-SHA1([SecretKey], KeyTime)
HttpString = [HttpMethod]\n[HttpURI]\n[HttpParameters]\n[HttpHeaders]\n
StringToSign = sha1\nKeyTime\nSHA1(HttpString)\n
Signature = HMAC-SHA1(SignKey, StringToSign)

消息摘要算法示例

不同语言如何调用 HMAC-SHA1 可以参考下面的示例:

PHP

$sha1HttpString = sha1('ExampleHttpString');

$signKey = hash_hmac('sha1', 'ExampleKeyTime', 'YourSecretKey');

Java

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.codec.digest.HmacUtils;

String sha1HttpString = DigestUtils.sha1Hex("ExampleHttpString");

String signKey = HmacUtils.hmacSha1Hex("YourSecretKey", "ExampleKeyTime");

Python

import hmac
import hashlib

sha1_http_string = hashlib.sha1('ExampleHttpString'.encode('utf-8')).hexdigest()

sign_key = hmac.new('YourSecretKey'.encode('utf-8'), 'ExampleKeyTime'.encode('utf-8'), hashlib.sha1).hexdigest()

Node.js

var crypto = require('crypto');

var sha1HttpString = crypto.createHash('sha1').update('ExampleHttpString').digest('hex');
var signKey = crypto.createHmac('sha1', 'YourSecretKey').update('ExampleKeyTime').digest('hex');

Go

import (
    "crypto/hmac"
    "crypto/sha1"
)

h := sha1.New()
h.Write([]byte("ExampleHttpString"))
sha1HttpString := h.Sum(nil)

var hashFunc = sha1.New
h = hmac.New(hashFunc, []byte("YourSecretKey"))
h.Write([]byte("ExampleKeyTime"))
signKey := h.Sum(nil)

实际案例

准备工作

登录访问管理控制台的 API 密钥管理 页面获取其 AppID、SecretId 和 SecretKey,举例如下:

AppID SecretId SecretKey
1250000000 SecretId SecretKey

上传对象

原始请求

PUT /example-coffer/example-file HTTP/1.1
Date: Thu, 16 May 2019 06:45:51 GMT
Host: cdcs.ap-beijing.myqcloud.com/example-coffer/example-file
Content-Type: text/plain
Content-Length: 13
Content-MD5: mQ/fVh815F3k6TAUm8m0eg==

ObjectContent

中间变量

  • KeyTime = 1557989151;1557996351
  • SignKey = eb2519b498b02ac213cb1f3d1a3d27a3b3c9bc5f
  • UrlParamList = (empty string)
  • HttpParameters = (empty string)
  • HeaderList = content-length;content-md5;content-type;date;host;x-cdcs-acl;x-cdcs-grant-read
  • HttpHeaders = content-length=13&content-md5=mQ%2FfVh815F3k6TAUm8m0eg%3D%3D&content-type=text%2Fplain&date=Thu%2C%2016%20May%202019%2006%3A45%3A51%20GMT&host=cdcs.ap-beijing.myqcloud.com
  • HttpString = put\n/example-coffer/example-file\n\ncontent-length=13&content-md5=mQ%2FfVh815F3k6TAUm8m0eg%3D%3D&content-type=text%2Fplain&date=Thu%2C%2016%20May%202019%2006%3A45%3A51%20GMT&host=cdcs.ap-beijing.myqcloud.com\n
  • StringToSign = sha1\n1557989151;1557996351\n8b2751e77f43a0995d6e9eb9477f4b685cca4172\n
  • Signature = 3b8851a11a569213c17ba8fa7dcf2abec6935172

其中,(empty string) 代表长度为0的空字符串,\n代表换行符。

签名后的请求

PUT /example-coffer/example-file HTTP/1.1
Date: Thu, 16 May 2019 06:45:51 GMT
Host: cdcs.ap-beijing.myqcloud.com
Content-Type: text/plain
Content-Length: 13
Content-MD5: mQ/fVh815F3k6TAUm8m0eg==
Authorization: q-sign-algorithm=sha1&q-ak=SecretId&q-sign-time=1557989151;1557996351&q-key-time=1557989151;1557996351&q-header-list=content-length;content-md5;content-type;date;host&q-url-param-list=&q-signature=3b8851a11a569213c17ba8fa7dcf2abec6935172

ObjectContent