postman 签名版本4签名PHP无法工作

bnl4lu3b  于 2023-06-22  发布在  Postman
关注(0)|答案(1)|浏览(144)

我正在尝试使用IAM创建到Rekognition端点的授权签名。在Postman中使用Postman的内部 * AWS Signature * 可以正常工作,但是尝试生成Authorization Header + Signature(在PHP中)并手动输入它被证明是不可能的。

我已经看了以下问题和答案:

这些解决方案似乎对我都不起作用。我想做的是有一个PHP函数,生成授权头,日期头等,并将其粘贴到Postman,让它返回一个结果。
不管我尝试什么组合(省略payload,添加payload,添加param,省略params),我都得到:
The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.
是的,我已经检查了Access键,PHP脚本和Postman中的数据是相同的JSON-它与Postman的“AWS Signuature”一起工作
但是下面会不行吗?

我在这里尝试的代码:

function sign_request($url, $data) {
      
  $host                 = 'rekognition.eu-west-2.amazonaws.com';
  $access_key               = '<<Acess_Key>>';
  $secret_key           = '<<Secret_Key>>';
  $region               = '<<Region>>';
  $service              = 'rekognition';

  $current = new DateTime('UTC');
  $current_date_time = $current->format('Ymd\THis\Z');
  $current_date = $current->format('Ymd');

  $signed_headers = [
    'content-type' => 'application/x-amz-json-1.1',
    'host' => $host,
    'x-amz-date' => $current_date_time,
    'x-amz-target' => 'RekognitionService.DetectFaces'
    //'x-amz-security-token' => $token, // leave this one out if you have a IAM created fixed access key - secret pair and do not need the token.
  ];
  $signed_headers_string = implode(';', array_keys($signed_headers));

  $canonical = [
    'POST',
    parse_url($url, PHP_URL_PATH),
    parse_url($url, PHP_URL_QUERY), 
  ];
  
  foreach ($signed_headers as $header => $value) {
    $canonical[] = "$header:$value";
  }
  $canonical[] = ''; 
  $canonical[] = $signed_headers_string;
  $canonical[] = hash('sha256', http_build_query($data));
  $canonical = implode("\n", $canonical);

  $credential_scope = [$current_date, $region, $service, 'aws4_request'];
  $key = array_reduce($credential_scope, fn ($key, $credential) => hash_hmac('sha256', $credential, $key, TRUE), 'AWS4' . $secret_key);
  $credential_scope = implode('/', $credential_scope);

  $string_to_sign = implode("\n", [
    'AWS4-HMAC-SHA256',
    $current_date_time,
    $credential_scope,
    hash('sha256', $canonical),
  ]);
  $signature = hash_hmac('sha256', $string_to_sign, $key);

  unset($signed_headers['host']);
  $signed_headers['Authorization'] = "AWS4-HMAC-SHA256 Credential=$access_key/$credential_scope, SignedHeaders=$signed_headers_string, Signature=$signature";
  return $signed_headers;
}

$requestUrl = 'https://rekognition.eu-west-2.amazonaws.com/?Action=DetectFaces';
$body = [
    "Attributes"=> [
        "ALL"
    ],
    "Image"> [
        "Bytes"=> "<<BASE64_of_Face_Image>>"
    ]
];

print_r(sign_request($requestUrl, $body ));

我错过了什么吗?

zvms9eto

zvms9eto1#

我通过将x-amz-content-sha256添加到上面的$signed_headers数组来使它工作。
标题也必须按字母顺序排列,如下所述:https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html部分StringToSign

$signed_headers = [
'content-type' => 'application/x-amz-json-1.1',
'host' => $host,
'x-amz-content-sha256' => hash('sha256', $data),
'x-amz-date' => $current_date_time,
'x-amz-target' => 'RekognitionService.DetectFaces',
//'x-amz-security-token' => $token, // leave this one out if you have a IAM created fixed access key - secret pair and do not need the token.
  ];

需要说明的是,StringtoSignCanonicalRequest中需要哈希负载
Amazon doc上面:The x-amz-content-sha256 header is required for all AWS Signature Version 4 requests. It provides a hash of the request payload. If there is no payload, you must provide the hash of an empty string.

host:examplebucket.s3.amazonaws.com
range:bytes=0-9
x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-amz-date:20130524T000000Z

host;range;x-amz-content-sha256;x-amz-date
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

新代码:

function sign_request($url, $data) {
      
  $host                 = 'rekognition.eu-west-2.amazonaws.com';
  $access_key               = '<<Acess_Key>>';
  $secret_key           = '<<Secret_Key>>';
  $region               = '<<Region>>';
  $service              = 'rekognition';

  $current = new DateTime('UTC');
  $current_date_time = $current->format('Ymd\THis\Z');
  $current_date = $current->format('Ymd');

  $signed_headers = [
    'content-type' => 'application/x-amz-json-1.1',
    'host' => $host,
    'x-amz-content-sha256' => hash('sha256', $data),
    'x-amz-date' => $current_date_time,
    'x-amz-target' => 'RekognitionService.DetectFaces',
    //'x-amz-security-token' => $token, // leave this one out if you have a IAM created fixed access key - secret pair and do not need the token.
  ];
  $signed_headers_string = implode(';', array_keys($signed_headers));

  $canonical = [
    'POST',
    parse_url($url, PHP_URL_PATH),
    parse_url($url, PHP_URL_QUERY), 
  ];
  
  
  
  foreach ($signed_headers as $header => $value) {
    $canonical[] = "$header:$value";
  }
  $canonical[] = ''; 
  $canonical[] = $signed_headers_string;
  $canonical[] = (hash('sha256', ($data)));
  //print_r($canonical);
  
  $canonical = implode("\n", $canonical);

  $credential_scope = [$current_date, $region, $service, 'aws4_request'];
  $key = array_reduce($credential_scope, fn ($key, $credential) => hash_hmac('sha256', $credential, $key, TRUE), 'AWS4' . $secret_key);
  $credential_scope = implode('/', $credential_scope);

  $string_to_sign = implode("\n", [
    'AWS4-HMAC-SHA256',
    $current_date_time,
    $credential_scope,
    hash('sha256', $canonical),
  ]);
  echo hash('sha256', $canonical)."\n\n";
  $signature = hash_hmac('sha256', $string_to_sign, $key);

  //unset($signed_headers['host']);
  $signed_headers['Authorization'] = "AWS4-HMAC-SHA256 Credential=$access_key/$credential_scope, SignedHeaders=$signed_headers_string, Signature=$signature";
  return $signed_headers;
}

$requestUrl = 'https://rekognition.eu-west-2.amazonaws.com/?Action=DetectFaces';
$data = json_encode([
    "Attributes" => [
        "ALL"
    ],
    "Image" => [
        "Bytes"=> "<<BASE64_FACE_IMG>>"
    ]
]);

print_r(sign_request($requestUrl, $data ));

相关问题