AWS4 版本签名计算参考

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# @Time: 2021/7/24 8:12
# @Author:zhangmingda
# @File: api_for_aws4_signature.py
# @Software: PyCharm
# Description: from urllib.request import quote
import hashlib
import hmac
import datetime
import requests
import json
import base64 class KscClient(object):
def __init__(self, ak, sk,service, domain, region, use_ssl=False):
self.ak = ak
self.sk = sk
self.host = service + "." + domain
self.region = region
self.service = service
self.protocol = 'https' if use_ssl else 'http'
self.base_url = self.protocol + '://' + self.host + '/' # 创建规范化请求查询字符串方法
def generate_canonical_querystring(self, **params):
'''
:param params: 查询字符串字典
:return: url编码后的查询字符串key1=value1&key2=value2 ...
'''
sorted_params = ''
sorted_param_keys = sorted(params.keys())
for param in sorted_param_keys:
# print(param)
sorted_params += quote(param) + '=' + quote(params.get(param), safe='') + '&' # 查询字符串中的值任何字符串都进行URL编码
# print(sorted_params)
return sorted_params[:-1] # 生成标准化请求头,请求头大写转小写,排序返回字符串
def generate_canonical_headers(self, **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 # 十六进制请求体Sha256Hash值--->请求体用
def bytes_data_sha256_hex(self, bytes_data=''.encode()):
'''
:param binary_data: 字节码文件
:return: 返回请求体内容的sha256 哈希值
'''
sha256 = hashlib.sha256()
sha256.update(bytes_data)
return sha256.hexdigest().lower() # 构建被签名字符串,把标准请求canonical_request 哈希取16进制用
def str_sha256_hex(self, string=''):
'''
:param string:
:return: 字符串的sha256哈希值
'''
sha256 = hashlib.sha256()
sha256.update(string.encode())
return sha256.hexdigest() # UTC时间字符串工具
def get_utc_datetime(self):
now_utc = datetime.datetime.utcnow()
datetime_utc = now_utc.strftime('%Y%m%dT%H%M%SZ')
date_utc = now_utc.strftime('%Y%m%d')
return datetime_utc, date_utc # 生成规范化请求的字符串方法
def generate_canonical_request(self, method, encode_uri, canonical_querystring, canonical_headers, signed_headers,payload_sha256_hex):
''' 1.1 规范化请求CanonicalRequest计算方法'''
return method + '\n' \
+ encode_uri + '\n' \
+ canonical_querystring + '\n' \
+ canonical_headers + '\n' \
+ signed_headers + '\n' \
+ payload_sha256_hex # 请求体不参与签名 时 : + 'UNSIGNED-PAYLOAD' # 生成被签名字符串的方法
def generate_string_tosign(self, algorithm, datetime_utc, credentialscope, canonical_reques_sha256_hex ):
''' 1.2 被签名字符串计算方法'''
return algorithm + '\n' \
+ datetime_utc + '\n' \
+ credentialscope + '\n' \
+ canonical_reques_sha256_hex # 第二步构建签名key的工具方法
def encode_string_to_hmac_256_digest(self, 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() # 创建签名KEY的方法
def generate_signing_key(self, signature_version,sk, date_utc, region, service, termchar='aws4_request'):
k_secret = signature_version + sk
k_date = self.encode_string_to_hmac_256_digest(k_secret.encode(), date_utc)
k_region = self.encode_string_to_hmac_256_digest(k_date, region)
k_service = self.encode_string_to_hmac_256_digest(k_region, service)
k_signing = self.encode_string_to_hmac_256_digest(k_service, termchar)
return k_signing # 创建签名的方法
def generate_signature(self, 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 # 构建带签名的请求头,或签名url查询字符串
def get_auth_data(self, method, query_params, append_headers={}, binary_payload=''.encode()):
# ========================================1. 创建被签名字符串======================================
# 1.1 构建标准化请求
method = method # 1.1.1 HTTP方法
canonical_uri = '/' # 1.1.2 资源URI
encode_uri = quote(canonical_uri, safe="/") # 1.1.2 将URI编码成%字符串格式 不包含? 后的url查询参数
payload_sha256_hex = self.bytes_data_sha256_hex(binary_payload) # 1.1.5 请求体sha256 十六进制HASH值
datetime_utc, date_utc = self.get_utc_datetime()
# 准备请求头
signature_headers = {
'Host': self.host,
'X-amz-date': datetime_utc
}
signature_headers.update(append_headers)
# 请求头排序格式化
sorted_lower_headers = self.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.2 创建信任状
credentialscope = date_utc + "/" + self.region + "/" + self.service + "/aws4_request"
# 查询参数字典
auth_params = {}
# 签名放在URL中时计算签名传递参数
if query_params:
auth_params = {
'X-Amz-Algorithm': "AWS4-HMAC-SHA256",
'X-Amz-Credential': self.ak + '/' + credentialscope,
'X-Amz-Date': datetime_utc,
'X-Amz-SignedHeaders': signed_headers,
}
auth_params.update(query_params)
# print('auth_params: ', auth_params)
auth_in_header_canonical_querystring = self.generate_canonical_querystring(**query_params) # 1.1.3 通过header传递签名的查询字符串
auth_in_queryparam_canonical_querystring = self.generate_canonical_querystring(**auth_params) # 1.1.3 通过URL传递签名的查询字符串
canonical_request_auth_in_header = self.generate_canonical_request(method, encode_uri, auth_in_header_canonical_querystring, canonical_headers, signed_headers, payload_sha256_hex)
canonical_request_auth_in_url = self.generate_canonical_request(method, encode_uri, auth_in_queryparam_canonical_querystring, canonical_headers, signed_headers, payload_sha256_hex)
print("Header传递签名的规范化请求".center(50, "="))
print(canonical_request_auth_in_header)
print("canonical_request_auth_in_url".center(50, "*"))
print(canonical_request_auth_in_header)
print("规范化化请求done".center(50, "=")) # 1.2 创建被签名字的符串StringToSign
canonical_request_auth_in_header_sha256_hex = self.str_sha256_hex(canonical_request_auth_in_header) # 规范化请求的256哈希值
canonical_request_auth_in_url_sha256_hex = self.str_sha256_hex(canonical_request_auth_in_url) # 规范化请求的256哈希值
algorithm = 'AWS4-HMAC-SHA256'
string_tosign_auth_in_header = self.generate_string_tosign(algorithm, datetime_utc, credentialscope, canonical_request_auth_in_header_sha256_hex)
string_tosign_auth_in_url = self.generate_string_tosign(algorithm, datetime_utc, credentialscope, canonical_request_auth_in_url_sha256_hex) print('Header传递签名被签名的字符串'.center(50, '='))
print(string_tosign_auth_in_header)
print('URL传递签名被签名的字符串'.center(50, '*'))
print(string_tosign_auth_in_url)
print('被签名的字符串Done!'.center(50,'=')) # ========================================2. 创建签名 ======================================
# 2.1创建签名签名秘钥
signature_version = 'AWS4'
signing_key = self.generate_signing_key(signature_version, self.sk, date_utc, self.region, self.service)
print("签名秘钥:",signing_key)
# 2.2 创建签名
signature_for_header = self.generate_signature(signing_key, string_tosign_auth_in_header)
signature_for_url = self.generate_signature(signing_key, string_tosign_auth_in_url)
print('Header鉴权的签名:', signature_for_header)
print('url鉴权的签名:', signature_for_url)
# ========================================3 请求头或URL 传递签名鉴权和参数 ======================================
authorization_headers = {
'Authorization': "AWS4-HMAC-SHA256 Credential="
+ self.ak + '/'
+ credentialscope + ", "
+ "SignedHeaders="
+ signed_headers + ', '
+ 'Signature=' + signature_for_header,
}
# 3.1请求头携带签名,返回带签名的请求头
authorization_headers.update(signature_headers) # 3.2 url携带签名返回带签名的URL查询字符串
request_query_params = {
'X-Amz-Signature': signature_for_url
}
request_query_params.update(auth_params)
authorization_params = self.generate_canonical_querystring(**request_query_params)
# print('request_query_params: ', request_query_params)
# print('authorization_params:',authorization_params)
auth_data = {
'authorization_headers': authorization_headers,
'authorization_params': request_query_params,
'signature_headers': signature_headers, # get object 在URL里面传递签名 请求头要带的签名头
}
return auth_data # 通过标准请求头方式传递签名GET文件
def get_test_auth_in_header(self, query_params={}, append_headers={}):
method = 'GET' # 1.1.1 HTTP方法
auth_data = self.get_auth_data(method, query_params, append_headers)
authorization_headers = auth_data.get('authorization_headers')
req = requests.get(url=self.base_url, headers=authorization_headers, params=query_params)
print('Header鉴权的URL:', req.url)
print(req.status_code)
print(req.text) def get_test_auth_in_query_param(self,query_params={}, append_headers={}):
method = 'GET' # 1.1.1 HTTP方法
auth_data = self.get_auth_data(method, query_params, append_headers)
auth_query_params = auth_data.get('authorization_params')
signature_headers = auth_data.get('signature_headers')
canonical_querystring = self.generate_canonical_querystring(**auth_query_params) # 通过URL传递鉴权=requests 传参方式1
url_for_auth = self.base_url + "?" + canonical_querystring
req2 = requests.get(url=url_for_auth, headers=signature_headers)
print("手工拼接鉴权的URL:", url_for_auth)
print(req2.status_code)
print(req2.text)
# 通过URL传递鉴权=requests 传参方式2
req3 = requests.get(self.base_url, headers=signature_headers, params=auth_query_params)
# req3 = requests.get(url=self.base_url, headers=signature_headers, params=canonical_querystring)
print("requests自动拼接鉴权的URL:", req3.url)
print(req3.status_code)
print(req3.text) if __name__ == '__main__':
ak = 'XXXXXXXXXXXXXXX'
sk = 'XXXXXXXXXXXXXXXXXXXXXXXXX'
service = 'kcm'
domain = 'api.ksyun.com'
region = 'cn-beijing-6'
ksc = KscClient(ak, sk, service, domain, region)
query_params = {
'Action': 'GetDownloadLink',
'Version': '2016-03-04',
'CertificateId': 'kcm2021022216204501'
}
ksc.get_test_auth_in_header(query_params=query_params)
# 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. [Noip 2018][标题统计 龙湖斗 摆渡车 对称二叉树]普及组题解

    啊喂,都已经9102年了,你还在想去年? 这里是一个Noip2018年PJ第二题打爆的OIer,错失省一 但经过了一年,我学到了很多,也有了很多朋友,水平也提高了很多,现在回看当时: 今年的Noip ...

  2. [省选联考 2021 A/B 卷] 宝石

    大概是一眼看出来是这个给定序列在树上序列上按顺序跑最大匹配. 然后考虑维护向上和向下的链的值. 大概的做法是用倍增维护,考虑\(f_{u,i}\)是\(c_u\)在序列里的位置向后匹配\(2^i\)位 ...

  3. 洛谷 P4621 - [COCI2012-2013#6] BAKTERIJE(exCRT)

    洛谷题面传送门 发篇正常点的题解. 首先对于这样的题暴力枚举肯定是不行的,因为最小时间显然可能达到 \((4nm)^5\approx 10^{20}\),就算数据很难卡到这个上界,构造出一些使你暴力超 ...

  4. base64和图片转换

    Base64是一种8Bit字节数据的编码方式. 在参数传输的过程中经常遇到的一种情况:使用全英文的没问题,但一旦涉及到中文就会出现乱码情况.与此类似,网络上传输的字符并不全是可打印的字符,比如二进制文 ...

  5. mac 下 如何在同一窗口打开多个终端并实现快捷键切换

    相信大家编代码的时候都会遇到,每次需要在头文件,库文件和源码文件中编代码的时候,总是需要在几个文件中切换来切换去的,而且一个文件就一个终端窗口,每次都要用鼠标点来点去,非常麻烦,所以如果能把这几个文件 ...

  6. goto 的用法

    #include <stdio.h> int main() { printf("go to cpy \n"); goto FLASH_CPY; printf(" ...

  7. (转载)java排序实现

    Java实现几种常见排序方法 日常操作中常见的排序方法有:冒泡排序.快速排序.选择排序.插入排序.希尔排序,甚至还有基数排序.鸡尾酒排序.桶排序.鸽巢排序.归并排序等. 冒泡排序是一种简单的排序算法. ...

  8. 09 eclipse配置maven环境

    eclipse配置maven环境 一.打开eclipse:Window>>Preferences: 二.搜索:"maven",然后点击:"Installati ...

  9. MySQL:事务常用语句

    Mysql(版本是8)的事务隔离级别 默认是RR:REPEATABLE-READ:可重复读 查看 当前隔离级别 全局隔离级别  修改 -- 当前修改 -- 设置成可重复读 SET transactio ...

  10. HashMap有几种遍历方法?推荐使用哪种?

    本文已收录<面试精选>系列,Gitee 开源地址:https://gitee.com/mydb/interview HashMap 的遍历方法有很多种,不同的 JDK 版本有不同的写法,其 ...