Closed jaswrks closed 9 years ago
Holy hell! I dunno what the folks at AWS are thinking! Their signature algo is just ridiculous. haha
All of this just to get a signature for the Authorization
header. Unbelievable! Gotta love the ridiculous attempt at making it more complex by nesting a date key inside a region key, inside a date/region key, just to get the key that you should sign with. On top of that there are a whole bunch of other stars that need to fall inline. Have they never heard of oAuth?
~ Ugh, 4 hours I'll never get back. It's working now though. I'll merge it in shortly :-)
This is one article I hope I don't need to read again anytime soon. http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
/**
* Creates an Amazon S3 AWS4-HMAC-SHA256 signature.
*
* @package s2Member\Files
* @since 150108
*
* @param string $string Input string/data, to be signed by this routine.
*
* @return string An AWS4-HMAC-SHA256 signature for Amazon S3.
*/
public static function amazon_s34_sign($string = '')
{
$s3c = array(); // Initialize config. keys.
foreach($GLOBALS['WS_PLUGIN__']['s2member']['o'] as $option => $option_value)
if(preg_match('/^amazon_s3_files_/', $option) && ($option = preg_replace('/^amazon_s3_files_/', '', $option)))
$s3c[$option] = $option_value;
$s3_date_key = c_ws_plugin__s2member_utils_strings::hmac_sha256_sign(date('Ymd'), 'AWS4'.$s3c['secret_key'], TRUE);
$s3_date_region_key = c_ws_plugin__s2member_utils_strings::hmac_sha256_sign($s3c['bucket_region'], $s3_date_key, TRUE);
$s3_date_region_service_key = c_ws_plugin__s2member_utils_strings::hmac_sha256_sign('s3', $s3_date_region_key, TRUE);
$s3_signing_key = c_ws_plugin__s2member_utils_strings::hmac_sha256_sign('aws4_request', $s3_date_region_service_key, TRUE);
return c_ws_plugin__s2member_utils_strings::hmac_sha256_sign((string)$string, $s3_signing_key);
}
/**
* Creates an Amazon S3 AWS4-HMAC-SHA256 signature/authorization header.
*
* @package s2Member\Files
* @since 150108
*
* @param string $s3_date The date header; e.g. `YYYYMMDD'T'HHMMSS'Z'`.
* @param string $s3_domain The API endpoint domain; e.g. `[bucket].s3.amazonaws.com`.
* @param string $s3_location The API endpoint URI; e.g. `/?acl`.
* @param string $s3_method The request method; e.g. `GET`, `PUT`, `POST`, etc.
* @param array $s3_headers An associative array of all headers.
* @param string $s3_body Any input data sent with the request.
*
* @return string An AWS4-HMAC-SHA256 signature/authorization header for Amazon S3.
*/
public static function amazon_s34_authorization($s3_date = '',
$s3_domain = 's3.amazonaws.com',
$s3_location = '/', $s3_method = 'GET',
$s3_headers = array(), $s3_body = '')
{
$s3_date = trim((string)$s3_date);
$s3_domain = trim(strtolower((string)$s3_domain));
$s3_location = trim((string)$s3_location);
$s3_method = trim(strtoupper((string)$s3_method));
$s3_headers = (array)$s3_headers;
$s3_body = trim((string)$s3_body);
$s3c = array(); // Initialize config. keys.
foreach($GLOBALS['WS_PLUGIN__']['s2member']['o'] as $option => $option_value)
if(preg_match('/^amazon_s3_files_/', $option) && ($option = preg_replace('/^amazon_s3_files_/', '', $option)))
$s3c[$option] = $option_value;
$s3_iso8601_date = date('Ymd\THis\Z');
$s3_location_parts = parse_url($s3_location);
$s3_canonical_path = !empty($s3_location_parts['path']) ? '/'.ltrim($s3_location_parts['path'], '/') : '/';
$s3_scope = date('Ymd').'/'.$s3c['bucket_region'].'/s3/aws4_request';
$s3_canonical_query = ''; // Initialize.
wp_parse_str((string)@$s3_location_parts['query'], $query_args);
ksort($query_args, SORT_STRING);
foreach($query_args as $_key => $_value)
$s3_canonical_query .= '&'.c_ws_plugin__s2member_utils_strings::urldecode_ur_chars_deep(rawurlencode($_key)).
'='.c_ws_plugin__s2member_utils_strings::urldecode_ur_chars_deep(rawurlencode($_value));
$s3_canonical_query = ltrim($s3_canonical_query, '&');
unset($_key, $_value); // Housekeeping.
$s3_canonical_headers = '';
$s3_canonical_header_keys = array();
ksort($s3_headers, SORT_STRING);
foreach($s3_headers as $_key => $_value)
if(is_string($_key) && ($_key = strtolower($_key)))
if(in_array($_key, array('host', 'content-type'), TRUE) || stripos($_key, 'X-Amz-') === 0)
{
$s3_canonical_headers .= strtolower($_key).':'.trim($_value)."\n";
$s3_canonical_header_keys[] = strtolower($_key);
}
unset($_key, $_value); // Housekeeping.
$s3_canonicial_request = $s3_method."\n".
$s3_canonical_path."\n".
$s3_canonical_query."\n".
$s3_canonical_headers."\n".
implode(';', $s3_canonical_header_keys)."\n".
hash('sha256', $s3_body);
$s3_string_to_sign = 'AWS4-HMAC-SHA256'."\n".
$s3_date."\n".
$s3_scope."\n".
hash('sha256', $s3_canonicial_request);
$s3_signature = self::amazon_s34_sign($s3_string_to_sign);
$s3_authorization_header_signature = 'AWS4-HMAC-SHA256 Credential='.$s3c['access_key'].'/'.$s3_scope.','.
'SignedHeaders='.implode(';', $s3_canonical_header_keys).','.
'Signature='.$s3_signature;
return $s3_authorization_header_signature;
}
ha ~ I'm deleting 38 CloudFront Distros I created while testing this. That's funny :smile:
Reopening this issue. It's come to my attention that digital signatures leading to specific resources may also require AWS4-HMAC-SHA256
in some regions.
Next Release Changelog:
This issue was resolved in the release of s2Member v150203. Please see: http://www.s2member.com/kb/v150203/ If there are any follow-ups needed, please open a new issue and we will investigate. Thanks!
Referencing: http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html