chengkun
2025-09-15 3c9050e82e582414dc7b208c8283fe47be37eeba
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<?php
namespace DoubleBreak\Spapi;
use GuzzleHttp\Psr7\Request;
 
class Signer {
 
  /**
   * calculateSignature data
   *
   * @return array request with signed headers
   */
 
  public static function sign($request, $signOptions)
  {
    //required
    $service = $signOptions['service'] ?? null;
    $accessKey = $signOptions['access_key'] ?? null;
    $secretKey = $signOptions['secret_key'] ?? null;
 
    $region = $signOptions['region'] ?? null;
    $host = $signOptions['host'] ?? null;
    $method = $signOptions['method'] ?? null;
 
    //optionl
    $accessToken = $signOptions['access_token'] ?? null;
    $securityToken = $signOptions['security_token'] ?? null;
    $userAgent = $signOptions['user_agent'] ?? 'spapi_client';
    $queryString = $signOptions['query_string'] ?? '';
    $data = $signOptions['payload'] ?? [];
    $uri = $signOptions['uri'] ?? '';
 
    if (is_null($service)) throw new SignerException("Service is required");
    if (is_null($accessKey)) throw new SignerException("Access key is required");
    if (is_null($secretKey)) throw new SignerException("Secret key is required");
    if (is_null($region)) throw new SignerException("Region key is required");
    if (is_null($host)) throw new SignerException("Host key is required");
    if (is_null($method)) throw new SignerException("Method key is required");
 
    $terminationString = 'aws4_request';
    $algorithm = 'AWS4-HMAC-SHA256';
    $amzdate = gmdate('Ymd\THis\Z');
    $date = substr($amzdate, 0, 8);
 
    //Prepare payload hash
    if (is_array($data)) {
      $param = json_encode($data);
      if ($param == "[]") {
          $requestPayload = "";
      } else {
          $requestPayload = $param;
      }
    } else {
      $requestPayload = $data;
    }
    $hashedPayload = hash('sha256', $requestPayload);
 
 
    //Compute Canonical Headers
    $canonicalHeaders = [
      'host' => $host,
      'user-agent' => $userAgent,
    ];
    if (!is_null($accessToken)) {
      $canonicalHeaders['x-amz-access-token'] = $accessToken;
    }
    $canonicalHeaders['x-amz-date'] = $amzdate;
    if (!is_null($securityToken)) {
      $canonicalHeaders['x-amz-security-token'] = $securityToken;
    }
 
    $canonicalHeadersStr = '';
    foreach($canonicalHeaders as $h => $v) {
      $canonicalHeadersStr .= $h . ':' . $v . "\n";
    }
    $signedHeadersStr = join(';' , array_keys($canonicalHeaders));
 
    //Prepare credentials scope
    $credentialScope = $date . '/' . $region . '/' . $service . '/' . $terminationString;
 
    //prepare canonical request
    $canonicalRequest = $method . "\n" . $uri . "\n" . $queryString . "\n" . $canonicalHeadersStr . "\n" . $signedHeadersStr . "\n" . $hashedPayload;
 
    //Prepare the signature payload
    $stringToSign = $algorithm . "\n" . $amzdate . "\n" . $credentialScope . "\n" . hash('sha256', $canonicalRequest);
 
    //Prepare lockers
    $kSecret = "AWS4" . $secretKey;
    $kDate = hash_hmac('sha256', $date, $kSecret, true);
    $kRegion = hash_hmac('sha256', $region, $kDate, true);
    $kService = hash_hmac('sha256', $service, $kRegion, true);
    $kSigning = hash_hmac('sha256', $terminationString, $kService, true);
 
    //Compute the signature
    $signature = trim(hash_hmac('sha256', $stringToSign, $kSigning)); // Without fourth parameter passed as true, returns lowercase hexits as called for by docs
 
    $authorizationHeader = $algorithm . " Credential={$accessKey}/{$credentialScope}, SignedHeaders={$signedHeadersStr}, Signature={$signature}";
 
 
    $headers = array_merge($canonicalHeaders, [
        "Authorization" => $authorizationHeader,
    ]);
 
    $request['headers'] = array_merge($request['headers'], $headers);
 
    return $request;
  }
}