PHP调用Amazon S3出现SignatureDoesNotMatch错误求助
AWS S3 SignatureDoesNotMatch 问题排查
问题描述
我尝试从S3主机获取所有存储桶,但遇到了AWS签名相关问题。已按照AWS官方文档步骤操作,代码参考了第三方PHP签名实现,Postman中能正常执行GET和创建存储桶,但PHP代码报错。
错误信息
"Code": "SignatureDoesNotMatch", "Message": "The request signature we calculated does not match the signature you provided. Check your Secret Access Key and signing method. For more information, see REST Authentication and SOAP Authentication for details."
相关PHP代码
public function signature($secretAccessKey, $region, $service, $date, $timeStamp, $httpRequestMethod, $host, $uri, $payload): array { $debugCanonicalRequest = []; $debugStringToSign = []; $canonicalRequest = []; $canonicalHeaders = []; $stringToSign = []; $phpAlgorithm = 'sha256'; $aws4_request = 'aws4_request'; $algorithm = 'AWS4-HMAC-SHA256'; $canonicalQueryString = ''; $signedHeaders = 'content-type;host;x-amz-date'; $canonicalHeaders[] = 'content-type:application/x-www-form-urlencoded'; $canonicalHeaders[] = 'host:' . $host; $canonicalHeaders[] = 'x-amz-date:' . $timeStamp; $canonicalHeadersStr = implode("\n", $canonicalHeaders); $canonicalRequest[] = $debugCanonicalRequest["HTTP verb"] = $httpRequestMethod; $canonicalRequest[] = $debugCanonicalRequest["canonical URI"] = $uri; $canonicalRequest[] = $debugCanonicalRequest["canonical query string"] = $canonicalQueryString; $canonicalRequest[] = $debugCanonicalRequest["canonical headers"] = $canonicalHeadersStr . "\n"; $canonicalRequest[] = $debugCanonicalRequest["Signed headers"] = $signedHeaders; $canonicalRequest[] = hash($phpAlgorithm, $payload); $debugCanonicalRequest["hashed payload"] = hash($phpAlgorithm, $payload); $requestCanonicalRequest = implode("\n", $canonicalRequest); $debugCanonicalRequest["full canonical request"] = implode("\n", $canonicalRequest); $stringToSign[] = $debugStringToSign["algorithm"] = $algorithm; $stringToSign[] = $debugStringToSign["timestamp"] = $timeStamp; $stringToSign[] = $debugStringToSign["scope"] = implode('/', [$date, $region, $service, $aws4_request]); $stringToSign[] = hash($phpAlgorithm, utf8_encode($requestCanonicalRequest)); $stringToSignStr = $debugStringToSign["full string to sign"] = implode("\n", $stringToSign); $dateKey = hash_hmac($phpAlgorithm, $date, 'AWS4' . $secretAccessKey, true); $dateRegionKey = hash_hmac($phpAlgorithm, $region, $dateKey, true); $dateRegionServiceKey = hash_hmac($phpAlgorithm, $service, $dateRegionKey, true); $signingKey = hash_hmac($phpAlgorithm, $aws4_request, $dateRegionServiceKey, true); return ["signature" => hash_hmac($phpAlgorithm, $stringToSignStr, $signingKey), "debug" => ["CanonicalRequest" => $debugCanonicalRequest, "StringToSign" => $debugStringToSign]]; } public function s3Request(): bool|string { $accessKey = [[accessKey]]; $secretKey = [[secretKey]]; $region = "us-east-1"; $service = "s3"; $httpRequestMethod = "GET"; $uri = '/'; $host = [[host]]; $payload = ""; $currentDateTime = new DateTime('UTC'); $date = $currentDateTime->format('Ymd'); $timeStamp = $currentDateTime->format('Ymd\THis\Z'); $signature = $this->signature($secretKey, $region, $service, $date, $timeStamp, $httpRequestMethod, $host, $uri, $payload); $content = hash("sha256", $payload); $headers = [ 'X-Amz-Content-Sha256: ' . $content, 'X-Amz-Date: ' . $timeStamp, 'Authorization: AWS4-HMAC-SHA256 Credential=' . $accessKey . '/' . $date . '/' . $region . '/' . $service . '/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=' . $signature["signature"] ]; $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => "http://192.168.1.18:9020/", CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => $httpRequestMethod, CURLOPT_HTTPHEADER => $headers, )); $response = curl_exec($curl); curl_close($curl); return json_encode([$signature["debug"], "headers" => $headers, "response" => $this->utility->responseConverter($response, "xmlBody")]); }
问题原因与修复方案
核心问题点
- SignedHeaders 不一致:签名生成时定义的
signedHeaders为content-type;host;x-amz-date,但请求Authorization头中声明的是host;x-amz-content-sha256;x-amz-date,二者必须完全匹配。 - 多余的Content-Type头部:GET请求且payload为空时,不需要在规范头部中添加
content-type:application/x-www-form-urlencoded,而实际请求也未发送该头部,导致签名计算的头部与实际请求头部不匹配。 - 缺失X-Amz-Content-Sha256规范头部:请求中添加了
X-Amz-Content-Sha256头,但签名计算的规范头部未包含它,必须将其纳入canonicalHeaders和signedHeaders。
具体修复步骤
- 调整签名函数中的头部配置:
// 更新signedHeaders,与请求头部对应 $signedHeaders = 'host;x-amz-content-sha256;x-amz-date'; // 移除多余的Content-Type,添加X-Amz-Content-Sha256到规范头部 $payloadHash = hash($phpAlgorithm, $payload); $canonicalHeaders[] = 'host:' . $host; $canonicalHeaders[] = 'x-amz-content-sha256:' . $payloadHash; $canonicalHeaders[] = 'x-amz-date:' . $timeStamp; - 确保Authorization头的SignedHeaders与签名函数一致:当前代码中的Authorization头SignedHeaders是正确的,只需保证签名函数中的
signedHeaders与之匹配即可。 - 验证头部一致性:确保所有在请求中发送的头部(除了host之外的标准头部)都被纳入规范头部和SignedHeaders,且没有多余的头部被包含。
内容的提问来源于stack exchange,提问作者sebastian kull
相关产品推荐
相关产品推荐

