import hashlib import hmac from urllib.parse import quote def sha1_hex(data: str) -> str: return hashlib.sha1(data.encode("utf-8")).hexdigest() def hmac_sha1_hex(key: bytes, msg: str) -> str: return hmac.new(key, msg.encode("utf-8"), hashlib.sha1).hexdigest() def canonical_kv(data: dict): """ 等价于 JS: D.obj2str(obj, true) """ items = [] for k in sorted(data.keys()): key = quote(str(k).lower(), safe='') val = quote(str(data[k]), safe='') items.append(f"{key}={val}") return "&".join(items) def canonical_key_list(data: dict): """ 等价于 JS: y(obj, true).join(";").toLowerCase() """ return ";".join(sorted([k.lower() for k in data.keys()])) def get_auth(params: dict): secret_id = params.get("SecretId") secret_key = params.get("SecretKey") method = (params.get("Method") or params.get("method") or "get").lower() query = params.get("Query") or {} headers = params.get("Headers") or {} pathname = params.get("Pathname") or "" use_raw_key = params.get("UseRawKey", False) key_time = params.get("KeyTime") if not secret_id: raise ValueError("missing param SecretId") if not secret_key: raise ValueError("missing param SecretKey") # ===== 处理 Path ===== if not use_raw_key: if not pathname.startswith("/"): pathname = "/" + pathname # ===== Canonical 处理 ===== header_list = canonical_key_list(headers) param_list = canonical_key_list(query) header_str = canonical_kv(headers) param_str = canonical_kv(query) # ===== HttpString ===== http_string = "\n".join([ method, pathname, param_str, header_str, "" ]) http_sha1 = sha1_hex(http_string) # ===== StringToSign ===== string_to_sign = "\n".join([ "sha1", key_time, http_sha1, "" ]) # ===== 计算签名 ===== sign_key = hmac_sha1_hex(secret_key.encode(), key_time) signature = hmac_sha1_hex(sign_key.encode(), string_to_sign) # ===== 拼接 Authorization ===== auth = "&".join([ "q-sign-algorithm=sha1", f"q-ak={secret_id}", f"q-sign-time={key_time}", f"q-key-time={key_time}", f"q-header-list={header_list}", f"q-url-param-list={param_list}", f"q-signature={signature}" ]) return auth