Skip to content

Instantly share code, notes, and snippets.

@porjo
Created April 16, 2019 05:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save porjo/9de1ea33329ddf9049599564d8e19a9d to your computer and use it in GitHub Desktop.
Save porjo/9de1ea33329ddf9049599564d8e19a9d to your computer and use it in GitHub Desktop.
PHP function to generate curl headers suitable for signed requests agains AWS API
// Sign the request and return header array for use by curl
//
// For HTTP methods with no payload (GET,DELETE) leave $Payload , $ContentType empty
//
// Based on: http://usefulangle.com/post/34/aws-s3-upload-api-php-curl
private function SignRequest($Method, $URL, $Payload='', $ContentType='')
{
if(!defined('AWS_ACCESS_KEY_ID') || !defined('AWS_SECRET_ACCESS_KEY') || !defined('AWS_REGION'))
return null;
// AWS API keys
$aws_access_key_id = AWS_ACCESS_KEY_ID;
$aws_secret_access_key = AWS_SECRET_ACCESS_KEY;
$aws_region = AWS_REGION;
$aws_service_name = 'es';
// UTC timestamp and date
$timestamp = gmdate('Ymd\THis\Z');
$date = gmdate('Ymd');
$URLBits = parse_url($URL);
$host_name = $URLBits['host'];
$path = $URLBits['path'];
$queryStr = $URLBits['query'];
// HTTP request headers as key & value
$request_headers = array();
if($ContentType)
$request_headers['Content-Type'] = $ContentType;
$request_headers['x-amz-date'] = $timestamp;
$request_headers['Host'] = $host_name;
// Sort it in ascending order
ksort($request_headers);
// Canonical headers
$canonical_headers = [];
foreach($request_headers as $key => $value) {
$canonical_headers[] = strtolower($key) . ":" . $value;
}
$canonical_headers = implode("\n", $canonical_headers);
// Signed headers
$signed_headers = [];
foreach($request_headers as $key => $value) {
$signed_headers[] = strtolower($key);
}
$signed_headers = implode(";", $signed_headers);
// AWS requires bits between slashes to be uri encoded
$pathSegments = explode('/', $path);
foreach($pathSegments as $i => $ps)
{
$pathSegments[$i] = rawurlencode($ps);
}
// uri encode query string
parse_str($queryStr, $queryVals);
ksort($queryVals);
$queryStr = '';
foreach($queryVals as $key => $val)
{
$queryStr .= rawurlencode($key)."=".rawurlencode($val).'&';
}
$queryStr = rtrim($queryStr, '&');
// Cannonical request
$canonical_request = [];
$canonical_request[] = $Method;
$canonical_request[] = implode('/', $pathSegments);
$canonical_request[] = $queryStr;
$canonical_request[] = $canonical_headers;
$canonical_request[] = "";
$canonical_request[] = $signed_headers;
$canonical_request[] = hash('sha256', $Payload);
$canonical_request = implode("\n", $canonical_request);
$hashed_canonical_request = hash('sha256', $canonical_request);
// AWS Scope
$scope = [];
$scope[] = $date;
$scope[] = $aws_region;
$scope[] = $aws_service_name;
$scope[] = "aws4_request";
// String to sign
$string_to_sign = [];
$string_to_sign[] = "AWS4-HMAC-SHA256";
$string_to_sign[] = $timestamp;
$string_to_sign[] = implode('/', $scope);
$string_to_sign[] = $hashed_canonical_request;
$string_to_sign = implode("\n", $string_to_sign);
// Signing key
$kSecret = 'AWS4' . $aws_secret_access_key;
$kDate = hash_hmac('sha256', $date, $kSecret, true);
$kRegion = hash_hmac('sha256', $aws_region, $kDate, true);
$kService = hash_hmac('sha256', $aws_service_name, $kRegion, true);
$kSigning = hash_hmac('sha256', 'aws4_request', $kService, true);
// Signature
$signature = hash_hmac('sha256', $string_to_sign, $kSigning);
// Authorization
$authorization = [
'Credential=' . $aws_access_key_id . '/' . implode('/', $scope),
'SignedHeaders=' . $signed_headers,
'Signature=' . $signature
];
$authorization = 'AWS4-HMAC-SHA256' . ' ' . implode( ',', $authorization);
// Curl headers
$curl_headers = [ 'Authorization: ' . $authorization ];
foreach($request_headers as $key => $value) {
$curl_headers[] = $key . ": " . $value;
}
return $curl_headers;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment