签名机制

最近更新时间:2021-09-18 18:01:28

查看PDF

签名机制

金山云OpenAPI支持两种签名算法:AWS签名算法版本4简化版签名算法,您可以根据业务需要选择所使用的签名算法,请注意两种签名算法所使用的公共参数有所区别。

AWS签名算法版本4

支持GET和POST两种HTTP方法,GET方法所有请求参数包括signature放置在url中,POST方法则将signature以名为Authorization header的形式放置在header中,其主要区别在于GET方式处理的请求url长度不能过长。

签名计算的主要流程如下:

1、创建一个正规化请求

在签名前,首先将请求进行正规化格式化,目的是让签名计算过程无二意,其主要过程伪代码如下:

CanonicalRequest = HTTPRequestMethod + '\n' + CanonicalURI + '\n' + CanonicalQueryString + '\n' + CanonicalHeaders + '\n' + SignedHeaders + '\n' + HexEncode(Hash(RequestPayload))

Hash指代计算哈希的算法,目前使用SHA-256,HexEncode是对哈希值进行用16进制编码(使用小写字母)。

具体步骤如下

  1. 抽取HTTP请求方法(如GET、PUT、POST)结尾附加“换行符”
  2. URI绝对路径进行URI编码得到正规化URI,如果绝对路径为空,那么使用前斜线"/",结尾附加“换行符”
  3. 构建正规化Querystring,结尾附加“换行符”
    • URI编码每一个querystring参数名称和参数值(注:GET方式需要包含哈希算法、信任状、签名日期和签名header等全部参数)
    • 按照ASCII字节顺序对参数名称严格排序
    • 将排序好的参数名称和参数值用=连接,按照排序结果将“参数对”用&连接
  4. 构建正规化headers,结尾附加“换行符”,伪代码如下:
CanonicalHeaders = CanonicalHeadersEntry0 + CanonicalHeadersEntry1 + ... + CanonicalHeadersEntryN

其中:

CanonicalHeadersEntry = Lowercase(HeaderName) + ':' + Trimall(HeaderValue) + '\n'

lowercase表示将header名字转为小写字母,trimall表示去掉header值前和值后的白空格,并将header值里面的连续白空格变成单空格,但是不去掉双引号中间的任何空格,且最后的正规化headers是按照header名称排序后的结果。

建议采用的CanonicalHeaders 如下:

CanonicalHeaders = 'host:' + host + '\n' + 'x-amz-date:' + RequestDate  + '\n'

其中,
RequestDate是公共请求头X-Amz-Date的值,表示当前请求的时间和日期,格式YYYYMMDD’T’HHMMSS’Z’ ,例如:20210726T111901Z

  1. 添加签名headers,结尾附加“换行符”。签名header是包含在正规化headers中名称列表,其目的是指明哪些header参与签名计算,从而忽略请求被proxy添加的额外header,其中host、x-amz-date两个header如果存在则必须添加进来,伪代码如下
SignedHeaders = Lowercase(HeaderName0) + ';' + Lowercase(HeaderName1) + ";" + ... + Lowercase(HeaderNameN)

对应上面建议的CanonicalHeaders,需采用如下的SignedHeaders :

SignedHeaders = 'host;x-amz-date'

然后处理请求body,如下
6. 对请求body使用哈希算法(SHA256)计算哈希值,并将二进制哈希值结果用16进制编码表示出来(且不使用大写字符),伪代码如下:

HashedPayload = Lowercase(HexEncode(Hash(requestPayload)))
  1. 此时,将上述1-6步骤的结果连接成一个字符串,即为正规化请求(CanonicalRequest)
  2. 对第7步的正规化请求使用哈希算法(SHA256)计算哈希值,并将二进制哈希值结果用16进制编码表示出来(且不使用大写字符),伪代码如下:
HashedCanonicalRequest = Lowercase(HexEncode(Hash(CanonicalRequest)))

2、创建签名字符串

