Skip to content
大纲

生成认证字符串

概述

当您将HTTP请求发送到Flyme云对象存储时,您需要对您的请求进行签名计算并生成认证字符串,以便Flyme云对象存储可以识别您的身份。您将使用Flyme云对象存储的访问密钥来进行签名计算,该访问密钥包含访问密钥ID(Access Key Id, 后文简称AK)和秘密访问密钥(Secret Access Key, 后文简称SK).

生成公式

认证字符串的生成公式如下:

bce-auth-v1/{accessKeyId}/{timestamp}/{expirationPeriodInSeconds}/{signedHeaders}/{signature}

主要包含了3部分,即前缀字符串(authStringPrefix)、签名头域(signedHeaders)和签名摘要(signature),其中:

前缀字符串:bce-auth-v1/{accessKeyId}/{timestamp}/{expirationPeriodInSeconds },都是可获得的参数;

签名头域:即签名算法中涉及到的HTTP头域列表,将在下文中详细描述

签名摘要:为计算所得,将在下文中详细描述

编码生成步骤

使用编码方式生成最终的认证字符串,您需要完成如下4个子任务:

  • 任务一:创建前缀字符串(authStringPrefix)
  • 任务二:创建规范请求(canonicalRequest),确定签名头域(signedHeaders)
  • 任务三:生成派生签名密钥(signingKey)
  • 任务四:生成签名摘要(signature),并拼接最终的认证字符串(authorization)

从认证字符串格式可以看出,除了参数签名摘要(signature)外,其他参数都是已知或可设置的。 下面详细介绍签名的生成过程,进而得出认证字符串。

签名的计算公式为signature = HMAC-SHA256-HEX(SigningKey,CanonicalRequest),从公式可以看出,想要获得签名需要得到SigningKey和CanonicalRequest两个参数,首先介绍如何获取这两个参数。

任务一:创建前缀字符串(authStringPrefix)

创建前缀字符串,即将已知参数拼接为如下形式:

bce-auth-v1/{accessKeyId}/{timestamp}/{expirationPeriodInSeconds}

其中:

  • accessKeyId: Access Key ID,请参看获取AK/SK来获取。
  • timestamp: 生成签名的UTC时间,格式为yyyy-mm-ddThh:mm:ssZ,例如:2015-04-27T08:23:49Z,请注意请求发送时间不能晚于生成签名时间太多,否则请求到达Flyme云对象存储时可能已经超过签名的有效期限。
  • expirationPeriodInSeconds: 签名有效期限,从timestamp所指定的时间开始计算,单位为秒。

上述参数拼接:bce-auth-v1/{accessKeyId}/{timestamp}/{expirationPeriodInSeconds},组成了认证字符串前缀authStringPrefix。

任务二:创建规范请求(canonicalRequest),确定签名头域(signedHeaders)

CanonicalRequest的计算公式为: CanonicalRequest = HTTP Method + "\n" + CanonicalURI + "\n" + CanonicalQueryString + "\n" + CanonicalHeaders

从公式可以看出CanonicalRequestHTTP MethodCanonicalURICanonicalQueryStringCanonicalHeaders四部分组成,它们的具体含义及获取方式如下。

1. HTTP Method

指HTTP协议中定义的GET、PUT、POST等请求方法,必须使用全大写的形式。Flyme云对象存储API所涉及的HTTP Method有如下五种。

1. GET
2. POST
3. PUT
4. DELETE
5. HEAD

2. CanonicalURI

CanonicalURI是对URL中的绝对路径进行编码后的结果,即CanonicalURI = UriEncodeExceptSlash(Path)。要求绝对路径Path必须以“/”开头,不以“/”开头的需要补充上,空路径为“/”。函数UriEncodeExceptSlash的具体含义及功能请参考相关函数

  • 示例

若URL为https://fos.flymeyun.com/example/测试,则其URL Path为/example/测试,将之规范化得到CanonicalURI =UriEncodeExceptSlash(/example/测试)= /example/%E6%B5%8B%E8%AF%95

3. CanonicalQueryString

CanonicalQueryString是对于URL中的Query String(Query String即URL中“?”后面的“key1 = value1 & key2 = value2 ”字符串)进行编码后的结果。

