Question:
I want to create a presigned url for the objects in my bucket. I use the following python code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
client = boto3.client( 's3', aws_access_key_id=os.environ['AWS_ACCESS_KEY'], aws_secret_access_key=os.environ['AWS_SECRETS_KEY'], config=botocore.client.Config(signature_version='s3v4'), region_name='eu-central-1' ) url = client.generate_presigned_url( ClientMethod='get_object', ExpiresIn=60, Params={ 'Bucket': MYBUCKET, 'Key': MYKEY }) |
I then send the generated URL to my frontend. On the client I will create an a tag with the generated link and use the click() method on it. This worked fine in other projects but here I only get the error:
1 2 |
The request signature we calculated does not match the signature you provided. Check your key and signing method. |
Which is strange. The user should have all the necessary rights. Because listing all the files in my bucket works fine.
Can someone point me in the right direction why this isn’t working?
EDIT
I’m using next.js on the frontend if this is of help.
Answer:
Had the exact same problem. Studied the AWS docs and wrote the (signature v4) procedure myself. The below is based on
https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
and works perfectly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
ENCODING = 'utf8' SEVEN_DAYS = 604800 logger = logging.getLogger() logger.setLevel(logging.INFO) def sign(key, msg): return hmac.new(key, msg.encode(ENCODING), hashlib.sha256).digest() def get_signature_key(key, dateStamp, regionName, serviceName): kDate = sign(('AWS4' + key).encode(ENCODING), dateStamp) kRegion = sign(kDate, regionName) kService = sign(kRegion, serviceName) kSigning = sign(kService, 'aws4_request') return kSigning def generate_presigned_s3_get(bucket, object_key, region, expires_in, access_key, secret_key): METHOD = 'GET' SERVICE = 's3' host = bucket + '.s3.' + region + '.amazonaws.com' endpoint = 'https://' + host t = datetime.datetime.utcnow() amz_date = t.strftime('%Y%m%dT%H%M%SZ') datestamp = t.strftime('%Y%m%d') canonical_uri = '/' + object_key canonical_headers = 'host:' + host + '\n' signed_headers = 'host' algorithm = 'AWS4-HMAC-SHA256' credential_scope = datestamp + '/' + region + '/' + SERVICE + '/' + 'aws4_request' 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=' + str(expires_in) canonical_querystring += '&X-Amz-SignedHeaders=' + signed_headers canonical_request = METHOD + '\n' + canonical_uri + '\n' + canonical_querystring[1:] + '\n' + canonical_headers + '\n' + signed_headers + '\nUNSIGNED-PAYLOAD' string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request.encode(ENCODING)).hexdigest() signing_key = get_signature_key(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 url = endpoint + canonical_uri + canonical_querystring logger.info('presigned url: %s' % url) return url |
I’ve also reported this issue to boto3 peeps:
https://github.com/boto/boto3/issues/1644