33import datetime
44import hmac
55import hashlib
6- from six .moves .urllib .parse import urlencode
6+ from six .moves .urllib .parse import quote
77
88
99class SignerV4 (object ):
@@ -39,8 +39,8 @@ def sign(path, method, headers, body, query, ak, sk, region, service):
3939
4040 signed_headers_string = ';' .join (sorted (signed_headers .keys ()))
4141
42- # The sorted() method sorts tuples by default, using the first item in each tuple.
43- canonical_request = ' \n ' . join ( [method , path , urlencode ( sorted (query )), signed_str , signed_headers_string , body_hash ])
42+ canonical_request = ' \n ' . join (
43+ [method , path , SignerV4 . canonical_query ( dict (query )), signed_str , signed_headers_string , body_hash ])
4444 credential_scope = '/' .join ([format_date [:8 ], region , service , 'request' ])
4545 signing_str = '\n ' .join (['HMAC-SHA256' , format_date , credential_scope ,
4646 hashlib .sha256 (canonical_request .encode ('utf-8' )).hexdigest ()])
@@ -54,6 +54,17 @@ def sign(path, method, headers, body, query, ak, sk, region, service):
5454 signed_headers_string + ', Signature=' + signature
5555 return
5656
57+ @staticmethod
58+ def canonical_query (query ):
59+ res = []
60+ for key in query :
61+ value = str (query [key ])
62+ res .append ((quote (key , safe = '-_.~' ), quote (value , safe = '-_.~' )))
63+ sorted_key_vals = []
64+ for key , value in sorted (res ):
65+ sorted_key_vals .append ('%s=%s' % (key , value ))
66+ return '&' .join (sorted_key_vals )
67+
5768 @staticmethod
5869 def get_signing_secret_key_v4 (sk , date , region , service ):
5970 kdate = SignerV4 .hmac_sha256 (sk .encode ('utf-8' ), date )
0 commit comments