编码步骤如下:

  1. 提取URL中的Query String项,即URL中“?”后面的“key1 = value1 & key2 = value2 ”字符串。
  2. 将Query String根据&分隔符拆开成若干项,每一项是key=value或者只有key的形式。
  3. 对拆开后的每一项进行编码处理,分以下三种情况。
    • 当该项的keyauthorization时,直接忽略该项。
    • 当该项只有key时,转换公式为UriEncode(key) + "="的形式。
    • 当该项是key=value的形式时,转换公式为 UriEncode(key) + "=" + UriEncode(value) 的形式。这里value可以是空字符串。
  4. 将每一项转换后的字符串按照字典顺序(ASCII码由小到大)排序,并使用& 符号连接起来,生成相应的CanonicalQueryString

编码示例:

获取URL为https://fos.flymeyun.com/example?text&text1=测试&text10=test的CanonicalQueryString。

  1. 提取URL中的Query String,得到 text&text1=测试&text10=test
  2. 根据&对Query String进行拆分,得到texttext1=测试text10=test三项。
  3. 对拆分的每一项进行编码。
    • text项进行编码:UriEncode("text") + "=",得到"text="
    • text1=测试项进行编码:UriEncode("text1") + "=" + UriEncode("测试"),得到"text1=%E6%B5%8B%E8%AF%95"
    • text10=test项进行编码:UriEncode("text10") + "=" + UriEncode("test"),得到"text10=test"
  4. 对上面三项编码后的字符串进行(按照ASCII码由小到大)排序,得到结果是text10=testtext1=%E6%B5%8B%E8%AF%95text= ,然后用&连接起来,得到CanonicalQueryString为text10=test&text1=%E6%B5%8B%E8%AF%95&text=

说明:

  1. 函数UriEncode的含义及功能请参考相关解释
  2. 示例中展示了如何处理只有key的项,非英文的value,以及数字和=进行排序。在实际的API中,因为参数起名是规范的,基本不会遇到这样的排序。正常的排序结果和只按照key进行排序是完全一致的。算法中有这个约束主要是出于定义严密性的考虑。

4. CanonicalHeaders

CanonicalHeaders是对HTTP请求中的Header部分进行选择性编码的结果。

在这个步骤中,可根据Header部分确定签名头域(signedHeaders)。签名头域是指签名算法中涉及到的HTTP头域列表。HTTP头域名字一律要求小写且头域名字之间用分号(;)分隔,如host;range;content-type。列表按照字典序排列。当前不支持x-fos开头的请求头,以及其他自定义的请求头。

编码步骤如下:

  1. 选择编码的Header。Flyme云对象存储API的要求是Host域进行编码。大多数情况下,我们推荐您对以下Header进行编码:
    • Host
    • Content-Length
    • Content-Type
    • Content-MD5

说明

a. 如果上述Header没有全部出现在您的HTTP请求里面,那么没有出现的部分无需进行编码。如果发送的请求里包含以上header,出现的header必须签名。
b. 您需要选择想要编码的Header。您必须在认证字符串中填写 {signedHeaders} 。填写方法为,把所有在这一阶段进行了编码的Header名字转换成全小写之后按照字典序排列,然后用分号(;)连接。 选择哪些Header进行编码不会影响API的功能,但是如果选择太少则可能遭到中间人攻击,Flyme云对象存储要求至少要包含Host域。

  1. 对Header进行编码获取CanonicalHeaders,编码步骤如下。

    a. 将Header的名字变成全小写,注意仅改名字。
    b. 将Header的值去掉开头和结尾的空白字符。 c. 经过上一步之后值为空字符串的Header忽略,其余的转换为 UriEncode(name) + ":" + UriEncode(value) 的形式。 d. 把上面转换后的所有字符串按照字典序进行排序。 e. 将排序后的字符串按顺序用\n符号连接起来得到最终的CanonicalHeaders

说明:

  1. UriEncode的含义及功能请参考相关函数说明。
  2. 很多发送HTTP请求的第三方库,会添加或者删除你指定的header(例如:某些库会删除content-length:0这个header),如果签名错误,请检查您真实发出的http请求的header,看看是否与签名时的header一样。