签名字符串主要包含请求以及正规化请求的元数据信息,由签名算法、请求日期、信任状和正规化请求哈希值连接组成,伪代码如下:

StringToSign = Algorithm + '\n' + RequestDate + '\n' + CredentialScope + '\n' + HashedCanonicalRequest

其中,
签名算法(Algorithm)为AWS4-HMAC-SHA256;
请求日期(RequestDate)是公共请求头X-Amz-Date的值,表示当前请求的日期和时间,格式YYYYMMDD’T’HHMMSS’Z’ ,例如:20210726T111901Z;
信任状(CredentialScope)格式为;YYYYMMDD/Region/Service/aws4_request;
正规化请求哈希值(HashedCanonicalRequest )为上述1中第8步的结果(注意结尾不附加“换行符”)。

注意:
YYYYMMDD取自RequestDate中的日期,对应上面的示例为20210726
Region为cn-beijing-6
Service为cdn

3、计算签名信息

在计算签名前,首先从私有访问密钥(secret AccessKey)派生出签名密钥(signing key),而不是直接使用私有访问密钥;之后使用签名密钥和2中计算的签名字符串来计算签名值,具体计算过程如下

  1. 生成签名密钥,伪代码如下
kSecret = *Your KSC Secret Access Key*
kDate = HMAC("AWS4" + kSecret, Date)
kRegion = HMAC(kDate, Region)
kService = HMAC(kRegion, Service)
kSigning = HMAC(kService, "aws4_request")

其方式是通过HMAC算法依次生成下一个HMAC的key值(第一个为私有访问密钥字符串),而message值则依次为信任状中的各项内容(Date、Region、Service、字符串"aws4_request");HMAC算法采用HMAC-SHA256,返回值为哈希值二进制形式(256bit,32字节),不需要做8/16进制编码显示。

注意:
Date为YYYYMMDD,取自RequestDate中的日期,对应上面的示例为20210726
Region为cn-beijing-6
Service为cdn

  1. 计算签名,伪代码如下:
signature = HexEncode(HMAC(derived-signing-key, string-to-sign))

使用HMAC-SHA256算法,以签名密钥作为key,签名字符串作为data计算签名,签名后的二进制哈希值结果以16进制编码输出。

4、签名的请求示例

通过 RESTful API 对 CDN 发起的 HTTP 签名请求,可以通过以下方式传递签名:

通过标准的 HTTP Authorization 头

签名头格式如下:

Authorization: Algorithm + ' ' + 'Credential=' + AccessKeyID + '/' + CredentialScope + ', ' +   'SignedHeaders=' + SignedHeaders + ', ' + 'Signature=' + signature

其中,
Algorithm为AWS4-HMAC-SHA256;
AccessKeyID为访问密钥ID;
CredentialScope格式为YYYYMMDD/Region/Service/aws4_request;
SignedHeaders为签名headers,取值如上所述
signature为签名值,取值如上所述

注意:
YYYYMMDD取自RequestDate中的日期,对应上面的示例为20210726
Region为cn-beijing-6
Service为cdn

GET请求示例:

