摘要:本文就将带您了解OBS的两种常见的鉴权方式——Header携带签名和URL携带签名。
OBS提供了REST(Representational State Transfer)风格API,支持您经过HTTP/HTTPS请求调用。在调用OBS的API前,须要了解OBS的鉴权认证方式。本文就将带您了解OBS的两种常见的鉴权方式——Header携带签名和URL携带签名。html
官网连接:https://support.huaweicloud.com/api-obs/obs_04_0010.html算法
原理图示segmentfault
计算方法api
(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编码的字符串):url
JONydLd9zpf+Eu3IYiUjNmukHN0=
计算示例
例:须要获取桶”obs-test”下的对象log.conf的对象ACL,如何构造请求并计算签名?
一、首先明确StringToSign的各字段:
请求方法:GET;
请求MD5:空
Content-Type:空
请求时间:Tue, 28 Jul 2020 06:29:47 GMT(即北京时间2020年7月28日14:29:47)
自定义头域(CanonicalizedHeaders):空
规范化资源(CanonicalizedResource):/obs-test/log.conf?acl
二、构造请求字符串StringToSign以下:
StringToSign = ‘’’GET Tue, 28 Jul 2020 06:29:47 GMT /obs-test/log.conf?acl’’’
三、根据签名算法,将StringToSign进行HMAC-SHA1计算后进行BASE64编码得到签名结果:xYlcrwT9jSaCtY0OnBE01OBR+aA=
以Python计算签名代码为例,供参考:
import hashlib import hmac import binascii from datetime import datetime # 验证信息 AK = '您的access_key_id' SK = '您的secret_access_key_id' # 指定HTTP方法,可选GET/PUT/DELETE/POST/OPTIONS httpMethod = "GET" # 指定请求的Header:Content-Type和Content-MD5 contentType = "" conten**5 = "" # 使用datetime库生成时间,若是须要自定义请求时间请保持格式一致 date = datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT') # 填写canonicalizedHeaders # canonicalizedHeaders = "x-obs-acl:public-read" # canonicalizedHeaders = "x-obs-acl:public-read\n"+'x-obs-storage-class:WARM\n' canonicalizedHeaders = "" # 填写CanonicalizedResource # CanonicalizedResource = "/BucketName/ObjectName" # CanonicalizedResource = "/BucketName/ObjectName?acl" # CanonicalizedResource = "/" CanonicalizedResource = "/BucketName/" # 生成StringToSign canonical_string = httpMethod + "\n" + conten**5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedHeaders + CanonicalizedResource # 计算签名并进行BASE64编码 hashed = hmac.new(SK.encode('UTF-8'), canonical_string.encode('UTF-8'), hashlib.sha1) encode_canonical = binascii.b2a_base64(hashed.digest())[:-1].decode('UTF-8') # 打印StringToSign以便出现问题时进行验证 print(canonical_string) # 打印签名 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
说明:
1. canonicalizedHeaders:表示HTTP请求头域中的OBS请求头字段,即以“x-obs-”做为前辍的头域,如“x-obs-date,x-obs-acl,x-obs-meta-*”;
a.请求头字段中关键字的的全部字符要转为小写,须要添加多个字段时,要将全部字段按照关键字的字典序从小到大进行排序;
b.在添加请求头字段时,若是有重名的字段,则须要进行合并。如:x-obs-meta-name:name1和x-obs-meta-name:name2,则须要先将重名字段的值(这里是name1和name2)以逗号分隔,合并成x-obs-meta-name:name1,name2;
c.头域中的请求头字段中的关键字不容许含有非ASCII码或不可识别字符;请求头字段中的值也不建议使用非ASCII码或不可识别字符,若是必定要使用非ASCII码或不可识别字符,须要客户端自行作编解码处理,能够采用URL编码或者Base64编码,服务端不会作解码处理;
d.当请求头字段中含有无心义空格或table键时,须要摒弃。例如:x-obs-meta-name: name(name前带有一个无心义空格),须要转换为: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.访问OBS时报错:Signature Does Not Match
签名不匹配的状况主要有如下两种可能:
a.您没有使用正确的AK/SK,您能够检查您计算签名使用的SK和发送请求时所携带的AK是否正确且匹配;
b.您计算签名时构造的StringToSign和服务端根据接收到的HTTP请求所计算的StringToSign不匹配,您能够检查服务端返回的StringToSign,并与本地计算签名所使用的StringToSign进行对比。
以下图是服务端返回的由接收到HTTP请求所还原的StringToSign,您能够经过对比您本地的StringToSign和您发送到服务端的HTTP请求,来分析您签名计算失败的缘由。
2.访问OBS时报错:Request has expired
此类状况请您检查您携带的Date是否正确,为保证请求的时效性,您所携带的Date头域必须与服务端的时间相差在15分钟之内(服务端为UTC时间),如您携带了x-obs-date头域,需检查x-obs-date的时间是否与服务端时间相差15分钟之内。
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
原理图示
计算方法
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时间戳。
携带签名的URL形式以下:
obs-ycytest.obs.cn-north-1.myhuaweicloud.com/?AccessKeyId=YN97UCJEKF2ALJ44AHAN&Expires=1575452568&Signature=0wG/GF7XgmOatCFhwHJh0J6NrtQ=
其对应的StringToSign为
GET 1575452568 /obs-ycytest/
URL中携带的参数具体含义见下表:
计算示例
例:须要获取桶”obs-test”下的对象log.conf的对象ACL,如何构造请求并计算签名?
一、首先明确StringToSign的各字段:
请求方法:GET; 请求MD5:空 Content-Type:空 请求时间:1595918661(即北京时间2020年7月28日14:44:21) 自定义头域(CanonicalizedHeaders):空 规范化资源(CanonicalizedResource):/obs-test/log.conf?acl
二、构造请求字符串StringToSign以下:
StringToSign = ‘’’GET 1595918661 /obs-test/log.conf?acl’’’
三、根据签名算法,将StringToSign进行HMAC-SHA1计算后进行BASE64编码得到签名结果:lLcYw1fFMJv5m+MS0XenNrqJlag=
根据计算的结果,将URL拼接起来便可生成携带签名的URL以下:
obs-test.obs.myhuaweicloud.com/log.conf?AccessKeyId=xxx&acl&Expires=1595918661&Signature= lLcYw1fFMJv5m+MS0XenNrqJlag=
在URL中携带签名时,只需将Date替换为UNIX时间戳便可计算对应的签名,所以对应的代码再也不赘述。
如须要使用临时AK/SK+SecurityToken的方式计算签名,计算签名的方法保持一致,但须要在对应的CanonicalizedResource中添加“?x-obs-security-token=…”字段,且计算获得的签名必需要进行URL编码。使用临时AK/SK+SecurityToken计算签名的代码以下:
import hashlib import hmac import binascii import urllib.request AK = 'Input Your AccessKeyId' SK = 'Input Your SecretKeyId' Token = 'Input Your SecurityToken' httpMethod = "GET" contentType = "" Conten**5 = '' date = '1594972984' canonicalizedHeaders = '' CanonicalizedResource = "/messageflow/flowengine.tar.gz" + "?x-obs-security-token=" + Token canonical_string = httpMethod + "\n" + Conten**5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedHeaders + CanonicalizedResource hashed = hmac.new(SK.encode('UTF-8'), canonical_string.encode('UTF-8'), hashlib.sha1) encode_canonical = binascii.b2a_base64(hashed.digest())[:-1].decode('UTF-8') url= 'messageflow.obs.myhuaweicloud.com/flowengine.tar.gz?x-obs-security-token={}&Expires={}&AccessKeyId={}&Signature={}'.format(Token, date, AK, urllib.request.quote(encode_canonical)) print(url)
说明:
1.在计算签名时,Date表示的是一个UNIX时间戳;
2.若是想要在浏览器中使用URL中携带签名生成的预约于URL,则计算签名时不要使用“Content-MD5”、“Content-Type”、“CanonicalizedHeaders”计算签名,不然浏览器不能携带这些参数,请求发送到服务端以后,会提示签名错误。
以消息内容“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'
以Python计算文件MD5代码为例,供参考:
import os import base64 import hashlib def md5_file_encode_by_size_offset(file_path=None, size=None, offset=None, chuckSize=None): if file_path is not None and size is not None and offset is not None: m = hashlib.md5() with open(file_path, 'rb') as fp: CHUNKSIZE = 65536 if chuckSize is None else chuckSize fp.seek(offset) read_count = 0 while read_count < size: read_size = CHUNKSIZE if size - read_count >= CHUNKSIZE else size - read_count data = fp.read(read_size) read_count_once = len(data) if read_count_once <= 0: break m.update(data) read_count += read_count_once return base64.b64encode(m.digest()).decode() file_path = r'Input Your File Path' size = os.path.getsize(file_path) Conten**5 = md5_file_encode_by_size_offset(file_path=file_path, size=size, offset=0) print(Conten**5)
常见错误是直接对计算出的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='