编码示例1:

如下是发送请求的Header:

Host: fos.flymeyun.com
Date: Mon, 27 Apr 2015 16:23:49 +0800
Content-Type: text/plain
Content-Length: 8
Content-Md5: NFzcPqhviddjRNnSOGo4rw==
x-fos-date: 2015-04-27T08:23:49Z
  1. 选择需要编码的Header,然后把所有名字都改为小写。
host: fos.flymeyun.com
date: Mon, 27 Apr 2015 16:23:49 +0800
content-type: text/plain
content-length: 8
content-md5: NFzcPqhviddjRNnSOGo4rw==
  1. 将Header的值去掉开头和结尾的空白字符。
host:fos.flymeyun.com
date:Mon, 27 Apr 2015 16:23:49 +0800
content-type:text/plain
content-length:8
content-md5:NFzcPqhviddjRNnSOGo4rw==
  1. 对每个Header进行UriEncode转换。
host:fos.flymeyun.com
date:Mon%2C%2027%20Apr%202015%2016%3A23%3A49%20%2B0800
content-type:text%2Fplain
content-length:8
content-md5:NFzcPqhviddjRNnSOGo4rw%3D%3D?
  1. 将步骤3中转换后的所有字符串按照字典序进行排序。
content-length:8
content-md5:NFzcPqhviddjRNnSOGo4rw%3D%3D?
content-type:text%2Fplain
date:Mon%2C%2027%20Apr%202015%2016%3A23%3A49%20%2B0800
host:fos.flymeyun.com
  1. 将排序后的字符串按顺序用\n符号连接起来得到最终结果。
content-length:8
content-md5:NFzcPqhviddjRNnSOGo4rw%3D%3D?
content-type:text%2Fplain
date:Mon%2C%2027%20Apr%202015%2016%3A23%3A49%20%2B0800
host:fos.flymeyun.com

同时获得认证字符串的signedHeaders内容应该是content-length;content-md5;content-type;date;host。

任务三:生成派生密钥(signingKey)

SigningKey = HMAC-SHA256-HEX(sk, authStringPrefix),其中:

  • HMAC-SHA256-HEX是HMAC SHA256算法函数,具体功能及描述参见相关函数说明
  • sk为用户的Secret Access Key,可以通过在控制台中进行查询,关于SK的获取方法,请参看获取AK/SK
  • authStringPrefix代表认证字符串的前缀部分,即:bce-auth-v1/{accessKeyId}/{timestamp}/{expirationPeriodInSeconds}

任务四:生成签名摘要及认证字符串(authorization)

通过上面的计算得到的SigningKey和CanonicalRequest按照下面公式可以得到签名。

Signature = HMAC-SHA256-HEX(SigningKey, CanonicalRequest)

其中:

  • HMAC-SHA256-HEX是HMAC SHA256算法函数,具体功能及描述参见相关函数说明。 最后由公式bce-auth-v1/{accessKeyId}/{timestamp}/{expirationPeriodInSeconds}/{signedHeaders}/{signature}得到认证字符串。

相关函数说明

函数名功能描述
HMAC-SHA256-HEX()调用HMAC SHA256算法,根据开发者提供的密钥(key)和密文(message)输出密文摘要,并把结果转换为小写形式的十六进制字符串。
Lowercase()将字符串全部变成小写。
Trim()去掉字符串开头和结尾的空白字符。
UriEncode()RFC 3986规定,"URI非保留字符"包括以下字符:字母(A-Z,a-z)、数字(0-9)、连字号(-)、点号(.)、下划线(_)、波浪线(~),算法实现如下:
1. 将字符串转换成UTF-8编码的字节流
2. 保留所有“URI非保留字符”原样不变
3. 对其余字节做一次RFC 3986中规定的百分号编码(Percent-encoding),即一个“%”后面跟着两个表示该字节值的十六进制字母,字母一律采用大写形式。
UriEncodeExceptSlash()与UriEncode() 类似,区别是斜杠(/)不做编码。一个简单的实现方式是先调用UriEncode(),然后把结果中所有的%2F都替换为/