AWS4 版本签名计算参考

  1. #!/usr/bin/env python3
  2. # -*- coding:utf-8 -*-
  3. # @Time: 2021/7/24 8:12
  4. # @Author:zhangmingda
  5. # @File: api_for_aws4_signature.py
  6. # @Software: PyCharm
  7. # Description:
  8.  
  9. from urllib.request import quote
  10. import hashlib
  11. import hmac
  12. import datetime
  13. import requests
  14. import json
  15. import base64
  16.  
  17. class KscClient(object):
  18. def __init__(self, ak, sk,service, domain, region, use_ssl=False):
  19. self.ak = ak
  20. self.sk = sk
  21. self.host = service + "." + domain
  22. self.region = region
  23. self.service = service
  24. self.protocol = 'https' if use_ssl else 'http'
  25. self.base_url = self.protocol + '://' + self.host + '/'
  26.  
  27. # 创建规范化请求查询字符串方法
  28. def generate_canonical_querystring(self, **params):
  29. '''
  30. :param params: 查询字符串字典
  31. :return: url编码后的查询字符串key1=value1&key2=value2 ...
  32. '''
  33. sorted_params = ''
  34. sorted_param_keys = sorted(params.keys())
  35. for param in sorted_param_keys:
  36. # print(param)
  37. sorted_params += quote(param) + '=' + quote(params.get(param), safe='') + '&' # 查询字符串中的值任何字符串都进行URL编码
  38. # print(sorted_params)
  39. return sorted_params[:-1]
  40.  
  41. # 生成标准化请求头,请求头大写转小写,排序返回字符串
  42. def generate_canonical_headers(self, **headers):
  43. '''
  44. :param headers: 所有用来签名的请求头
  45. :return: 返回小写请求头排序后的key:value 字符串,每个key:value 之间\n换行
  46. '''
  47. sorted_headers = '' # 准备标准请求头key+value字符串
  48. sorted_signed_headers = '' # 准备签名的请求头key组合字符串
  49. header_keys = headers.keys() # 获取请求头所有key
  50. lower_headers = {} # 准备存放小写请求头和值的字典
  51. for header in header_keys: # 把大写请求头字典都转换为小写请求头字典
  52. lower_headers[header.lower()] = headers.get(header)
  53. sorted_lower_header_keys = sorted(lower_headers.keys()) # 把签名的请求头排序
  54.  
  55. '''排序后的请求头和值进行组合字符串'''
  56. for lower_header in sorted_lower_header_keys:
  57. sorted_headers += lower_header + ':' + str(lower_headers[lower_header]).strip() + '\n'
  58. sorted_signed_headers += lower_header + ";"
  59. # print("============请求头排序==============")
  60. # print(sorted_headers)
  61. # print('------被签名的请求头----')
  62. # print(sorted_signed_headers)
  63. # print("==========请求头排序end============")
  64. sorted_lower_headers = {
  65. 'sorted_headers': sorted_headers, # 签名的请求头key 和 value
  66. 'signed_headers': sorted_signed_headers[:-1] # 被签名的请求头字符串组合,最后一个分号; 不要
  67. }
  68. return sorted_lower_headers
  69.  
  70. # 十六进制请求体Sha256Hash值--->请求体用
  71. def bytes_data_sha256_hex(self, bytes_data=''.encode()):
  72. '''
  73. :param binary_data: 字节码文件
  74. :return: 返回请求体内容的sha256 哈希值
  75. '''
  76. sha256 = hashlib.sha256()
  77. sha256.update(bytes_data)
  78. return sha256.hexdigest().lower()
  79.  
  80. # 构建被签名字符串,把标准请求canonical_request 哈希取16进制用
  81. def str_sha256_hex(self, string=''):
  82. '''
  83. :param string:
  84. :return: 字符串的sha256哈希值
  85. '''
  86. sha256 = hashlib.sha256()
  87. sha256.update(string.encode())
  88. return sha256.hexdigest()
  89.  
  90. # UTC时间字符串工具
  91. def get_utc_datetime(self):
  92. now_utc = datetime.datetime.utcnow()
  93. datetime_utc = now_utc.strftime('%Y%m%dT%H%M%SZ')
  94. date_utc = now_utc.strftime('%Y%m%d')
  95. return datetime_utc, date_utc
  96.  
  97. # 生成规范化请求的字符串方法
  98. def generate_canonical_request(self, method, encode_uri, canonical_querystring, canonical_headers, signed_headers,payload_sha256_hex):
  99. ''' 1.1 规范化请求CanonicalRequest计算方法'''
  100. return method + '\n' \
  101. + encode_uri + '\n' \
  102. + canonical_querystring + '\n' \
  103. + canonical_headers + '\n' \
  104. + signed_headers + '\n' \
  105. + payload_sha256_hex # 请求体不参与签名 时 : + 'UNSIGNED-PAYLOAD'
  106.  
  107. # 生成被签名字符串的方法
  108. def generate_string_tosign(self, algorithm, datetime_utc, credentialscope, canonical_reques_sha256_hex ):
  109. ''' 1.2 被签名字符串计算方法'''
  110. return algorithm + '\n' \
  111. + datetime_utc + '\n' \
  112. + credentialscope + '\n' \
  113. + canonical_reques_sha256_hex
  114.  
  115. # 第二步构建签名key的工具方法
  116. def encode_string_to_hmac_256_digest(self, encode_salt, msg, digestmod=hashlib.sha256):
  117. '''
  118. :param encode_salt: 盐
  119. :param msg: 字符串信息
  120. :param digestmod: 摘要算法
  121. :return: HMAC-SHA256 加盐哈希后的字节
  122. '''
  123. digest_maker = hmac.new(encode_salt, msg.encode(), digestmod=digestmod)
  124. return digest_maker.digest()
  125.  
  126. # 创建签名KEY的方法
  127. def generate_signing_key(self, signature_version,sk, date_utc, region, service, termchar='aws4_request'):
  128. k_secret = signature_version + sk
  129. k_date = self.encode_string_to_hmac_256_digest(k_secret.encode(), date_utc)
  130. k_region = self.encode_string_to_hmac_256_digest(k_date, region)
  131. k_service = self.encode_string_to_hmac_256_digest(k_region, service)
  132. k_signing = self.encode_string_to_hmac_256_digest(k_service, termchar)
  133. return k_signing
  134.  
  135. # 创建签名的方法
  136. def generate_signature(self, signing_key, string_tosign):
  137. digest_maker = hmac.new(signing_key, string_tosign.encode(), digestmod=hashlib.sha256)
  138. hex_signature = digest_maker.hexdigest()
  139. # print('hex_signature:', digest_maker.hexdigest())
  140. return hex_signature
  141.  
  142. # 构建带签名的请求头,或签名url查询字符串
  143. def get_auth_data(self, method, query_params, append_headers={}, binary_payload=''.encode()):
  144. # ========================================1. 创建被签名字符串======================================
  145. # 1.1 构建标准化请求
  146. method = method # 1.1.1 HTTP方法
  147. canonical_uri = '/' # 1.1.2 资源URI
  148. encode_uri = quote(canonical_uri, safe="/") # 1.1.2 将URI编码成%字符串格式 不包含? 后的url查询参数
  149. payload_sha256_hex = self.bytes_data_sha256_hex(binary_payload) # 1.1.5 请求体sha256 十六进制HASH值
  150. datetime_utc, date_utc = self.get_utc_datetime()
  151. # 准备请求头
  152. signature_headers = {
  153. 'Host': self.host,
  154. 'X-amz-date': datetime_utc
  155. }
  156. signature_headers.update(append_headers)
  157. # 请求头排序格式化
  158. sorted_lower_headers = self.generate_canonical_headers(**signature_headers)
  159. canonical_headers = sorted_lower_headers.get('sorted_headers') # 1.1.4 小写排序后的请求头key:value
  160. signed_headers = sorted_lower_headers.get('signed_headers') # 1.1.5 小写排序后的签名头
  161. # 1.2 创建信任状
  162. credentialscope = date_utc + "/" + self.region + "/" + self.service + "/aws4_request"
  163. # 查询参数字典
  164. auth_params = {}
  165. # 签名放在URL中时计算签名传递参数
  166. if query_params:
  167. auth_params = {
  168. 'X-Amz-Algorithm': "AWS4-HMAC-SHA256",
  169. 'X-Amz-Credential': self.ak + '/' + credentialscope,
  170. 'X-Amz-Date': datetime_utc,
  171. 'X-Amz-SignedHeaders': signed_headers,
  172. }
  173. auth_params.update(query_params)
  174. # print('auth_params: ', auth_params)
  175. auth_in_header_canonical_querystring = self.generate_canonical_querystring(**query_params) # 1.1.3 通过header传递签名的查询字符串
  176. auth_in_queryparam_canonical_querystring = self.generate_canonical_querystring(**auth_params) # 1.1.3 通过URL传递签名的查询字符串
  177. canonical_request_auth_in_header = self.generate_canonical_request(method, encode_uri, auth_in_header_canonical_querystring, canonical_headers, signed_headers, payload_sha256_hex)
  178. canonical_request_auth_in_url = self.generate_canonical_request(method, encode_uri, auth_in_queryparam_canonical_querystring, canonical_headers, signed_headers, payload_sha256_hex)
  179. print("Header传递签名的规范化请求".center(50, "="))
  180. print(canonical_request_auth_in_header)
  181. print("canonical_request_auth_in_url".center(50, "*"))
  182. print(canonical_request_auth_in_header)
  183. print("规范化化请求done".center(50, "="))
  184.  
  185. # 1.2 创建被签名字的符串StringToSign
  186. canonical_request_auth_in_header_sha256_hex = self.str_sha256_hex(canonical_request_auth_in_header) # 规范化请求的256哈希值
  187. canonical_request_auth_in_url_sha256_hex = self.str_sha256_hex(canonical_request_auth_in_url) # 规范化请求的256哈希值
  188. algorithm = 'AWS4-HMAC-SHA256'
  189. string_tosign_auth_in_header = self.generate_string_tosign(algorithm, datetime_utc, credentialscope, canonical_request_auth_in_header_sha256_hex)
  190. string_tosign_auth_in_url = self.generate_string_tosign(algorithm, datetime_utc, credentialscope, canonical_request_auth_in_url_sha256_hex)
  191.  
  192. print('Header传递签名被签名的字符串'.center(50, '='))
  193. print(string_tosign_auth_in_header)
  194. print('URL传递签名被签名的字符串'.center(50, '*'))
  195. print(string_tosign_auth_in_url)
  196. print('被签名的字符串Done!'.center(50,'='))
  197.  
  198. # ========================================2. 创建签名 ======================================
  199. # 2.1创建签名签名秘钥
  200. signature_version = 'AWS4'
  201. signing_key = self.generate_signing_key(signature_version, self.sk, date_utc, self.region, self.service)
  202. print("签名秘钥:",signing_key)
  203. # 2.2 创建签名
  204. signature_for_header = self.generate_signature(signing_key, string_tosign_auth_in_header)
  205. signature_for_url = self.generate_signature(signing_key, string_tosign_auth_in_url)
  206. print('Header鉴权的签名:', signature_for_header)
  207. print('url鉴权的签名:', signature_for_url)
  208. # ========================================3 请求头或URL 传递签名鉴权和参数 ======================================
  209. authorization_headers = {
  210. 'Authorization': "AWS4-HMAC-SHA256 Credential="
  211. + self.ak + '/'
  212. + credentialscope + ", "
  213. + "SignedHeaders="
  214. + signed_headers + ', '
  215. + 'Signature=' + signature_for_header,
  216. }
  217. # 3.1请求头携带签名,返回带签名的请求头
  218. authorization_headers.update(signature_headers)
  219.  
  220. # 3.2 url携带签名返回带签名的URL查询字符串
  221. request_query_params = {
  222. 'X-Amz-Signature': signature_for_url
  223. }
  224. request_query_params.update(auth_params)
  225. authorization_params = self.generate_canonical_querystring(**request_query_params)
  226. # print('request_query_params: ', request_query_params)
  227. # print('authorization_params:',authorization_params)
  228. auth_data = {
  229. 'authorization_headers': authorization_headers,
  230. 'authorization_params': request_query_params,
  231. 'signature_headers': signature_headers, # get object 在URL里面传递签名 请求头要带的签名头
  232. }
  233. return auth_data
  234.  
  235. # 通过标准请求头方式传递签名GET文件
  236. def get_test_auth_in_header(self, query_params={}, append_headers={}):
  237. method = 'GET' # 1.1.1 HTTP方法
  238. auth_data = self.get_auth_data(method, query_params, append_headers)
  239. authorization_headers = auth_data.get('authorization_headers')
  240. req = requests.get(url=self.base_url, headers=authorization_headers, params=query_params)
  241. print('Header鉴权的URL:', req.url)
  242. print(req.status_code)
  243. print(req.text)
  244.  
  245. def get_test_auth_in_query_param(self,query_params={}, append_headers={}):
  246. method = 'GET' # 1.1.1 HTTP方法
  247. auth_data = self.get_auth_data(method, query_params, append_headers)
  248. auth_query_params = auth_data.get('authorization_params')
  249. signature_headers = auth_data.get('signature_headers')
  250. canonical_querystring = self.generate_canonical_querystring(**auth_query_params)
  251.  
  252. # 通过URL传递鉴权=requests 传参方式1
  253. url_for_auth = self.base_url + "?" + canonical_querystring
  254. req2 = requests.get(url=url_for_auth, headers=signature_headers)
  255. print("手工拼接鉴权的URL:", url_for_auth)
  256. print(req2.status_code)
  257. print(req2.text)
  258. # 通过URL传递鉴权=requests 传参方式2
  259. req3 = requests.get(self.base_url, headers=signature_headers, params=auth_query_params)
  260. # req3 = requests.get(url=self.base_url, headers=signature_headers, params=canonical_querystring)
  261. print("requests自动拼接鉴权的URL:", req3.url)
  262. print(req3.status_code)
  263. print(req3.text)
  264.  
  265. if __name__ == '__main__':
  266. ak = 'XXXXXXXXXXXXXXX'
  267. sk = 'XXXXXXXXXXXXXXXXXXXXXXXXX'
  268. service = 'kcm'
  269. domain = 'api.ksyun.com'
  270. region = 'cn-beijing-6'
  271. ksc = KscClient(ak, sk, service, domain, region)
  272. query_params = {
  273. 'Action': 'GetDownloadLink',
  274. 'Version': '2016-03-04',
  275. 'CertificateId': 'kcm2021022216204501'
  276. }
  277. ksc.get_test_auth_in_header(query_params=query_params)
  278. # ksc.get_test_auth_in_query_param(query_params=query_params)

