如何用Python生成AWS签名?签名过期及生成失败问题求助
AWS Signature Version 4签名错误排查与修复
遇到的错误
{"message":"Signature expired: 20220813T114319Z is now earlier than 20220813T114400Z (20220813T114900Z - 5 min.)"}
代码问题分析
1. 签名过期原因
请求中硬编码了X-Amz-Date为20220813T114319Z,该时间早于当前服务器时间5分钟以上。AWS要求请求的时间戳与服务器UTC时间差不得超过5分钟,否则会触发签名过期错误。
2. 签名生成代码的核心错误
- Service名称错误:调用API Gateway服务时,
service参数应为execute-api,而非sao-francisco,签名生成时的服务名称必须与实际调用的AWS服务匹配。 - Canonical URI错误:
canonical_uri应该是请求的路径部分(不含协议和主机),正确值为/prod/fulfillmentManagers/xxxxxxxxxx/fulfillments/zzzzzzzzzz,而非完整的endpoint URL。 - Headers不匹配:签名生成时的
canonical_headers和signed_headers未包含实际请求中的Content-Type、X-Amz-Security-Token、X-UserId,且header名未统一为小写(AWS要求header名小写)。 - Authorization格式错误:Credential字段中
datestamp前多了空格,Signature字段前也多了空格,AWS对签名格式的空格要求严格,多余空格会导致签名验证失败。
修复后的完整代码
import datetime, hashlib, hmac, requests access_key = "你的AccessKey" secret_key = "你的SecretKey" session_token = "你的SessionToken" method = 'GET' service = 'execute-api' # 修正为API Gateway的服务名称 host = 'yyyyyyyyyy.execute-api.us-east-1.amazonaws.com' region = 'us-east-1' endpoint = f'https://{host}' path = '/prod/fulfillmentManagers/xxxxxxxxxx/fulfillments/zzzzzzzzzz' 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 # 获取当前UTC时间,确保签名和请求时间一致 t = datetime.datetime.utcnow() amz_date = t.strftime('%Y%m%dT%H%M%SZ') datestamp = t.strftime('%Y%m%d') # 构造Canonical URI(仅路径部分) canonical_uri = path # 构造Canonical Headers,包含所有请求header,且名称小写 canonical_headers = ( f'content-type:application/x-amz-json-1.1\n' f'host:{host}\n' f'x-amz-date:{amz_date}\n' f'x-amz-security-token:{session_token}\n' f'x-userid:bob\n' ) # Signed Headers需与Canonical Headers一一对应,小写,分号分隔 signed_headers = 'content-type;host;x-amz-date;x-amz-security-token;x-userid' algorithm = 'AWS4-HMAC-SHA256' credential_scope = f'{datestamp}/{region}/{service}/aws4_request' canonical_querystring = '' payload_hash = hashlib.sha256(('').encode('utf-8')).hexdigest() # 构造Canonical Request canonical_request = ( f'{method}\n' f'{canonical_uri}\n' f'{canonical_querystring}\n' f'{canonical_headers}\n' f'{signed_headers}\n' f'{payload_hash}' ) # 构造String to Sign string_to_sign = ( f'{algorithm}\n' f'{amz_date}\n' f'{credential_scope}\n' f'{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头,注意格式无多余空格 authorization_header = ( f'AWS4-HMAC-SHA256 Credential={access_key}/{credential_scope}, ' f'SignedHeaders={signed_headers}, ' f'Signature={signature}' ) # 构造请求headers headers = { 'Content-Type': 'application/x-amz-json-1.1', 'X-UserId': 'bob', 'X-Amz-Security-Token': session_token, 'X-Amz-Date': amz_date, 'Authorization': authorization_header } # 发送请求 url = f'{endpoint}{path}' response = requests.request(method, url, headers=headers, data='') print(response.text)
内容的提问来源于stack exchange,提问作者Karthik
相关产品推荐
相关产品推荐