GET /2016-09-01/domain/GetDomainConfigs?DomainId=2D08BTW HTTP/1.1
User-Agent: python-requests/2.23.0
accept-encoding: gzip
accept: */*
connection: keep-alive
host: cdn.api.ksyun.com
x-action: GetDomainConfigs
x-version: 2016-09-01
x-amz-date: 20210726T111902Z
Authorization: AWS4-HMAC-SHA256 Credential=AKLTTx7VDwyJRNGkjODXPTCauQ/20210726/cn-beijing-6/cdn/aws4_request, SignedHeaders=host;x-amz-date, Signature=def04483a4ad6fa8f0407f1ab132cd0aa7cd7ee1537baeedbd4dd495f8a8a3e4

POST请求示例:

POST /2016-09-01/domain/GetDomainConfigs HTTP/1.1
Host: cdn.api.ksyun.com
User-Agent: python-requests/2.23.0
Accept-Encoding: gzip, deflate
Accept: application/json
Connection: keep-alive
content-type: application/json
x-amz-date: 20210726T111901Z
authorization: AWS4-HMAC-SHA256 Credential=AKLTTx7VDwyJRNGkjODXPTCauQ/20210726/cn-shanghai-1/cdn/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=07b5b4121797bbd84e9971769508447268785f4e11212d9af86ae5656280fd8b
X-version: 2016-09-01
X-action: GetDomainConfigs
Content-Length: 22

{“DomainId”:“2D08BTW”}

5、签名SDK示例

主要SDK:

PHP

JAVA

PYTHON

其他语言签名算法参考:

GOLANG

JS

简化版签名算法

签名过程

1.构造规范化请求字符串

第一步:请求参数排序

  • 请求参数包括公共参数和业务参数,不包含公共参数Signature。
  • 排序规则以参数名按照字典排序。

第二步:请求参数编码
使用UTF-8字符集按照RFC3986规则编码请求参数和参数取值,编码规则如下:

  • 对于字符A ~ Z、a ~ z、0 ~ 9以及字符-、_、.和~不编码。
  • 对于其他字符编码成%XY的格式,其中XY是字符对应ASCII码的16进制(大写)。

不同语言实现的URLEncode方式不同,为了得到RFC3986规则的编码,分别说明如下:

  • 如果您使用的是Java中的java.net.URLEncoder,可以先用URLEncoder.encode方法编码,随后将编码后的字符中加号(+)替换为%20、星号(*)替换为%2A、%7E替换为波浪号(~)即可;
  • 如果您使用的是golang中的net/url,可以用url.Values.Encode方法编码,随后将编码后的字符中加号(+)替换为%20即可;
  • 如果您使用的是php中的rawurlencode方法,不需要替换;
  • 如果您使用的是python3中的urllib.request,可以用urllib.request.quote方法,需要指定字符串中的波浪号(~)不编码,例如urllib.request.quote(str, ‘~’),str为待编码字符串;

第三步:请求参数拼接成CanonicalizedQueryString
每对URLEncode后的参数名称和参数值,用=进行连接。每对之间使用&进行连接。得到规范化请求字符串CanonicalizedQueryString。

2.计算签名。

sign = hash_hmac(‘sha256’, CanonicalizedQueryString, sk)

sign值为签名算法返回的16进制格式小写字符串

签名样例:

5346bfebeb3f2162e2459e09a52b640584e5f1aa5012b15c6b85388680d4663e
计算签名时使用的sk为Accesskey对应的秘钥,使用的哈希算法是:HMAC-SHA256。

3.将签名值作为Signature参数值添加到请求参数中。


签名过程示例


该样例以iam服务的CreateUser为例,点击查看该API文档


1、构造规范化请求字符串:CanonicalizedQueryString

//请求参数数组:
array (
    'Accesskey'=> 'AKLTXQVF0pOmS6aahIrD5r0B3Q', //公共参数
    'Service'=> 'iam', //公共参数
    'Action'=> 'CreateUser', //公共参数
    'Version'=> '2015-11-01', //公共参数
    'Timestamp'=> '2021-08-12T02:47:36Z', //公共参数
    'SignatureVersion'=> '1.0', //公共参数
    'SignatureMethod'=> 'HMAC-SHA256', //公共参数
    'UserName' => 'Ttest', //业务参数
    'RealName' => '周四测试', //业务参数
    'Email' => '[email protected]', //业务参数
    'Remark' => '~ce shi*%#|+', //业务参数
)

第一步:请求参数排序

//排序结果如下:
array (
    'Accesskey' => 'AKLTXQVF0pOmS6aahIrD5r0B3Q',
    'Action' => 'CreateUser',
    'Email' => '[email protected]',
    'RealName' => '周四测试',
    'Remark' => '~ce shi*%#|+',
    'Service' => 'iam',
    'SignatureMethod' => 'HMAC-SHA256',
    'SignatureVersion' => '1.0',
    'Timestamp' => '2021-08-12T02:47:36Z',
    'UserName' => 'Ttest',
    'Version' => '2015-11-01',
)

第二步:请求参数编码

//编码结果如下:
array (
    'Accesskey' => 'AKLTXQVF0pOmS6aahIrD5r0B3Q',
    'Action' => 'CreateUser',
    'Email' => 'zsce%40kkingsoft.com',
    'RealName' => '%E5%91%A8%E5%9B%9B%E6%B5%8B%E8%AF%95',
    'Remark' => '~ce%20shi%2A%25%23%7C%2B',
    'Service' => 'iam',
    'SignatureMethod' => 'HMAC-SHA256',
    'SignatureVersion' => '1.0',
    'Timestamp' => '2021-08-12T02%3A47%3A36Z',
    'UserName' => 'Ttest',
    'Version' => '2015-11-01',
)

第三步:请求参数拼接成CanonicalizedQueryString

//拼接结果如下
Accesskey=AKLTXQVF0pOmS6aahIrD5r0B3Q&Action=CreateUser&Email=zsce%40kkingsoft.com&RealName=%E5%91%A8%E5%9B%9B%E6%B5%8B%E8%AF%95&Remark=~ce%20shi%2A%25%23%7C%2B&Service=iam&SignatureMethod=HMAC-SHA256&SignatureVersion=1.0&Timestamp=2021-08-12T02%3A47%3A36Z&UserName=Ttest&Version=2015-11-01

2、计算签名

sk = 'OMovU5PTLh6y9E9Ioe3K411jt99VqyQSBXgAcDYlo49R3lvUIzb6e/efZCFDmtFlzw=='
CanonicalizedQueryString = "Accesskey=AKLTXQVF0pOmS6aahIrD5r0B3Q&Action=CreateUser&Email=zsce%40kkingsoft.com&RealName=%E5%91%A8%E5%9B%9B%E6%B5%8B%E8%AF%95&Remark=~ce%20shi%2A%25%23%7C%2B&Service=iam&SignatureMethod=HMAC-SHA256&SignatureVersion=1.0&Timestamp=2021-08-12T02%3A47%3A36Z&UserName=Ttest&Version=2015-11-01"
//sign = hash_hmac('sha256', CanonicalizedQueryString, sk)
sign: fc9088ab845949dac4040be9b7ce7859068b5c21d4c400fec8ee0cefb777f659

3、将签名值作为Signature参数值添加到请求参数中

//请求示例:
curl -s -L -X POST 'iam.api.ksyun.com' \
-H 'Accept: application/json' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'Accesskey=AKLTXQVF0pOmS6aahIrD5r0B3Q' \
--data-urlencode 'Service=iam' \
--data-urlencode 'Action=CreateUser' \
--data-urlencode 'Version=2015-11-01' \
--data-urlencode 'Timestamp=2021-08-12T02:47:36Z' \
--data-urlencode 'SignatureVersion=1.0' \
--data-urlencode 'SignatureMethod=HMAC-SHA256' \
--data-urlencode 'UserName=Ttest' \
--data-urlencode 'RealName=周四测试' \
--data-urlencode '[email protected]' \
--data-urlencode 'Remark=~ce shi*%#|+' \
--data-urlencode 'Signature=fc9088ab845949dac4040be9b7ce7859068b5c21d4c400fec8ee0cefb777f659'

签名demo

提供phpjavagopython四种语言的签名demo

文档内容是否对您有帮助?

根本没帮助
文档较差
文档一般
文档不错
文档很好

在文档使用中是否遇到以下问题

内容不全,不深入
内容更新不及时
描述不清晰,比较混乱
系统或功能太复杂,缺乏足够的引导
内容冗长

更多建议

0/200

评价建议不能为空

提交成功!

非常感谢您的反馈,我们会继续努力做到更好!

问题反馈