Header 传递鉴权测试

URL传递鉴权测试

Python 计算AWS4签名,Header鉴权与URL鉴权的更多相关文章

  1. python 计算校验和

    校验和是经常使用的,这里简单的列了一个针对按字节计算累加和的代码片段.其实,这种累加和的计算,将字节翻译为无符号整数和带符号整数,结果是一样的. 使用python计算校验和时记住做截断就可以了. 这里 ...

  2. [转载] python 计算字符串长度

    本文转载自: http://www.sharejs.com/codes/python/4843 python 计算字符串长度,一个中文算两个字符,先转换成utf8,然后通过计算utf8的长度和len函 ...

  3. Python计算斗牛游戏的概率

    Python计算斗牛游戏的概率 过年回家,都会约上亲朋好友聚聚会,会上经常会打麻将,斗地主,斗牛.在这些游戏中,斗牛是最受欢迎的,因为可以很多人一起玩,而且没有技术含量,都是看运气(专业术语是概率). ...

  4. 利用Python计算π的值,并显示进度条

    利用Python计算π的值,并显示进度条  第一步:下载tqdm 第二步;编写代码 from math import * from tqdm import tqdm from time import ...

  5. 用Python计算幂的两种方法,非递归和递归法

    用Python计算幂的两种方法: #coding:utf-8 #计算幂的两种方法.py #1.常规方法利用函数 #不使用递归计算幂的方法 """ def power(x, ...

  6. Python计算分位数

    Python计算分位数    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/gdkyxy2013/article/details/80911514 ...

  7. python 怎么模拟加header(如User-Agent、Content-Type等等)

    # -*- coding: cp936 -*- #python 27 #xiaodeng #python 怎么模拟加header(如User-Agent.Content-Type等等) #办法一: i ...

  8. 为了用python计算一个汉字的中心点,差点没绞尽脑汁活活累死

    为了用python计算一个汉字的中心点,差点没绞尽脑汁活活累死

  9. python计算时间差的方法

    本文实例讲述了python计算时间差的方法.分享给大家供大家参考.具体分析如下: 1.问题: 给定你两个日期,如何计算这两个日期之间间隔几天,几个星期,几个月,几年? 2.解决方法: 标准模块date ...

