OBS API 鉴权实现宝典(上)—签名计算篇

举报
云技术搬运工 发表于 2020/09/15 11:07:22 2020/09/15
【摘要】 OBS提供了REST(Representational State Transfer)风格API,支持您通过HTTP/HTTPS请求调用。在调用OBS的API前,需要了解OBS的鉴权认证方式。本文就将带您了解OBS的两种常见的鉴权方式——Header携带签名和URL携带签名。

OBS提供了RESTRepresentational State Transfer)风格API,支持您通过HTTP/HTTPS请求调用。在调用OBSAPI前,需要了解OBS的鉴权认证方式。本文就将带您了解OBS的两种常见的鉴权方式——Header携带签名和URL携带签名。

1、Header中携带签名计算

官网链接:https://support.huaweicloud.com/api-obs/obs_04_0010.html

1.1、签名的计算原理和计算方法

原理图示

image.png

计算方法

1.构造请求字符串(StringToSign)。

请求字符串的构造方法如下:

StringToSign =

    HTTP-Verb + "\n" +

    Content-MD5 + "\n" +

    Content-Type + "\n" +

    Date + "\n" +

    CanonicalizedHeaders + CanonicalizedResource

2.对第一步的结果进行UTF-8编码。

3.使用SK对第二步的结果进行HMAC-SHA1签名计算。

4.对第三步的结果进行Base64编码,得到签名。

签名如以下形式(28位长度的BASE64编码的字符串):

JONydLd9zpf+Eu3IYiUjNmukHN0= 

计算示例

例:需要获取桶”obs-test”下的对象log.conf的对象ACL,如何构造请求并计算签名?

1、首先明确StringToSign的各字段:

请求方法:GET

请求MD5:空

Content-Type:空

