BceV1Signer.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. <?php
  2. require_once(XASSET_PATH . 'auth/SignerInterface.php');
  3. require_once(XASSET_PATH . 'auth/SignOptions.php');
  4. require_once(XASSET_PATH . 'common/config/HttpHeaders.php');
  5. require_once(XASSET_PATH . 'auth/HttpUtils.php');
  6. require_once(XASSET_PATH . 'auth/DateUtils.php');
  7. /**
  8. * The V1 implementation of Signer with the BCE signing protocol.
  9. */
  10. class BceV1Signer implements SignerInterface
  11. {
  12. const BCE_AUTH_VERSION = "bce-auth-v1";
  13. // Default headers to sign with the BCE signing protocol.
  14. private static $defaultHeadersToSign;
  15. public static function __init()
  16. {
  17. BceV1Signer::$defaultHeadersToSign = array(
  18. strtolower(HttpHeaders::HOST),
  19. strtolower(HttpHeaders::CONTENT_LENGTH),
  20. strtolower(HttpHeaders::CONTENT_TYPE),
  21. strtolower(HttpHeaders::CONTENT_MD5),
  22. );
  23. }
  24. /**
  25. * Sign the given request with the given set of credentials. Modifies the passed-in request to apply the signature.
  26. *
  27. * @param $credentials array the credentials to sign the request with.
  28. * @param $httpMethod string
  29. * @param $path string
  30. * @param $headers array
  31. * @param $params array
  32. * @param $options array the options for signing.
  33. * @return string The signed authorization string.
  34. */
  35. public function sign(
  36. array $credentials,
  37. $httpMethod,
  38. $path,
  39. $headers,
  40. $params,
  41. $options = array()
  42. )
  43. {
  44. if (!isset($options[SignOptions::EXPIRATION_IN_SECONDS])) {
  45. $expirationInSeconds = SignOptions::DEFAULT_EXPIRATION_IN_SECONDS;
  46. } else {
  47. $expirationInSeconds = $options[SignOptions::EXPIRATION_IN_SECONDS];
  48. }
  49. // to compatible with ak/sk or accessKeyId/secretAccessKey
  50. if (isset($credentials['ak'])) {
  51. $accessKeyId = $credentials['ak'];
  52. }
  53. if (isset($credentials['sk'])) {
  54. $secretAccessKey = $credentials['sk'];
  55. }
  56. if (isset($credentials['accessKeyId'])) {
  57. $accessKeyId = $credentials['accessKeyId'];
  58. }
  59. if (isset($credentials['secretAccessKey'])) {
  60. $secretAccessKey = $credentials['secretAccessKey'];
  61. }
  62. if (isset($options[SignOptions::TIMESTAMP])) {
  63. $timestamp = $options[SignOptions::TIMESTAMP];
  64. } else {
  65. $timestamp = new \DateTime();
  66. }
  67. $timestamp->setTimezone(DateUtils::$UTC_TIMEZONE);
  68. $authString = BceV1Signer::BCE_AUTH_VERSION . '/' . $accessKeyId . '/'
  69. . DateUtils::formatAlternateIso8601Date(
  70. $timestamp
  71. ) . '/' . $expirationInSeconds;
  72. $signingKey = hash_hmac('sha256', $authString, $secretAccessKey);
  73. // Formatting the URL with signing protocol.
  74. $canonicalURI = BceV1Signer::getCanonicalURIPath($path);
  75. // Formatting the query string with signing protocol.
  76. $canonicalQueryString = HttpUtils::getCanonicalQueryString(
  77. $params,
  78. true
  79. );
  80. // Sorted the headers should be signed from the request.
  81. $headersToSignOption = null;
  82. if (isset($options[SignOptions::HEADERS_TO_SIGN])) {
  83. $headersToSignOption = $options[SignOptions::HEADERS_TO_SIGN];
  84. }
  85. $headersToSign = BceV1Signer::getHeadersToSign($headers, $headersToSignOption);
  86. // Formatting the headers from the request based on signing protocol.
  87. $canonicalHeader = BceV1Signer::getCanonicalHeaders($headersToSign);
  88. $headersToSign = array_keys($headersToSign);
  89. sort($headersToSign);
  90. $signedHeaders = '';
  91. if ($headersToSignOption !== null) {
  92. $signedHeaders = strtolower(
  93. trim(implode(";", $headersToSign))
  94. );
  95. }
  96. $canonicalRequest = "$httpMethod\n$canonicalURI\n" . "$canonicalQueryString\n$canonicalHeader";
  97. // Signing the canonical request using key with sha-256 algorithm.
  98. $signature = hash_hmac('sha256', $canonicalRequest, $signingKey);
  99. $authorizationHeader = "$authString/$signedHeaders/$signature";
  100. return $authorizationHeader;
  101. }
  102. /**
  103. * @param $path string
  104. * @return string
  105. */
  106. private static function getCanonicalURIPath($path)
  107. {
  108. if (empty($path)) {
  109. return '/';
  110. } else {
  111. if ($path[0] == '/') {
  112. return HttpUtils::urlEncodeExceptSlash($path);
  113. } else {
  114. return '/' . HttpUtils::urlEncodeExceptSlash($path);
  115. }
  116. }
  117. }
  118. /**
  119. * @param $headers array
  120. * @return string
  121. */
  122. private static function getCanonicalHeaders($headers)
  123. {
  124. if (count($headers) == 0) {
  125. return '';
  126. }
  127. $headerStrings = array();
  128. foreach ($headers as $k => $v) {
  129. if ($k === null) {
  130. continue;
  131. }
  132. if ($v === null) {
  133. $v = '';
  134. }
  135. $headerStrings[] = rawurlencode(
  136. strtolower(trim($k))
  137. ) . ':' . rawurlencode(trim($v));
  138. }
  139. sort($headerStrings);
  140. return implode("\n", $headerStrings);
  141. }
  142. /**
  143. * @param $headers array
  144. * @param $headersToSign array
  145. * @return array
  146. */
  147. private static function getHeadersToSign($headers, $headersToSign)
  148. {
  149. $ret = array();
  150. if ($headersToSign !== null) {
  151. $tmp = array();
  152. foreach ($headersToSign as $header) {
  153. $tmp[] = strtolower(trim($header));
  154. }
  155. $headersToSign = $tmp;
  156. }
  157. foreach ($headers as $k => $v) {
  158. if (trim((string)$v) !== '') {
  159. if ($headersToSign !== null) {
  160. if (in_array(strtolower(trim($k)), $headersToSign)) {
  161. $ret[$k] = $v;
  162. }
  163. } else {
  164. if (BceV1Signer::isDefaultHeaderToSign($k)
  165. ) {
  166. $ret[$k] = $v;
  167. }
  168. }
  169. }
  170. }
  171. return $ret;
  172. }
  173. /**
  174. * @param $header string
  175. * @return bool
  176. */
  177. private static function isDefaultHeaderToSign($header)
  178. {
  179. $header = strtolower(trim($header));
  180. if (in_array($header, BceV1Signer::$defaultHeadersToSign)) {
  181. return true;
  182. }
  183. $prefix = substr($header, 0, strlen(HttpHeaders::BCE_PREFIX));
  184. if ($prefix === HttpHeaders::BCE_PREFIX) {
  185. return true;
  186. } else {
  187. return false;
  188. }
  189. }
  190. }
  191. BceV1Signer::__init();