https://docs.ksyun.com/documents/6890
V4签名计算:
-
签名信息放在请求头中(纯脚本,非面向对象)
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # @Time: 2021/7/2 21:04 # @Author:zhangmingda # @File: test_v4_signature_get.py # @Software: PyCharm # Description: from urllib.request import quote import hashlib import hmac import datetime import requests ''' CanonicalRequest = HTTPRequestMethod + '\n' + CanonicalURI + '\n' + CanonicalQueryString + '\n' + CanonicalHeaders + '\n' + SignedHeaders + '\n' + HexEncode(Sha256Hash(RequestPayload)) ''' now_utc = datetime.datetime.utcnow() datetime_utc = now_utc.strftime('%Y%m%dT%H%M%SZ') date_utc = now_utc.strftime('%Y%m%d') hosts = { 'BEIJING': "ks3-cn-beijing.ksyun.com" } region = 'BEIJING' bucket = 'zhangmingda' ks3key = 'life-bbs/测试.txt' service = 'ks3' method = 'GET' host = hosts.get(region) # 生成标准化请求头 def generate_canonical_headers(**headers): ''' :param headers: 所有用来签名的请求头 :return: 返回小写请求头排序后的key:value 字符串,每个key:value 之间\n换行 ''' sorted_headers = '' # 准备标准请求头key+value字符串 sorted_signed_headers = '' # 准备签名的请求头key组合字符串 header_keys = headers.keys() # 获取请求头所有key lower_headers = {} # 准备存放小写请求头和值的字典 for header in header_keys: # 把大写请求头字典都转换为小写请求头字典 lower_headers[header.lower()] = headers.get(header) sorted_lower_header_keys = sorted(lower_headers.keys()) # 把签名的请求头排序 '''排序后的请求头和值进行组合字符串''' for lower_header in sorted_lower_header_keys: sorted_headers += lower_header + ':' + str(lower_headers[lower_header]).strip() + '\n' sorted_signed_headers += lower_header + ";" # print("============请求头排序==============") # print(sorted_headers) # print('------被签名的请求头----') # print(sorted_signed_headers) # print("==========请求头排序end============") sorted_lower_headers = { 'sorted_headers': sorted_headers, # 签名的请求头key 和 value 'signed_headers': sorted_signed_headers[:-1] # 被签名的请求头字符串组合,最后一个分号; 不要 } return sorted_lower_headers # 创建规范化请求查询字符串 def generate_canonical_querystring(**params): ''' :param params: 查询字符串字典 :return: url编码后的查询字符串key1=value1&key2=value2 ... ''' sorted_params = '' sorted_param_keys = sorted(params.keys()) for param in sorted_param_keys: sorted_params += quote(param) + '=' + quote(params.get(param), safe='') + '&' # 查询字符串中的值任何字符串都进行URL编码 return sorted_params[:-1] # 十六进制请求体Sha256Hash值--->请求体用 def binary_data_sha256_hex(binary_data=''.encode()): ''' :param args: :param forms: :return: 返回请求体内容的sha256 哈希值 ''' sha256 = hashlib.sha256() sha256.update(binary_data) return sha256.hexdigest().lower() # 构建被签名字符串,把标准请求canonical_request 哈希取16进制用 def str_sha256_hex(string=''): ''' :param string: :return: 字符串的sha256哈希值 ''' sha256 = hashlib.sha256() sha256.update(string.encode()) return sha256.hexdigest() # 第二步构建签名key用 def encode_string_to_hmac_256_digest(encode_salt, msg, digestmod=hashlib.sha256): ''' :param encode_salt: 盐 :param msg: 字符串信息 :param digestmod: 摘要算法 :return: HMAC-SHA256 加盐哈希后的字节 ''' digest_maker = hmac.new(encode_salt, msg.encode(), digestmod=digestmod) return digest_maker.digest() '''========================= 1. 准备被签名字符串 StringToSign计算==============================''' '''======== 1.1 规范化请求变量准备 ==========''' sign_method = method # 1.1.1 HTTP方法 canonical_uri = '/' + bucket + '/' + ks3key # 1.1.2 资源URI encode_uri = quote(canonical_uri, safe=";/?:@&=+$,") # 将URL编码成%字符串格式 query_params = {} canonical_querystring = generate_canonical_querystring(**query_params) # 1.1.3 查询字符串 payload_sha256_hex = binary_data_sha256_hex() # 1.1.5 请求体sha256 十六进制HASH值 signature_headers = { # 'x-kss-content-sha256': 'UNSIGNED-PAYLOAD', # 请求体不参与鉴权的时候,如此传参 'x-kss-content-sha256': payload_sha256_hex, 'Host': host, # 'X-kss-date': datetime_utc # 如果请求头含Date 字段,这里日期可以不参与签名计算,注释即可 } sorted_lower_headers = generate_canonical_headers(**signature_headers) canonical_headers = sorted_lower_headers.get('sorted_headers') # 1.1.4 小写排序后的请求头key:value signed_headers = sorted_lower_headers.get('signed_headers') # 1.1.5 小写排序后的签名头 ''' 1.1 规范化请求CanonicalRequest计算方法''' canonical_request = method + '\n'\ + encode_uri + '\n' \ + canonical_querystring + '\n' \ + canonical_headers + '\n' \ + signed_headers + '\n' \ + payload_sha256_hex # 请求体不参与签名 时 : + 'UNSIGNED-PAYLOAD' print("==========================规范化化请求=================================") print(canonical_request) print("==========================规范化请求done=================================") # 1.2 StringToSign algorithm = 'KSS4-HMAC-SHA256' requestdatetime = datetime_utc credentialscope = date_utc + "/" + region + "/" + service + "/kss4_request" canonical_request_sha256_hex = str_sha256_hex(canonical_request) string_tosign = algorithm + '\n'\ + requestdatetime + '\n'\ + credentialscope + '\n'\ + canonical_request_sha256_hex print('================被签名的字符串======================') print(string_tosign) print('================被签名的字符串done======================') # =========================第二步准备签名KEY================================= ''' kSecret = your Secret Key kDate = HMAC("KSS4" + kSecret, Date) kRegion = HMAC(kDate, Region) kService = HMAC(kRegion, Service) kSigning = HMAC(kService, "kss4_request") ''' # 创建签名KEY的方法 def generate_signing_key(sk, date_utc, region, service, termchar='kss4_request'): k_secret = "KSS4" + sk k_date = encode_string_to_hmac_256_digest(k_secret.encode(), date_utc) k_region = encode_string_to_hmac_256_digest(k_date, region) k_service = encode_string_to_hmac_256_digest(k_region, service) k_signing = encode_string_to_hmac_256_digest(k_service, termchar) return k_signing # 创建签名的方法 def generate_signature(signing_key, string_tosign): digest_maker = hmac.new(signing_key, string_tosign.encode(), digestmod=hashlib.sha256) hex_signature = digest_maker.hexdigest() # print('hex_signature:', digest_maker.hexdigest()) return hex_signature if __name__ == '__main__': ak = 'XXXXXXXXXXXX' sk = 'XXXXXXXXXXXXXXXX' signing_key = generate_signing_key(sk, date_utc, region, service) signature = generate_signature(signing_key, string_tosign) request_headers = { 'Authorization': "KSS4-HMAC-SHA256 Credential=" + ak + '/' + credentialscope + ", " + "SignedHeaders=" + signed_headers + ', ' + 'Signature=' + signature, 'Date': datetime_utc } request_headers.update(signature_headers) # 通过通过标准的 HTTP Authorization 头发送签名请求 url1 = "http://" + host + '/' + bucket + '/' + ks3key req = requests.get(url1, headers=request_headers) print(req.status_code) print(req.text) print(req.content.decode('GB2312'))
-
签名信息放在URL 查询字符串中(纯脚本,非面向对象)
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # @Time: 2021/7/2 21:04 # @Author:zhangmingda # @File: test_v4_signature_get.py # @Software: PyCharm # Description: from urllib.request import quote import hashlib import hmac import datetime import requests ''' CanonicalRequest = HTTPRequestMethod + '\n' + CanonicalURI + '\n' + CanonicalQueryString + '\n' + CanonicalHeaders + '\n' + SignedHeaders + '\n' + HexEncode(Sha256Hash(RequestPayload)) ''' now_utc = datetime.datetime.utcnow() datetime_utc = now_utc.strftime('%Y%m%dT%H%M%SZ') date_utc = now_utc.strftime('%Y%m%d') regions = { 'ks3-cn-beijing.ksyun.com': "BEIJING" } endpoint = 'ks3-cn-beijing.ksyun.com' host = endpoint region = regions.get(host) bucket = 'zhangmingda' ks3key = 'life-bbs/测试.txt' service = 'ks3' method = 'GET' ak = 'AKLTdwZzHs8UTY6ZiLcqCmJL1w' # 生成标准化请求头 def generate_canonical_headers(**headers): ''' :param headers: 所有用来签名的请求头 :return: 返回小写请求头排序后的key:value 字符串,每个key:value 之间\n换行 ''' sorted_headers = '' # 准备标准请求头key+value字符串 sorted_signed_headers = '' # 准备签名的请求头key组合字符串 header_keys = headers.keys() # 获取请求头所有key lower_headers = {} # 准备存放小写请求头和值的字典 for header in header_keys: # 把大写请求头字典都转换为小写请求头字典 lower_headers[header.lower()] = headers.get(header) sorted_lower_header_keys = sorted(lower_headers.keys()) # 把签名的请求头排序 '''排序后的请求头和值进行组合字符串''' for lower_header in sorted_lower_header_keys: sorted_headers += lower_header + ':' + str(lower_headers[lower_header]).strip() + '\n' sorted_signed_headers += lower_header + ";" # print("============请求头排序==============") # print(sorted_headers) # print('------被签名的请求头----') # print(sorted_signed_headers) # print("==========请求头排序end============") sorted_lower_headers = { 'sorted_headers': sorted_headers, # 签名的请求头key 和 value 'signed_headers': sorted_signed_headers[:-1] # 被签名的请求头字符串组合,最后一个分号; 不要 } return sorted_lower_headers # 创建规范化请求查询字符串 def generate_canonical_querystring(**params): ''' :param params: 查询字符串字典 :return: url编码后的查询字符串key1=value1&key2=value2 ... ''' sorted_params = '' sorted_param_keys = sorted(params.keys()) for param in sorted_param_keys: sorted_params += quote(param) + '=' + quote(params.get(param), safe='') + '&' # 查询字符串中的值任何字符串都进行URL编码 return sorted_params[:-1] # 十六进制请求体Sha256Hash值--->请求体用 def binary_data_sha256_hex(binary_data=''.encode()): ''' :param args: :param forms: :return: 返回请求体内容的sha256 哈希值 ''' sha256 = hashlib.sha256() sha256.update(binary_data) return sha256.hexdigest().lower() # 构建被签名字符串,把标准请求canonical_request 哈希取16进制用 def str_sha256_hex(string=''): ''' :param string: :return: 字符串的sha256哈希值 ''' sha256 = hashlib.sha256() sha256.update(string.encode()) return sha256.hexdigest() # 第二步构建签名key用 def encode_string_to_hmac_256_digest(encode_salt, msg, digestmod=hashlib.sha256): ''' :param encode_salt: 盐 :param msg: 字符串信息 :param digestmod: 摘要算法 :return: HMAC-SHA256 加盐哈希后的字节 ''' digest_maker = hmac.new(encode_salt, msg.encode(), digestmod=digestmod) return digest_maker.digest() '''========================= 1. 准备被签名字符串 StringToSign计算==============================''' '''======== 1.1 规范化请求变量准备 ==========''' sign_method = method # 1.1.1 HTTP方法 canonical_uri = '/' + bucket + '/' + ks3key # 1.1.2 资源URI encode_uri = quote(canonical_uri, safe="/") # 1.1.2 将URI编码成%字符串格式 不包含? 后的url查询参数 payload_sha256_hex = binary_data_sha256_hex() # 1.1.5 请求体sha256 十六进制HASH值 signature_headers = { 'Host': host, 'x-kss-content-sha256': payload_sha256_hex, } sorted_lower_headers = generate_canonical_headers(**signature_headers) canonical_headers = sorted_lower_headers.get('sorted_headers') # 1.1.4 小写排序后的请求头key:value signed_headers = sorted_lower_headers.get('signed_headers') # 1.1.5 小写排序后的签名头 credentialscope = date_utc + "/" + region + "/" + service + "/kss4_request" query_params = { 'X-Kss-Algorithm': "KSS4-HMAC-SHA256", 'X-Kss-Credential': ak + '/' + credentialscope, 'X-Kss-Date': datetime_utc, 'X-Kss-Expires': '3600', 'X-Kss-SignedHeaders': signed_headers, } canonical_querystring = generate_canonical_querystring(**query_params) # 1.1.3 查询字符串 ''' 1.1 规范化请求CanonicalRequest计算方法''' canonical_request = method + '\n'\ + encode_uri + '\n' \ + canonical_querystring + '\n' \ + canonical_headers + '\n' \ + signed_headers + '\n' \ + payload_sha256_hex # 请求体不参与签名 时 : + 'UNSIGNED-PAYLOAD' print("==========================规范化化请求=================================") print(canonical_request) print("==========================规范化请求done=================================") canonical_request_sha256_hex = str_sha256_hex(canonical_request) # 规范化请求的256哈希值 # 1.2 StringToSign algorithm = 'KSS4-HMAC-SHA256' requestdatetime = datetime_utc string_tosign = algorithm + '\n'\ + requestdatetime + '\n'\ + credentialscope + '\n'\ + canonical_request_sha256_hex print('================被签名的字符串======================') print(string_tosign) print('================被签名的字符串done======================') # =========================第二步准备签名KEY================================= ''' kSecret = your Secret Key kDate = HMAC("KSS4" + kSecret, Date) kRegion = HMAC(kDate, Region) kService = HMAC(kRegion, Service) kSigning = HMAC(kService, "kss4_request") ''' # 创建签名KEY的方法 def generate_signing_key(sk, date_utc, region, service, termchar='kss4_request'): k_secret = "KSS4" + sk k_date = encode_string_to_hmac_256_digest(k_secret.encode(), date_utc) k_region = encode_string_to_hmac_256_digest(k_date, region) k_service = encode_string_to_hmac_256_digest(k_region, service) k_signing = encode_string_to_hmac_256_digest(k_service, termchar) return k_signing # 创建签名的方法 def generate_signature(signing_key, string_tosign): digest_maker = hmac.new(signing_key, string_tosign.encode(), digestmod=hashlib.sha256) hex_signature = digest_maker.hexdigest() # print('hex_signature:', digest_maker.hexdigest()) return hex_signature if __name__ == '__main__': sk = 'OAXDdJoILGG2tD3tXDD8EVIiP7sOueqp2ylgBrg+nF/au0btfHcGF/Br80j4JFAiMQ==' signing_key = generate_signing_key(sk, date_utc, region, service) signature = generate_signature(signing_key, string_tosign) request_headers = signature_headers request_query_params = { 'X-Kss-Signature': signature } request_query_params.update(query_params) print(request_query_params) # 通过 URL QueryString 发送签名 # requests 用法1 url1 = "http://" + host + '/' + bucket + '/' + ks3key + '?' + generate_canonical_querystring(**request_query_params) print(url1) response = requests.get(url1, headers=request_headers) print(response.status_code) print(response.content.decode('gb2312')) # requests 用法2 url2 = "http://" + host + '/' + bucket + '/' + ks3key response2 = requests.get(url2, params=request_query_params, headers=request_headers) print(response2.status_code) print(response2.url)
输出:
D:\Python3_study\tornado1\Scripts\python.exe C:/Users/ZHANGMINGDA/Desktop/kingsoft/KS3/test_v4_signature_in_url_get.py ==========================规范化化请求================================= GET /zhangmingda/life-bbs/%E6%B5%8B%E8%AF%95.txt X-Kss-Algorithm=KSS4-HMAC-SHA256&X-Kss-Credential=AKLTdwZzHs8UTY6ZiLcqCmJL1w%2F20210704%2FBEIJING%2Fks3%2Fkss4_request&X-Kss-Date=20210704T010302Z&X-Kss-Expires=3600&X-Kss-SignedHeaders=host%3Bx-kss-content-sha256 host:ks3-cn-beijing.ksyun.com x-kss-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 host;x-kss-content-sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ==========================规范化请求done================================= ================被签名的字符串====================== KSS4-HMAC-SHA256 20210704T010302Z 20210704/BEIJING/ks3/kss4_request d4f17877ff6ced3b1df5fb7fbebef9933aa8d0698de653127a5d64ec92e523b1 ================被签名的字符串done====================== {'X-Kss-Signature': 'a0a071ae8fb5f6a9018fa2583bf082bea08acad6fbdb395a0a3f6bd69601fd80', 'X-Kss-Algorithm': 'KSS4-HMAC-SHA256', 'X-Kss-Credential': 'AKLTdwZzHs8UTY6ZiLcqCmJL1w/20210704/BEIJING/ks3/kss4_request', 'X-Kss-Date': '20210704T010302Z', 'X-Kss-Expires': '3600', 'X-Kss-SignedHeaders': 'host;x-kss-content-sha256'} http://ks3-cn-beijing.ksyun.com/zhangmingda/life-bbs/测试.txt?X-Kss-Algorithm=KSS4-HMAC-SHA256&X-Kss-Credential=AKLTdwZzHs8UTY6ZiLcqCmJL1w%2F20210704%2FBEIJING%2Fks3%2Fkss4_request&X-Kss-Date=20210704T010302Z&X-Kss-Expires=3600&X-Kss-Signature=a0a071ae8fb5f6a9018fa2583bf082bea08acad6fbdb395a0a3f6bd69601fd80&X-Kss-SignedHeaders=host%3Bx-kss-content-sha256 200 test 测试 200 http://ks3-cn-beijing.ksyun.com/zhangmingda/life-bbs/%E6%B5%8B%E8%AF%95.txt?X-Kss-Signature=a0a071ae8fb5f6a9018fa2583bf082bea08acad6fbdb395a0a3f6bd69601fd80&X-Kss-Algorithm=KSS4-HMAC-SHA256&X-Kss-Credential=AKLTdwZzHs8UTY6ZiLcqCmJL1w%2F20210704%2FBEIJING%2Fks3%2Fkss4_request&X-Kss-Date=20210704T010302Z&X-Kss-Expires=3600&X-Kss-SignedHeaders=host%3Bx-kss-content-sha256 Process finished with exit code 0