请求时间:Tue, 28 Jul 2020 06:29:47 GMT(即北京时间202072814:29:47

自定义头域(CanonicalizedHeaders):空

规范化资源(CanonicalizedResource:/obs-test/log.conf?acl

2、构造请求字符串StringToSign如下:

StringToSign = ‘’’GET

 

 

Tue, 28 Jul 2020 06:29:47 GMT

/obs-test/log.conf?acl’’’

3、根据签名算法,将StringToSign进行HMAC-SHA1计算后进行BASE64编码获得签名结果:xYlcrwT9jSaCtY0OnBE01OBR+aA=

 

1.2、签名计算的实现方式

Python计算签名代码为例,供参考:

1.  import hashlib  

2.  import hmac  

3.  import binascii  

4.  from datetime import datetime  

5.    

6.  验证信息  

7.  AK = '您的access_key_id'  

8.  SK = '您的secret_access_key_id'  

9.    

10. 指定HTTP方法,可选GET/PUT/DELETE/POST/OPTIONS  

11. httpMethod = "GET"  

12.   

13. 指定请求的Header:Content-TypeContent-MD5  

14. contentType = ""  

15. conten**5 = ""  

16.   

17. 使用datetime库生成时间,如果需要自定义请求时间请保持格式一致  

18. date = datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')  

19.   

20. 填写canonicalizedHeaders  

21. # canonicalizedHeaders = "x-obs-acl:public-read"  

22. # canonicalizedHeaders = "x-obs-acl:public-read\n"+'x-obs-storage-class:WARM\n'  

23. canonicalizedHeaders = ""  

24.   

25. 填写CanonicalizedResource  

26. # CanonicalizedResource = "/BucketName/ObjectName"  

27. # CanonicalizedResource = "/BucketName/ObjectName?acl"  

28. # CanonicalizedResource = "/"

29. CanonicalizedResource = "/BucketName/"  

30.   

31. 生成StringToSign  

32. canonical_string = httpMethod + "\n" + conten**5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedHeaders + CanonicalizedResource  

33.   

34. 计算签名并进行BASE64编码  

35. hashed = hmac.new(SK.encode('UTF-8'), canonical_string.encode('UTF-8'), hashlib.sha1)  

36. encode_canonical = binascii.b2a_base64(hashed.digest())[:-1].decode('UTF-8')  

37.   

38. 打印StringToSign以便出现问题时进行验证  

39. print(canonical_string)  

40.   

41. 打印签名  

42. print(encode_canonical)  


C语言签名算法示例:

请参考https://obs-community.obs.cn-north-1.myhuaweicloud.com/sign/signature_c.zip,下载C语言签名计算代码样例,其中:

计算签名的接口包含在sign.h头文件中。

计算签名的示例代码在main.c文件中。

 

可视化签名计算工具:

您也可以通过OBS提供的可视化签名计算工具来计算签名。

工具链接:

https://obs-community.obs.cn-north-1.myhuaweicloud.com/sign/header_signature.html

image.png

 

说明:

1. canonicalizedHeaders:表示HTTP请求头域中的OBS请求头字段,即以“x-obs-”作为前辍的头域,如“x-obs-datex-obs-aclx-obs-meta-*”;

a.请求头字段中关键字的的所有字符要转为小写,需要添加多个字段时,要将所有字段按照关键字的字典序从小到大进行排序;

b.在添加请求头字段时,如果有重名的字段,则需要进行合并。如:x-obs-meta-name:name1x-obs-meta-name:name2,则需要先将重名字段的值(这里是name1name2)以逗号分隔,合并成x-obs-meta-name:name1,name2

c.头域中的请求头字段中的关键字不允许含有非ASCII码或不可识别字符;请求头字段中的值也不建议使用非ASCII码或不可识别字符,如果一定要使用非ASCII码或不可识别字符,需要客户端自行做编解码处理,可以采用URL编码或者Base64编码,服务端不会做解码处理;

d.当请求头字段中含有无意义空格或table键时,需要摒弃。例如:x-obs-meta-name: namename前带有一个无意义空格),需要转换为:x-obs-meta-name:name

e.每一个请求头字段最后都需要另起新行。

 

2. canonicalizedResource表示HTTP请求所指定的OBS资源,构造方式如下:

<桶名+对象名>+[子资源] …

a.通过桶绑定的自定义域名访问OBS,桶名由自定义域名表示,则为"/obs.ccc.com/object",其中“obs.ccc.com”为桶绑定的自定义域名。如果没有对象名,如列举桶,则为"/obs.ccc.com/"

b.不是通过桶绑定的自定义域名访问OBS的场景,则为"/bucket/object",如果没有对象名,如列举桶,则为"/bucket/"。如果桶名也没有,则为“/”;

c.如果有子资源,则将子资源添加进来,例如?acl?logging

 

3. 如需要使用临时AK/SK+SecurityToken的方式计算签名,计算签名的方法保持一致,但需要在头域中添加“x-obs-security-token:…”字段。

 

4.计算Content-MD5的方法见文末的说明。

 

5.其他语言计算签名的代码可详见对应语言的SDK,详见:

https://support.huaweicloud.com/sdkreference-obs/obs_02_0001.html


1.3、常见问题

1.访问OBS时报错:Signature Does Not Match

签名不匹配的情况主要有以下两种可能:

a.您没有使用正确的AK/SK,您可以检查您计算签名使用的SK和发送请求时所携带的AK是否正确且匹配;

b.您计算签名时构造的StringToSign和服务端根据接收到的HTTP请求所计算的StringToSign不匹配,您可以检查服务端返回的StringToSign,并与本地计算签名所使用的StringToSign进行对比。

如下图是服务端返回的由接收到HTTP请求所还原的StringToSign,您可以通过对比您本地的StringToSign和您发送到服务端的HTTP请求,来分析您签名计算失败的原因。

image.png

2.访问OBS时报错:Request has expired

此类情况请您检查您携带的Date是否正确,为保证请求的时效性,您所携带的Date头域必须与服务端的时间相差在15分钟以内(服务端为UTC时间),如您携带了x-obs-date头域,需检查x-obs-date的时间是否与服务端时间相差15分钟以内。

2、在URL中携带签名

OBS服务支持用户构造一个特定操作的URL,这个URL中会包含用户AK、签名、有效期、资源等信息,任何拿到这个URL的人均可执行这个操作,OBS服务收到这个请求后认为该请求就是签发URL用户自己在执行操作。例如构造一个携带签名信息的下载对象的URL,拿到相应URL的人能下载这个对象,但该URL只在Expires指定的失效时间内有效。URL中携带签名主要用于在不提供给其他人Secret Access Key的情况下,让其他人能用预签发的URL来进行身份认证,并执行预定义的操作。

官网链接https://support.huaweicloud.com/api-obs/obs_04_0011.html

2.1、签名的计算原理和计算方法

原理图示

image.png

计算方法

1.构造请求字符串(StringToSign)。

请求字符串的构造方法如下:

StringToSign =

    HTTP-Verb + "\n" +

    Content-MD5 + "\n" +

    Content-Type + "\n" +

    Date + "\n" +

    CanonicalizedHeaders + CanonicalizedResource


2.对第一步的结果进行UTF-8编码。

3.使用SK对第二步的结果进行HMAC-SHA1签名计算。

4.对第三步的结果进行Base64编码,得到签名。

签名如以下形式(28位长度的BASE64编码的字符串):

JONydLd9zpf+Eu3IYiUjNmukHN0=


URL中携带的签名计算方法同Header中携带签名的签名计算方法,但是需要将Date更换为UNIX时间戳。

image.png


携带签名的URL形式如下:

obs-ycytest.obs.cn-north-1.myhuaweicloud.com/?AccessKeyId=YN97UCJEKF2ALJ44AHAN&Expires=1575452568&Signature=0wG/GF7XgmOatCFhwHJh0J6NrtQ=

其对应的StringToSign

GET


1575452568

/obs-ycytest/

URL中携带的参数具体含义见下表:

参数名称

描述

是否必选

AccessKeyId

签发者的AK信息。OBS根据AK确定签发者的身份,并认为URL就是签发者在访问。

Expires

临时授权失效的时间;临时授权失效的时间为24小时,非临时的授权时间可设置的最长时间为1年,但是如果含有了临时的AK,其授权失效时间最大值也只能为24小时;UTC时间,197011日零时之后的指定的Expires时间内有效(以秒为单位)。

Signature

根据用户SKExpires等参数计算出的签名信息。

x-obs-security-token

使用临时AK/SK鉴权时,临时AK/SKsecuritytoken必须同时使用,请求头中需要添加“x-obs-security-token”字段

 

计算示例

例:需要获取桶”obs-test”下的对象log.conf的对象ACL,如何构造请求并计算签名?

1、首先明确StringToSign的各字段:

请求方法:GET

请求MD5:空

Content-Type:空

请求时间:1595918661(即北京时间202072814:44:21

自定义头域(CanonicalizedHeaders):空

规范化资源(CanonicalizedResource:/obs-test/log.conf?acl

2、构造请求字符串StringToSign如下:

StringToSign = ‘’’GET

 

 

1595918661

/obs-test/log.conf?acl’’’

3、根据签名算法,将StringToSign进行HMAC-SHA1计算后进行BASE64编码获得签名结果:lLcYw1fFMJv5m+MS0XenNrqJlag=

根据计算的结果,将URL拼接起来即可生成携带签名的URL如下:

obs-test.obs.myhuaweicloud.com/log.conf?AccessKeyId=xxx&acl&Expires=1595918661&Signature= lLcYw1fFMJv5m+MS0XenNrqJlag=

 

2.2、签名计算的实现方式

URL中携带签名时,只需将Date替换为UNIX时间戳即可计算对应的签名,因此对应的代码不再赘述。

如需要使用临时AK/SK+SecurityToken的方式计算签名,计算签名的方法保持一致,但需要在对应的CanonicalizedResource中添加“?x-obs-security-token=…”字段,且计算得到的签名必须要进行URL编码。使用临时AK/SK+SecurityToken计算签名的代码如下:

1.  import hashlib  

2.  import hmac  

3.  import binascii  

4.  import urllib.request  

5.    

6.  AK = 'Input Your AccessKeyId'  

7.  SK = 'Input Your SecretKeyId'  

8.  Token = 'Input Your SecurityToken'  

9.    

10. httpMethod = "GET"  

11. contentType = ""  

12. Conten**5 = ''  

13. date = '1594972984'  

14. canonicalizedHeaders = ''  

15. CanonicalizedResource = "/messageflow/flowengine.tar.gz" + "?x-obs-security-token=" + Token  

16. canonical_string = httpMethod + "\n" + Conten**5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedHeaders + CanonicalizedResource  

17. hashed = hmac.new(SK.encode('UTF-8'), canonical_string.encode('UTF-8'), hashlib.sha1)  

18. encode_canonical = binascii.b2a_base64(hashed.digest())[:-1].decode('UTF-8')  

19.   

20. url= 'messageflow.obs.myhuaweicloud.com/flowengine.tar.gz?x-obs-security-token={}&Expires={}&AccessKeyId={}&Signature={}'.format(Token, date, AK, urllib.request.quote(encode_canonical))  

21. print(url)  


说明:

1.在计算签名时,Date表示的是一个UNIX时间戳;

2.如果想要在浏览器中使用URL中携带签名生成的预定于URL,则计算签名时不要使用“Content-MD5”、“Content-Type”、“CanonicalizedHeaders”计算签名,否则浏览器不能携带这些参数,请求发送到服务端之后,会提示签名错误。

 

3、Content-MD5的计算方式

3.1、Content-MD5的计算方法

以消息内容“0123456789”为例,以下详细说明计算该字符串的Content-MD5的方法。

1.先计算MD5加密的二进制数组(128位)。

2.对这个二进制数组进行base64编码(而不是对32位字符串编码)。

Python为例:

>>> import base64,hashlib

>>> hash = hashlib.md5()

>>> hash.update("0123456789".encode(‘utf-8’))

>>> base64.b64encode(hash.digest())

'eB5eJF1ptWaXm4bijSPyxw=='

注:hash.digest(),计算出二进制数组(128位)。

>>> hash.digest()

'x\x1e^$]i\xb5f\x97\x9b\x86\xe2\x8d#\xf2\xc7'


3.2、Content-MD5计算的实现

以Python计算文件MD5代码为例,供参考:

1.  import os  

2.  import base64  

3.  import hashlib  

4.    

5.    

6.  def md5_file_encode_by_size_offset(file_path=None, size=None, offset=None, chuckSize=None):  

7.      if file_path is not None and size is not None and offset is not None:  

8.          m = hashlib.md5()  

9.          with open(file_path, 'rb') as fp:  

10.             CHUNKSIZE = 65536 if chuckSize is None else chuckSize  

11.             fp.seek(offset)  

12.             read_count = 0  

13.             while read_count < size:  

14.                 read_size = CHUNKSIZE if size - read_count >= CHUNKSIZE else size - read_count  

15.                 data = fp.read(read_size)  

16.                 read_count_once = len(data)  

17.                 if read_count_once <= 0:  

18.                     break  

19.                 m.update(data)  

20.                 read_count += read_count_once  

21.         return base64.b64encode(m.digest()).decode()  

22.   

23.   

24. file_path = r'Input Your File Path'  

25. size = os.path.getsize(file_path)  

26. Conten**5 = md5_file_encode_by_size_offset(file_path=file_path, size=size, offset=0)  

27. print(Conten**5)  


3.3、常见问题

常见错误是直接对计算出的32位字符串进行base64编码。

# hash.hexdigest(),计算得到可见的32位字符串编码。

>>> import base64,hashlib

>>> hash = hashlib.md5()

>>> hash.update("0123456789".encode(‘utf-8’))

>>> hash.hexdigest()

'781e5e245d69b566979b86e28d23f2c7'

错误的MD5值进行base64编码后的结果:

>>> base64.b64encode(hash.hexdigest())

'NzgxZTVlMjQ1ZDY5YjU2Njk3OWI4NmUyOGQyM2YyYzc='



系列文章推荐

OBS API 鉴权实现宝典(中)—POST签名计算

OBS API 鉴权实现宝典(下)—OBS API实战篇



【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

举报
请填写举报理由
0/200