@@ -91,3 +91,71 @@ def get_signing_secret_key_v4(sk, date, region, service):
9191 @staticmethod
9292 def hmac_sha256 (key , msg ):
9393 return hmac .new (key , msg .encode ('utf-8' ), hashlib .sha256 ).digest ()
94+
95+ @staticmethod
96+ def sign_url (path , method , query , ak , sk , region , service , session_token = None ):
97+ """
98+ Generate presigned URL query string (AWS Signature V4)
99+
100+ :param path: Request path
101+ :param method: HTTP method (GET, POST, etc.)
102+ :param query: Query parameters dict
103+ :param ak: Access Key
104+ :param sk: Secret Key
105+ :param region: Service region
106+ :param service: Service name
107+ :param session_token: Optional session token
108+ :return: Query string with signature
109+ """
110+ format_date = datetime .datetime .utcnow ().strftime ("%Y%m%dT%H%M%SZ" )
111+ date = format_date [:8 ]
112+
113+ # Build credential scope
114+ credential_scope = '/' .join ([date , region , service , 'request' ])
115+
116+ # Add required query parameters
117+ query = dict (query ) # Make a copy to avoid modifying original
118+ query ['X-Date' ] = format_date
119+ query ['X-NotSignBody' ] = ''
120+ query ['X-Credential' ] = ak + '/' + credential_scope
121+ query ['X-Algorithm' ] = 'HMAC-SHA256'
122+ query ['X-SignedHeaders' ] = ''
123+ query ['X-SignedQueries' ] = ''
124+
125+ if session_token :
126+ query ['X-Security-Token' ] = session_token
127+
128+ # Generate X-SignedQueries with all query parameter names
129+ query ['X-SignedQueries' ] = ';' .join (sorted (query .keys ()))
130+
131+ # Build canonical request with empty body
132+ body_hash = hashlib .sha256 (b'' ).hexdigest ()
133+ canonical_request = '\n ' .join ([
134+ method ,
135+ path ,
136+ SignerV4 .canonical_query (query ),
137+ '\n ' , # Empty line for signed headers section
138+ '' , # Empty signed headers list
139+ body_hash
140+ ])
141+ sdk_core_logger .debug_sign ("[sign_url] canonical_request:\n %s" , canonical_request )
142+
143+ # Build string to sign
144+ signing_str = '\n ' .join ([
145+ 'HMAC-SHA256' ,
146+ format_date ,
147+ credential_scope ,
148+ hashlib .sha256 (canonical_request .encode ('utf-8' )).hexdigest ()
149+ ])
150+ sdk_core_logger .debug_sign ("[sign_url] string_to_sign:\n %s" , signing_str )
151+
152+ # Calculate signature
153+ signing_key = SignerV4 .get_signing_secret_key_v4 (sk , date , region , service )
154+ signature = hmac .new (signing_key , signing_str .encode ('utf-8' ), hashlib .sha256 ).hexdigest ()
155+ sdk_core_logger .debug_sign ("[sign_url] calculated signature: %s" , signature )
156+
157+ # Add signature to query
158+ query ['X-Signature' ] = signature
159+
160+ # Return encoded query string
161+ return urlencode (sorted (query .items ()))
0 commit comments