/** * Signs request with signature version 4 * * @param Request $request Http Request * @param string $region optional Overrides region as destination region for multi-regional operations * @param string $file optional Path to the file transmitted in request for signature calculation * * @throws QueryClientException */ protected function signRequestV4($request, $region = null, $file = null) { $time = time(); //Gets the http method name $httpMethod = $request->getRequestMethod(); //Region is mandatory part for this type of authentication $region = $region ?: $this->getAws()->getRegion(); //Gets host from the url $components = parse_url($request->getRequestUrl()); $crd = gmdate('Ymd', $time) . '/' . $region . '/' . $this->getServiceName() . '/aws4_request'; $opt = ['X-Amz-Algorithm' => 'AWS4-HMAC-SHA256', 'X-Amz-Credential' => $this->awsAccessKeyId . '/' . $crd, 'X-Amz-Date' => gmdate('Ymd\\THis\\Z', $time), 'X-Amz-Expires' => '86400']; $headers = ['X-Amz-Date' => $opt['X-Amz-Date']]; //Calculating canonicalized query string $c11dQueryString = ''; if (!empty($components['query'])) { $pars = (new QueryString($components['query']))->toArray(); ksort($pars); $c11dQueryString = http_build_query($pars, null, '&', PHP_QUERY_RFC3986); } //Calculating payload if ($httpMethod == 'PUT' && isset($file)) { $headers['X-Amz-Content-Sha256'] = hash_file('sha256', $file); } if (!isset($headers['X-Amz-Content-Sha256'])) { $headers['X-Amz-Content-Sha256'] = hash('sha256', $request->getRawBody()); } //Adding x-amz headers $request->addHeaders($headers); //Calculating canonical headers $allHeaders = $request->getHeaders(); $allHeaders['X-Amz-SignedHeaders'] = 'host'; $signedHeaders = ['host' => $components['host']]; foreach ($allHeaders as $k => $v) { $lk = strtolower($k); //This x-amz header does not have to be signed if ($lk == 'x-amz-signedheaders') { continue; } if (preg_match('/x-amz-/i', $k) || $lk == 'content-type' || $lk == 'content-md5') { $signedHeaders[$lk] = $v; } } ksort($signedHeaders); $allHeaders['X-Amz-SignedHeaders'] = join(';', array_keys($signedHeaders)); $canonicalHeaders = ''; foreach ($signedHeaders as $k => $v) { $canonicalHeaders .= $k . ':' . $v . "\n"; } $canonicalRequest = $httpMethod . "\n" . $components['path'] . "\n" . $c11dQueryString . "\n" . $canonicalHeaders . "\n" . $allHeaders['X-Amz-SignedHeaders'] . "\n" . $headers['X-Amz-Content-Sha256']; $stringToSign = $opt['X-Amz-Algorithm'] . "\n" . $opt['X-Amz-Date'] . "\n" . $crd . "\n" . hash('sha256', $canonicalRequest); $dateKey = hash_hmac('sha256', gmdate('Ymd', $time), "AWS4" . $this->secretAccessKey, true); $dateRegionKey = hash_hmac('sha256', $region, $dateKey, true); $dateRegionServiceKey = hash_hmac('sha256', $this->getServiceName(), $dateRegionKey, true); $signingKey = hash_hmac('sha256', 'aws4_request', $dateRegionServiceKey, true); //X-Amz-Signature $signature = hash_hmac('sha256', $stringToSign, $signingKey); $headers['Authorization'] = $opt['X-Amz-Algorithm'] . ' ' . 'Credential=' . $opt['X-Amz-Credential'] . ',' . 'SignedHeaders=' . $allHeaders['X-Amz-SignedHeaders'] . ',' . 'Signature=' . $signature; $request->addHeaders($headers); }