method = 'GET'
service = 'execute-api'
user_agent = 'My Selling Tool/2.0 (Language=Python3; Platform=Windows/10)'
region = 'us-east-1'
host = 'sellingpartnerapi-na.amazon.com'
endpoint = 'https://sellingpartnerapi-na.amazon.com'
request_parameters = '/fba/inbound/v0/shipments/shipmentId1/preorder/confirm?MarketplaceId=ATVPDKIKX0DER&NeedByDate=2020-10-10'
acess_token = 'xxx'
access_key = 'xxx'
secret_key = 'xxx'
def sign(key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
def getSignatureKey(key, dateStamp, regionName, serviceName):
kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp)
kRegion = sign(kDate, regionName)
kService = sign(kRegion, serviceName)
kSigning = sign(
def getSignatureKey(key, dateStamp, regionName, serviceName):
t = datetime.datetime.utcnow()
amzdate = t.strftime('%Y%m%dT%H%M%SZ')
datestamp = t.strftime('%Y%m%d')
canonical_uri = '/'
canonical_querystring = request_parameters
canonical_headers = 'host:' + host + '\n' + 'user-agent:' + user_agent + '\n' + 'x-amz-access-token:' + acess_token + '\n' + 'x-amz-date:' + amzdate
signed_headers = 'host;user-agent;x-amz-access-token'
payload_hash = hashlib.sha256(('').encode('utf-8')).hexdigest()
canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash
algorithm = 'AWS4-HMAC-SHA256'
credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' + amzdate + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()
signing_key = getSignatureKey(secret_key, datestamp, region, service)
signature = hmac.new(signing_key, (string_to_sign).encode(
'utf-8'), hashlib.sha256).hexdigest()
authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature
headers = {'Authorization': authorization_header,
'host':host,
'user-agent': user_agent,
'x-amz-access-token': acess_token,
'x-amz-date': amzdate}
request_url = endpoint + '' + canonical_querystring
r = requests.get(request_url, headers=headers)
I follow sp-api and signatureV4 but got response "InvalidSignature"
this error is occur on Step 4. Create and sign your request.
I have no idea for 2 months, I guess the problem is about headers ?
Does my code need to be corrected or the problem is not here ?
You can use pip install aws-requests-auth There is Pip install link
from aws_requests_auth.aws_auth import AWSRequestsAuth
import json
import sys
AWS_AUTH = AWSRequestsAuth(
aws_access_key=config[0]["aws_access_key"],
aws_secret_access_key=config[0]["aws_secret_access_key"],
aws_host=config[0]["aws_host"],
aws_region=config[0]["aws_region"],
aws_service=config[0]["aws_service"],
)
AUTH_URL = config[0]["AUTH_URL"]
REFRESH_TOKEN = config[0]["REFRESH_TOKEN"]
CLIENT_ID = config[0]["CLIENT_ID"]
CLIENT_SECRET = config[0]["CLIENT_SECRET"]
AUTH_BODY = {
"grant_type": "refresh_token",
"refresh_token": REFRESH_TOKEN,
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
}
headers = {
"content-type": "application/x-www-form-urlencoded;charset=UTF-8",
"host": "sellingpartnerapi-fe.amazon.com",
}
auth_login = requests.post(AUTH_URL, headers=headers, data=AUTH_BODY)
response = auth_login.json()
reponseCode = auth_login.status_code
if reponseCode == 200:
print("====== AWS AUTH SIGN ====== OK", reponseCode)
return response["access_token"], AWS_AUTH, config[0]
else:
print("AUTH SIGN Error", response)
return False
Related
I am trying to generate a pre-signed web socket URL to get real time messaging notification for the AWS chime in the frontend as shown here . I planning to get this deployed as separate back end API using lambda. I followed exactly as shown here but I am getting "unauthorized" error when connecting to this generated URL in the front end. Can any one help me with what needs to be done to generate this pre-signed URL in python? I think I am using the wrong host/service parameters for chime.
import json
import boto3
import urllib.parse
import requests
import uuid
import datetime
import sys, os, base64, datetime, hashlib, hmac
import os
def sign(key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
def getSignatureKey(key, dateStamp, regionName, serviceName):
kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp)
kRegion = sign(kDate, regionName)
kService = sign(kRegion, serviceName)
kSigning = sign(kService, 'aws4_request')
return kSigning
def lambda_handler(event, context):
method = 'GET'
algorithm = 'AWS4-HMAC-SHA256'
service = 'chime'
host = client.get_messaging_session_endpoint()['Endpoint']['Url']
region = 'us-east-1'
#Getting the messaging endpoint using boto3
client = boto3.client('chime',region_name='us-east-1')
endpoint='wss://'+client.get_messaging_session_endpoint()['Endpoint']['Url']
user_id=event['queryStringParameters'].get('userId')
session_id=event['queryStringParameters'].get('sessionId')
user_id_arn=f'arn:aws:chime:us-east-1:11******:app-instance/03457-*****-412345-b3e4-123444/user/{user_id}'
access_key=os.environ['access_key']
secret_key=os.environ['secret_key']
#Following the steps as shown in the AWS documentation https://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html
t = datetime.datetime.utcnow()
amz_date = t.strftime('%Y%m%dT%H%M%SZ')
datestamp = t.strftime('%Y%m%d')
credential_scope=urllib.parse.quote(f'{access_key}/{datestamp}/us-east-1/chime/aws4_request', safe='')
canonical_uri = '/connect'
payload_hash = hashlib.sha256(('').encode('utf-8')).hexdigest()
canonical_headers = 'host:' + host + '\n'
signed_headers = 'host'
credential_scope=urllib.parse.quote(f'{access_key}/{datestamp}/us-east-1/chime/aws4_request', safe='')
canonical_querystring=''
canonical_querystring+='?X-Amz-Algorithm=AWS4-HMAC-SHA256'
canonical_querystring+=f'&X-Amz-Credential={credential_scope}'
canonical_querystring += '&X-Amz-Date=' + amz_date
canonical_querystring += '&X-Amz-SignedHeaders=' + signed_headers
canonical_querystring += '&X-Amz-Expires=3600'
canonical_querystring += '&sessionId=' + session_id
canonical_querystring += '&userArn=' + user_id_arn
canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash
hashed_canonical_request=hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()
string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashed_canonical_request
signing_key = getSignatureKey(secret_key, datestamp, region, service)
signature = hmac.new(signing_key, (string_to_sign).encode("utf-8"), hashlib.sha256).hexdigest()
canonical_querystring += '&X-Amz-Signature=' + signature
request_url = endpoint + canonical_uri+canonical_querystring
return_dict={'wssUrl':request_url}
return return dict
Any one who is wondering what the issue was- we have to pass the user_id_arn as arn%3Aaws%3Achime%3Aus-east-1%3A123456789012%3Aapp-instance%2f5abcdefg-cc50-4a70-a88e-fd07351d3c2a%2Fuser%2Fcustom-user-id instead of arn:aws:chime:us-east-1:123456789012:app-instance/f5abcdefg-cc50-4a70-a88e-fd07351d3c2a/user/custom-user-id
Full working code:
import json
import boto3
import urllib.parse
import requests
import uuid
import datetime
import sys, os, base64, datetime, hashlib, hmac
import os
def sign(key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
def getSignatureKey(key, dateStamp, regionName, serviceName):
kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp)
kRegion = sign(kDate, regionName)
kService = sign(kRegion, serviceName)
kSigning = sign(kService, 'aws4_request')
return kSigning
def handler():
# TODO: Replace with your info
session_id= # <session_id>
user_id_arn= # <user_id_arn>
access_key= # <access_key>
secret_key= # <secret_key>
# Following the steps as shown in the AWS documentation https://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html
t = datetime.datetime.utcnow()
amz_date = t.strftime('%Y%m%dT%H%M%SZ')
datestamp = t.strftime('%Y%m%d')
# Getting the messaging endpoint using boto3
client = boto3.client('chime',region_name='us-east-1')
hostname = client.get_messaging_session_endpoint()['Endpoint']['Url']
method = 'GET'
service = 'chime'
region = 'us-east-1'
canonical_uri = '/connect'
canonical_headers = 'host:' + hostname + '\n'
signed_headers = 'host'
algorithm = 'AWS4-HMAC-SHA256'
credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request'
canonical_querystring = ''
canonical_querystring += 'X-Amz-Algorithm=AWS4-HMAC-SHA256'
canonical_querystring += '&X-Amz-Credential=' + urllib.parse.quote_plus(access_key + '/' + credential_scope)
canonical_querystring += '&X-Amz-Date=' + amz_date
canonical_querystring += '&X-Amz-Expires=3600'
canonical_querystring += '&X-Amz-Security-Token=' + urllib.parse.quote(session_token, safe='')
canonical_querystring += '&X-Amz-SignedHeaders=' + signed_headers
canonical_querystring += '&sessionId=' + urllib.parse.quote(session_id, safe='')
canonical_querystring += '&userArn=' + urllib.parse.quote(user_id_arn, safe='')
payload_hash = hashlib.sha256(('').encode('utf-8')).hexdigest()
canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash
string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()
signing_key = getSignatureKey(secret_key, datestamp, region, service)
signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), hashlib.sha256).hexdigest()
canonical_querystring += '&X-Amz-Signature=' + signature
request_url = 'wss://' + hostname + canonical_uri + '?' + canonical_querystring
return_dict={'wssUrl':request_url}
return return_dict
My code for deriving the signature in python is as follows. Here my intention is to generate signature and string to sign in so that i can use these in a html form.It is showing the following error:
SignatureDoesNotMatch and the request signature we calculated does not match the signature you provided.
import sys,os,base64, datetime, hashlib, hmac
import requests
method = 'POST'
service = 's3'
host = 'myBucket.s3.amazonaws.com'
region = 'someregion'
request_parameters = ''
stToEncode = '{"expiration": "2020-06-30T12:00:00.000Z","conditions": [{"bucket": "myBucket"},
["starts-with","$key","user/user1/"],{"acl": "public-read"},{"success_action_redirect":
"http://myBucket.s3.amazonaws.com/postHtml.html"},["starts-with", "$Content-Type", "text/"],{"x-
amz- meta-uuid": "14365123651274"},{"x-amz-server-side-encryption": "AES256"},["starts-with",
"$x-amz- meta-tag", ""],{"x-amz-credential": "AKIAIOSFODNN7EXAMPLE/20200630/us-east-
1/s3/aws4_request"},{"x- amz-algorithm": "AWS4-HMAC-SHA256"},{"x-amz-date": "20200630T000000Z"
}]}'
stEncode = stToEncode.encode('utf8')
b64_stEncode = base64.b64encode(stEncode)
t = datetime.datetime.utcnow()
amzdate = t.strftime('%Y%m%dT%H%M%SZ')
datestamp = t.strftime('%Y%m%d') # Date w/o time, used in credential scope
def sign(key, msg):
return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()
def getSignatureKey(key, dateStamp, regionName, serviceName):
kDate = sign(("AWS4" + key).encode("utf-8"), dateStamp)
kRegion = sign(kDate, regionName)
kService = sign(kRegion, serviceName)
kSigning = sign(kService, "aws4_request")
return kSigning
access_key = 'somekey'
secret_key = 'somesecret'
if access_key is None or secret_key is None:
print('No access key is available')
sys.exit()
t = datetime.datetime.utcnow()
amzdate = t.strftime('%Y%m%dT%H%M%SZ')
datestamp = t.strftime('%Y%m%d') # Date w/o time, used in credential scope
# ************* TASK 2: CREATE THE STRING TO SIGN*************
# Match the algorithm to the hashing algorithm you use, either SHA-1 or
# SHA-256 (recommended)
canonical_uri = '/'
canonical_headers = 'host:' + host + '\n' + 'x-amz-date:' + amzdate + '\n'
signed_headers = 'host;x-amz-date'
canonical_querystring = request_parameters
payload_hash = hashlib.sha256(('').encode('utf-8')).hexdigest()
canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' +
canonical_headers + '\n' + signed_headers + '\n' + payload_hash
algorithm = 'AWS4-HMAC-SHA256'
credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' + amzdate + '\n' + credential_scope + '\n' +
hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()
# ************* TASK 3: CALCULATE THE SIGNATURE *************
# Create the signing key using the function defined above.
signing_key = getSignatureKey(secret_key, datestamp, region, service)
# Sign the string_to_sign using the signing_key
signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), hashlib.sha256).hexdigest()
#print(b64_stEncode)
print(string_to_sign)
print(signature)
I have tried everything. It works well for GET AND PUT requests. But with POST (to initiate multipart request) it throws various error like 405 Method not allowed or Signature Mismatch error. Any help is appreciated and welcome. Please if anyone have any idea throw some hints.
If anyone can edit this python code to get it working i would really appreciate that. Thanks in advance.
import sys, os, base64, datetime, hashlib, hmac
import requests # pip install requests
method = 'POST'
service = 's3'
host = 'freemedianews.s3.amazonaws.com'
region = 'ap-southeast-1'
endpoint = 'https://freemedianews.s3-ap-southeast-1.amazonaws.com'
request_parameters = ''
def sign(key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
def getSignatureKey(key, dateStamp, regionName, serviceName):
kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp)
kRegion = sign(kDate, regionName)
kService = sign(kRegion, serviceName)
kSigning = sign(kService, 'aws4_request')
return kSigning
access_key = '*******'
secret_key = '*******'
if access_key is None or secret_key is None:
print 'No access key is available.'
sys.exit()
t = datetime.datetime.utcnow()
amzdate = '20180621T151517Z'
datestamp = '20180621' # Date w/o time, used in credential scope
request.html
canonical_uri = '/a/message/1200/1200.png'
content_type = "multipart/form-data"
request_parameters variable.
canonical_querystring = request_parameters
payload_hash = hashlib.sha256('').hexdigest()
canonical_headers ='content-type:' + content_type + '\n' + 'host:' + host + '\n' + 'x-amz-content-sha256:' + payload_hash + '\n' + 'x-amz-date:' + amzdate + '\n'
signed_headers = 'content-type;host;x-amz-content-sha256;x-amz-date'
canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash
algorithm = 'AWS4-HMAC-SHA256'
credential_scope = datestamp + "/" + region + '/' + service + '/' + 'aws4_request'
credential_scope_final = access_key + "/" + datestamp + "/" + region + '/' + service + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' + amzdate + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request).hexdigest()
signing_key = getSignatureKey(secret_key, datestamp, region, service)
print "signing_key -----" + signing_key
signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), hashlib.sha256).hexdigest()
print "signature -----" + signature
authorization_header = algorithm + ' ' + 'Credential=' + credential_scope_final + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature
headers = {'Content-Type': content_type, 'Host': host, 'X-Amz-Content-Sha256': payload_hash, 'X-Amz-Date':amzdate, 'Authorization':authorization_header}
print authorization_header + "---this"
request_url = endpoint + canonical_uri
r = requests.post(request_url, data=request_parameters, headers=headers)
print r
print '\nRESPONSE++++++++++++++++++++++++++++++++++++'
print 'Response code: %d\n' % r.status_code
print r.text
I wan to achieve below. I have 10 log servers and 10 web servers in different locations. Every location has a pair i.e 1 log server and 1 web server. In S3 for every pair of servers, there is a bucket for storing logs like Location 1, Location 2, location 3.
I want to upload logs from every location to its respective buckets. I can do that with awscli but for that i have to create Iam user for every location and attach a s3 policy and put in access keys and secret keys in every location. I do not want this approach.
Instead, i was thinking that i would embed my access keys and secret access keys in every web server and then using AWS Signature version 4 , i would generate a signature for every file with respect to its bucket and upload to S3.
import sys, os, base64, datetime, hashlib, hmac
from cassandra.cluster import Cluster
from datetime import datetime, timedelta
import json
import requests
import logging
LOGGER = None
def sign(secret_key, msg):
return hmac.new(secret_key, msg.encode("utf-8"),hashlib.sha256).digest()
def getSignatureKey(secret_key, date_stamp, regionName, serviceName):
kDate = sign(('AWS4' + secret_key).encode('utf-8'), date_stamp)
kRegion = sign(kDate, regionName)
kService = sign(kRegion, serviceName)
kSigning = sign(kService, 'aws4_request')
return kSigning
def S3UploadPolicy(date_stampfiso,customer_name, amz_date,credential):
params = {}
params['expiration'] = date_stampfiso
params['conditions'] = [{'bucket': customer_name},
{'acl': 'private'},
{'success_action_status': '201'},
['starts_with', '', ''],
{'x-amz-algorithm': 'AWS4-HMAC-SHA256'},
{'x-amz-credential': credential},
{'x-amz-date': amz_date}]
params = json.dumps(params)
return params
def S3Upload(access_key,date_stamp,date_stampfiso,customer_name, amz_date, regionName, secret_key, serviceName, filename):
host = '<bucketname>.s3.amazonaws.com'
endpoint_url = 'http://' + customer_name + '.s3.amazonaws.com'
#content_type = 'multipart/form-data; charset=UTF-8'
content_type = 'text/plain'
#method = 'POST'
method = 'PUT'
canonical_uri = '/' + customer_name
canonical_querystring = filename
canonical_headers = 'content-type:' + content_type + '\n' + 'host:' + host + '\n' + 'x-amz-date:' + amz_date + '\n'
credential_scope = date_stamp + '/' + regionName + '/' + serviceName + '/' + 'aws4_request'
signed_headers = 'content-type;host;x-amz-content-sha256;x-amz-date'
policy = S3UploadPolicy(date_stampfiso,customer_name, amz_date,credential_scope)
policyBase64 = base64.b64encode(policy)
payload_hash = hashlib.sha256(policyBase64).hexdigest()
canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash
xamzalgorithm = 'AWS4-HMAC-SHA256'
algorithm = xamzalgorithm
string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request).hexdigest()
signing_key = getSignatureKey(secret_key, date_stamp, regionName, serviceName)
signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), hashlib.sha256).hexdigest()
authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature
print authorization_header
#sys.exit(1)
headers = {
'content-type': content_type,
'x-amz-date': amz_date,
'authorization': authorization_header,
'x-amz-content-sha256': payload_hash
}
try:
print '\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++'
print 'Request URL = ' + endpoint_url
r = requests.put(endpoint_url, headers=headers)
print '\nRESPONSE++++++++++++++++++++++++++++++++++++'
print 'Response code: %d\n' % r.status_code
print r.text
except Exception as e:
LOGGER.error(e)
def main():
global LOGGER
msg = ''
access_key = 'xxxxxxxxxxxx'
secret_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
customer_name = 'abc-test2'
regionName = 'us-west-2'
serviceName = 's3'
filename = '/home/abc/abc.pem'
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s | %(levelname)s | %(module)s [%(process)d %(thread)d] | [%(filename)s:%(lineno)s - %(funcName)s() ] | \n%(message)s')
LOGGER = logging.getLogger(__name__)
## Calculate Date
t = datetime.utcnow()
amz_date = t.strftime('%Y%m%dT%H%M%SZ')
date_stamp = t.strftime('%Y%m%d')
date_stampf = datetime.now() + timedelta(hours=24)
amz_date_future = date_stampf.strftime('%Y%m%dT%H%M%SZ')
date_stampfiso = date_stampf.isoformat()
S3Upload(access_key, date_stamp, date_stampfiso, customer_name, amz_date, regionName, secret_key, serviceName,
filename)
if __name__ == '__main__':
main()
I am trying to use the python sample code from amazon docs to do a "GET with an Authorization Header" mechanism on a s3 bucket. The source code which I am using is below:
# AWS Version 4 signing example
# EC2 API (DescribeRegions)
# See: http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
# This version makes a GET request and passes the signature
# in the Authorization header.
import sys, os, base64, datetime, hashlib, hmac
import requests # pip install requests
# ************* REQUEST VALUES *************
method = 'GET'
service = 's3'
host = 's3.amazonaws.com'
region = 'us-east-1'
endpoint = 'http://s3.amazonaws.com/sample_object/foo'
request_parameters = ''
# Key derivation functions. See:
# http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-python
def sign(key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
def getSignatureKey(key, dateStamp, regionName, serviceName):
kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp)
kRegion = sign(kDate, regionName)
kService = sign(kRegion, serviceName)
kSigning = sign(kService, 'aws4_request')
return kSigning
# Read AWS access key from env. variables or configuration file. Best practice is NOT
# to embed credentials in code.
access_key = os.environ.get('AWS_ACCESS_KEY_ID')
secret_key = os.environ.get('AWS_SECRET_ACCESS_KEY')
if access_key is None or secret_key is None:
print 'No access key is available.'
sys.exit()
# Create a date for headers and the credential string
t = datetime.datetime.utcnow()
amzdate = t.strftime('%Y%m%dT%H%M%SZ')
datestamp = t.strftime('%Y%m%d') # Date w/o time, used in credential scope
# ************* TASK 1: CREATE A CANONICAL REQUEST *************
# http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
# Step 1 is to define the verb (GET, POST, etc.)--already done.
# Step 2: Create canonical URI--the part of the URI from domain to query
# string (use '/' if no path)
canonical_uri = '/sample_object/foo'
# Step 3: Create the canonical query string. In this example (a GET request),
# request parameters are in the query string. Query string values must
# be URL-encoded (space=%20). The parameters must be sorted by name.
# For this example, the query string is pre-formatted in the request_parameters variable.
canonical_querystring = request_parameters
# Step 4: Create the canonical headers and signed headers. Header names
# and value must be trimmed and lowercase, and sorted in ASCII order.
# Note that there is a trailing \n.
# amz_content_sha256 = hashlib.sha256('').hexdigest()
amz_content_sha256 = 'UNSIGNED-PAYLOAD'
canonical_headers = 'host:' + host + '\n' + 'x-amz-content-sha256:' + amz_content_sha256 + '\n' + 'x-amz-date:' + amzdate + '\n'
# Step 5: Create the list of signed headers. This lists the headers
# in the canonical_headers list, delimited with ";" and in alpha order.
# Note: The request can include any headers; canonical_headers and
# signed_headers lists those that you want to be included in the
# hash of the request. "Host" and "x-amz-date" are always required.
signed_headers = 'host;x-amz-content-sha256;x-amz-date'
# Step 6: Create payload hash (hash of the request body content). For GET
# requests, the payload is an empty string ("").
payload_hash = hashlib.sha256('').hexdigest()
# Step 7: Combine elements to create create canonical request
canonical_request = method + '\n' + canonical_uri + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash
# ************* TASK 2: CREATE THE STRING TO SIGN*************
# Match the algorithm to the hashing algorithm you use, either SHA-1 or
# SHA-256 (recommended)
algorithm = 'AWS4-HMAC-SHA256'
credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' + amzdate + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request).hexdigest()
# ************* TASK 3: CALCULATE THE SIGNATURE *************
# Create the signing key using the function defined above.
signing_key = getSignatureKey(secret_key, datestamp, region, service)
# Sign the string_to_sign using the signing_key
signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), hashlib.sha256).hexdigest()
# ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************
# The signing information can be either in a query string value or in
# a header named Authorization. This code shows how to use a header.
# Create authorization header and add to request headers
authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature
# The request can include any headers, but MUST include "host", "x-amz-date",
# and (for this scenario) "Authorization". "host" and "x-amz-date" must
# be included in the canonical_headers and signed_headers, as noted
# earlier. Order here is not significant.
# Python note: The 'host' header is added automatically by the Python 'requests' library.
headers = {'x-amz-date':amzdate, 'Authorization':authorization_header, 'x-amz-content-sha256':amz_content_sha256}
# ************* SEND THE REQUEST *************
request_url = endpoint
print '\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++'
print 'Request URL = ' + request_url
r = requests.get(request_url, headers=headers)
print '\nRESPONSE++++++++++++++++++++++++++++++++++++'
print 'Response code: %d\n' % r.status_code
print r.text
I have correctly set the access id and secret access key. I used the same with boto python library and it seems to work when I list buckets, put objects etc. However when I use the above method I get back a 403 error saying "The request signature we calculated does not match the signature you provided. Check your key and signing method." I am not sure what is wrong with the above method. Any pointers.?
The canonical headers should have a trailing newline and canonical_request needs the canonical_querystring line even if it is blank. Also, use UNSIGNED_PAYLOAD for amz_content_sha256 for GET requests.
Here's a working example. It gets a file called key on an S3 bucket in the us-west-2 region called bucket:
import sys, os, base64, datetime, hashlib, hmac
import requests
method = 'GET'
service = 's3'
host = 's3-us-west-2.amazonaws.com'
region = 'us-west-2'
endpoint = 'http://s3-us-west-2.amazonaws.com'
request_parameters = ''
def sign(key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
def getSignatureKey(key, dateStamp, regionName, serviceName):
kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp)
kRegion = sign(kDate, regionName)
kService = sign(kRegion, serviceName)
kSigning = sign(kService, 'aws4_request')
return kSigning
access_key = os.environ.get('AWS_ACCESS_KEY_ID')
secret_key = os.environ.get('AWS_SECRET_ACCESS_KEY')
if access_key is None or secret_key is None:
print 'No access key is available.'
sys.exit()
t = datetime.datetime.utcnow()
amzdate = t.strftime('%Y%m%dT%H%M%SZ')
datestamp = t.strftime('%Y%m%d') # Date w/o time, used in credential scope
canonical_uri = '/bucket/key'
canonical_querystring = request_parameters
canonical_headers = 'host:' + host + '\n' + 'x-amz-content-sha256:UNSIGNED-PAYLOAD' + '\n' + 'x-amz-date:' + amzdate + '\n'
signed_headers = 'host;x-amz-content-sha256;x-amz-date'
payload_hash = 'UNSIGNED-PAYLOAD'
canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash
algorithm = 'AWS4-HMAC-SHA256'
credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' + amzdate + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request).hexdigest()
signing_key = getSignatureKey(secret_key, datestamp, region, service)
signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), hashlib.sha256).hexdigest()
authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature
headers = {'x-amz-date':amzdate, 'x-amz-content-sha256': 'UNSIGNED-PAYLOAD', 'Authorization':authorization_header}
request_url = endpoint + canonical_uri
print '\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++'
print 'Request URL = ' + request_url
r = requests.get(request_url, headers=headers)
print '\nRESPONSE++++++++++++++++++++++++++++++++++++'
print 'Response code: %d\n' % r.status_code
print r.text