生成带自定义元数据的S3 PUT预签名URL时签名不匹配问题
问题场景
生成携带自定义元数据mk1:mv1的S3 PUT预签名URL后,使用curl请求时收到签名不匹配错误:
The request signature we calculated does not match the signature you provided. Check your key and signing method.
生成URL的Java代码
public URL generatePushResourceLocation(DocumentId documentId, String contentMD5) { String key = documentId.getId().toString(); PutObjectRequest.Builder putObjectRequestBuilder = PutObjectRequest.builder().bucket(bucket).key(key) .metadata(Map.of("mk1", "mv1")); PutObjectRequest putObjectRequest = putObjectRequestBuilder.build(); PutObjectPresignRequest request = PutObjectPresignRequest.builder().putObjectRequest(putObjectRequest) .signatureDuration(Duration.ofMinutes(1)).build(); PresignedPutObjectRequest presignPutObject = s3Presigner.presignPutObject(request); URL signedPutURL = presignPutObject.url(); log.info("Push Presigned URL created: [{}]", signedPutURL.toString()); return signedPutURL; }
完整错误响应
<Error> <Code>SignatureDoesNotMatch</Code> <Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message> <AWSAccessKeyId>AKIAQ3EGWRZYSIU7ENEO</AWSAccessKeyId> <StringToSign>AWS4-HMAC-SHA256 20240402T122245Z 20240402/eu-west-1/s3/aws4_request 66cdf5260d90912bebfd2cc337f5c1bcfb8fb6b4847852bd427627f82bf9b8c8</StringToSign> <SignatureProvided>bd7e26bf661eea50966733816872544a6bf75151e30f819f2239725fcae08203</SignatureProvided> <StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 34 30 34 30 32 54 31 32 32 32 34 35 5a 0a 32 30 32 34 30 34 30 32 2f 65 75 2d 77 65 73 74 2d 31 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 36 36 63 64 66 35 32 36 30 64 39 30 39 31 32 62 65 62 66 64 32 63 63 33 33 37 66 35 63 31 62 63 66 62 38 66 62 36 62 34 38 34 37 38 35 32 62 64 34 32 37 36 32 37 66 38 32 62 66 39 62 38 63 38</StringToSignBytes> <CanonicalRequest>PUT /espaidoc-des/1117beac-c2b4-414a-a518-1c4ac805644d X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAQ3EGWRZYSIU7ENEO%2F20240402%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20240402T122245Z&X-Amz-Expires=600&X-Amz-SignedHeaders=host%3Bx-amz-meta-mk1 host:s3.eu-west-1.amazonaws.com x-amz-meta-mk1: host;x-amz-meta-mk1 UNSIGNED-PAYLOAD</CanonicalRequest> <CanonicalRequestBytes>50 55 54 0a 2f 65 73 70 61 69 64 6f 63 2d 64 65 73 2f 31 31 31 37 62 65 61 63 2d 63 32 62 34 2d 34 31 34 61 2d 61 35 31 38 2d 31 63 34 61 63 38 30 35 36 34 34 64 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 51 33 45 47 57 52 5a 59 53 49 55 37 45 4e 45 4f 25 32 46 32 30 32 34 30 34 30 32 25 32 46 65 75 2d 77 65 73 74 2d 31 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 34 30 34 30 32 54 31 32 32 32 34 35 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 36 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 68 6f 73 74 25 33 42 78 2d 61 6d 7a 2d 6d 65 74 61 2d 6d 6b 31 0a 68 6f 73 74 3a 73 33 2e 65 75 2d 77 65 73 74 2d 31 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 0a 78 2d 61 6d 7a 2d 6d 65 74 61 2d 6d 6b 31 3a 0a 0a 68 6f 73 74 3b 78 2d 61 6d 7a 2d 6d 65 74 61 2d 6d 6b 31 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes> <RequestId>6Q3P82ZWPWNAC5V0</RequestId> <HostId>uEVaiZU1gKj0n6KNIrkuWHELwJD9El/DdQ1uKu+o5dwsEvoFdaVTv/gL0e6AfbN/kthrJlo0+9A=</HostId> </Error>
使用的curl命令
curl --request PUT --header 'Content-Type:' --data-binary '@hurl/push-document/lorem.txt' 'https://s3.eu-west-1.amazonaws.com/espaidoc-des/54c07cd8-1bce-43b9-ab32-28a7e9108c19?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240402T123532Z&X-Amz-SignedHeaders=host%3Bx-amz-meta-mk1&X-Amz-Expires=60&X-Amz-Credential=AKIAQ3EGWRZYSIU7ENEO%2F20240402%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=ebfbbcfbbaadae7b580ed0a6cddb503371a6aa11b59e229fcc5663225f45a9be'
解决思路
从错误返回的CanonicalRequest可以看出,签名校验时包含了x-amz-meta-mk1请求头,但该头的值为空——这是因为生成预签名URL时指定了元数据,但请求时没有携带对应的元数据头,导致签名校验不通过。
具体修复步骤
- 请求时必须携带匹配的元数据头:在curl命令中添加
x-amz-meta-mk1: mv1头,确保和预签名URL生成时的元数据完全一致。修改后的curl命令:
curl --request PUT --header 'Content-Type:' --header 'x-amz-meta-mk1: mv1' --data-binary '@hurl/push-document/lorem.txt' 'https://s3.eu-west-1.amazonaws.com/espaidoc-des/54c07cd8-1bce-43b9-ab32-28a7e9108c19?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240402T123532Z&X-Amz-SignedHeaders=host%3Bx-amz-meta-mk1&X-Amz-Expires=60&X-Amz-Credential=AKIAQ3EGWRZYSIU7ENEO%2F20240402%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=ebfbbcfbbaadae7b580ed0a6cddb503371a6aa11b59e229fcc5663225f45a9be'
验证签名头的一致性:预签名URL的
X-Amz-SignedHeaders包含host和x-amz-meta-mk1,所以请求时必须携带这两个头(host头curl会自动添加,无需手动指定,但x-amz-meta-mk1必须显式添加)。可选:解除元数据与签名的绑定:如果不需要固定元数据,可以在生成预签名URL时,不将元数据放入
PutObjectRequest的metadata中,让请求方上传时自行添加元数据——此时元数据不会被纳入签名校验范围,但这种方式不适合需要强制设置元数据的场景。
内容的提问来源于stack exchange,提问作者Jordi