随机推荐

  1. HTML四种定位-固定定位

    固定定位 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset=&q ...

  2. Vue 中使用 TypeScript 详细总结

    VUE 项目中使用 Typescript 第一节:项目起步 Vue 中使用 TypeScript 项目中主要使用到的第三方依赖 vue2 vue-class-component vue-propert ...

  3. [Ocean Modelling for Begineers] Ch4. Long Waves in a Channel

    Ch4. Long Waves in a Channel 简介 本章主要介绍明渠中分层流体模拟.练习包括浅水表面波,风暴潮.内波和分层流体模拟. 4.1 有限差分法详细介绍 4.1.1 泰勒公式 4. ...

  4. sprint-boot 日志

    市面上的日志框架: JUL.JCL.Jboss-logging.logback.log4j.log4j2.slf4j.... SpringBoot:底层是Spring框架,Spring框架默认是用JC ...

  5. 【宏组学】如何根据taxid(或taxname)快速获得taxname(或taxid)?

    需求 我有一个物种taxonomy ID的list,想获得相应的物种名,不要一个个去NCBI Taxonomy官网查.反之根据物种名list查询对应的taxid. 实现 因为之前没怎么用过,我的第一个 ...

  6. mysql 实现某年单季度内的品牌TOPn销量在此年此单季度内销量占比

    数据表:       结果表: mysql语句:  

  7. Docker的基本使用及DockerFile的编写

    前言: 最近在准备面试,在复习到Docker相关内容时,想写一些东西分享给大家然后加深一下自己的印象,有了这篇随笔. Docker的简介: docker从文件系统.网络互连到进程隔离等等,极大的简化了 ...

  8. 使用 CliWrap 让C#中的命令行交互举重若轻

    在代码中进行命令行交互是一个很常见的场景, 特别是在一些CI CD 自动化流程中, 在这之前我们会使用 System.Diagnostics.Process API, 现在有一个更灵活的工具 CliW ...

  9. 学习java的第十六天

    一.今日收获 1.完成了手册第二章没有验证完成的例题 2.预习了第三章的算法以及for语句与if语句的用法 二.今日难题 1.验证上出现问题,没有那么仔细. 2.第二章还有没有完全理解的问题 三.明日 ...

  10. A Child's History of England.7

    After the death of Ethelbert, Edwin, King of Northumbria [公元616年,隋朝末年], who was such a good king tha ...