V4Signature.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <?php
  2. /**
  3. * Copyright 2019 Huawei Technologies Co.,Ltd.
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
  5. * this file except in compliance with the License. You may obtain a copy of the
  6. * License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software distributed
  11. * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  12. * CONDITIONS OF ANY KIND, either express or implied. See the License for the
  13. * specific language governing permissions and limitations under the License.
  14. *
  15. */
  16. namespace Obs\Internal\Signature;
  17. use Obs\Internal\Common\Model;
  18. class V4Signature extends AbstractSignature
  19. {
  20. const CONTENT_SHA256 = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
  21. protected $region;
  22. protected $utcTimeZone;
  23. public function __construct($ak, $sk, $pathStyle, $endpoint, $region, $methodName, $signature, $utcTimeZone, $securityToken = false, $isCname = false)
  24. {
  25. parent::__construct($ak, $sk, $pathStyle, $endpoint, $methodName, $signature, $securityToken, $isCname);
  26. $this->region = $region;
  27. $this->utcTimeZone = $utcTimeZone;
  28. }
  29. public function doAuth(array &$requestConfig, array &$params, Model $model)
  30. {
  31. $result = $this->prepareAuth($requestConfig, $params, $model);
  32. $result['headers']['x-amz-content-sha256'] = self::CONTENT_SHA256;
  33. $bucketName = $result['dnsParam'];
  34. $result['headers']['Host'] = $result['host'];
  35. $time = null;
  36. if (array_key_exists('x-amz-date', $result['headers'])) {
  37. $time = $result['headers']['x-amz-date'];
  38. } elseif (array_key_exists('X-Amz-Date', $result['headers'])) {
  39. $time = $result['headers']['X-Amz-Date'];
  40. } else {
  41. // nothing handle
  42. }
  43. $timestamp = $time ? date_create_from_format('Ymd\THis\Z', $time, $this->utcTimeZone)->getTimestamp()
  44. : time();
  45. $result['headers']['Date'] = gmdate('D, d M Y H:i:s \G\M\T', $timestamp);
  46. $longDate = gmdate('Ymd\THis\Z', $timestamp);
  47. $shortDate = substr($longDate, 0, 8);
  48. $credential = $this->getCredential($shortDate);
  49. $signedHeaders = $this->getSignedHeaders($result['headers']);
  50. $canonicalstring = $this->makeCanonicalstring($result['method'], $result['headers'], $result['pathArgs'], $bucketName, $result['uriParam'], $signedHeaders);
  51. $result['cannonicalRequest'] = $canonicalstring;
  52. $signature = $this->getSignature($canonicalstring, $longDate, $shortDate);
  53. $authorization = "AWS4-HMAC-SHA256 Credential={$credential},SignedHeaders={$signedHeaders},Signature={$signature}";
  54. $result['headers']['Authorization'] = $authorization;
  55. return $result;
  56. }
  57. public function getSignature($canonicalstring, $longDate, $shortDate)
  58. {
  59. $stringToSign = [];
  60. $stringToSign[] = 'AWS4-HMAC-SHA256';
  61. $stringToSign[] = "\n";
  62. $stringToSign[] = $longDate;
  63. $stringToSign[] = "\n";
  64. $stringToSign[] = $this->getScope($shortDate);
  65. $stringToSign[] = "\n";
  66. $stringToSign[] = hash('sha256', $canonicalstring);
  67. $dateKey = hash_hmac('sha256', $shortDate, 'AWS4' . $this->sk, true);
  68. $regionKey = hash_hmac('sha256', $this->region, $dateKey, true);
  69. $serviceKey = hash_hmac('sha256', 's3', $regionKey, true);
  70. $signingKey = hash_hmac('sha256', 'aws4_request', $serviceKey, true);
  71. return hash_hmac('sha256', implode('', $stringToSign), $signingKey);
  72. }
  73. public function getCanonicalQueryString($pathArgs)
  74. {
  75. $queryStr = '';
  76. ksort($pathArgs);
  77. $index = 0;
  78. foreach ($pathArgs as $key => $value) {
  79. $queryStr .= $key . '=' . $value;
  80. if ($index !== count($pathArgs) - 1) {
  81. $queryStr .= '&';
  82. }
  83. $index++;
  84. }
  85. return $queryStr;
  86. }
  87. public function getCanonicalHeaders($headers)
  88. {
  89. $headersResult = [];
  90. foreach ($headers as $key => $value) {
  91. $headersResult[strtolower($key)] = $value;
  92. }
  93. ksort($headersResult);
  94. $canonicalHeaderStr = '';
  95. foreach ($headersResult as $key => $value) {
  96. $value = is_array($value) ? implode(',', $value) : $value;
  97. $canonicalHeaderStr .= $key . ':' . $value;
  98. $canonicalHeaderStr .= "\n";
  99. }
  100. return $canonicalHeaderStr;
  101. }
  102. public function getCanonicalURI($bucketName, $objectKey)
  103. {
  104. $uri = '';
  105. if ($this->pathStyle && $bucketName) {
  106. $uri .= '/' . $bucketName;
  107. }
  108. if ($objectKey) {
  109. $uri .= '/' . $objectKey;
  110. }
  111. if ($uri === '') {
  112. $uri = '/';
  113. }
  114. return $uri;
  115. }
  116. public function makeCanonicalstring($method, $headers, $pathArgs, $bucketName, $objectKey, $signedHeaders = null, $payload = null)
  117. {
  118. $buffer = [];
  119. $buffer[] = $method;
  120. $buffer[] = "\n";
  121. $buffer[] = $this->getCanonicalURI($bucketName, $objectKey);
  122. $buffer[] = "\n";
  123. $buffer[] = $this->getCanonicalQueryString($pathArgs);
  124. $buffer[] = "\n";
  125. $buffer[] = $this->getCanonicalHeaders($headers);
  126. $buffer[] = "\n";
  127. $buffer[] = $signedHeaders ? $signedHeaders : $this->getSignedHeaders($headers);
  128. $buffer[] = "\n";
  129. $buffer[] = $payload ? strval($payload) : self::CONTENT_SHA256;
  130. return implode('', $buffer);
  131. }
  132. public function getSignedHeaders($headers)
  133. {
  134. $headersResult = [];
  135. foreach ($headers as $key => $value) {
  136. $headersResult[] = strtolower($key);
  137. }
  138. sort($headersResult);
  139. $signedHeaders = '';
  140. foreach ($headersResult as $key => $value) {
  141. $signedHeaders .= $value;
  142. if ($key !== count($headersResult) - 1) {
  143. $signedHeaders .= ';';
  144. }
  145. }
  146. return $signedHeaders;
  147. }
  148. public function getScope($shortDate)
  149. {
  150. return $shortDate . '/' . $this->region . '/s3/aws4_request';
  151. }
  152. public function getCredential($shortDate)
  153. {
  154. return $this->ak . '/' . $this->getScope($shortDate);
  155. }
  156. }