签署我的请求时我做错了什么?AWS SignatureV4 和 PHP

Aze*_*pas 2 php request signature amazon-web-services

我已经被这个问题困了两天了。我编写了一个 Python 脚本,它向 AWS Pinpoint 服务发出 PUT 请求。与许多其他 AWS 服务一样,Pinpoint 需要对请求进行签名验证,我设法在 Python 中处理了这一点。

现在我正在尝试将我的脚本翻译成 Symfony 的 PHP 服务。当我向 AWS pinpoint 运行我的第一个请求时,我得到以下信息:

我们计算的请求签名与您提供的签名不匹配。检查您的 AWS 秘密访问密钥和签名方法。有关详细信息,请参阅服务文档。 此请求的规范字符串应该是\n'PUT\n/v1/apps/.../endpoints/...\n\ncontent-type:application/json\nhost:pinpoint.eu-west-1。 amazonaws.com\nx-amz-content-sha256:de98d86577f0e1...655e6de27154af1c05ab34\nx-amz-date:20191226T151542Z\nx-amz-security-token:IQoJ-agent...放大/1.1.2 反应原生 aws-amplify/1.1.2 反应原生回调\n\ncontent-type;host;x-amz-content-sha256;x-amz-date;x-amz-security-token; x-amz-user-agent\n0240a9479d0a66d74eaae42dc...95247aaa800fcbe5cf2

String-to-Sign 应该是 'AWS4-HMAC-SHA256\n20191226T151542Z\n20191226/eu-west-1/mobiletargeting/aws4_request\nb2c451534fe370503ecf4068b4912803e20191226T151542Z

所以我已经检查过我的规范字符串是否错误,这与 AWS 要求的完全相同。String-to-Sign 与 Canonical String 哈希不同。

这是我的标题功能

public function create_headers($data,\DateTime $time,$canonical_uri,$method,$to_api=null)
    {
        $amz_date = $time->format('Ymd\THis\Z');
        $date_stamp = $time->format('Ymd');
        $payload_hash = hash('sha256',$data);#utf8_encode($data));
        $canonical_querystring = "";
        $canonical_headers = 'content-type:' . $this->content_type . '\n' . 'host:' . $this->host . '\n' . 'x-amz-content-sha256:' . $payload_hash . '\n' . 'x-amz-date:' . $amz_date . '\n' . 'x-amz-security-token:' . $this->security_token . '\n' . 'x-amz-user-agent:aws-amplify/1.1.2 react-native aws-amplify/1.1.2 react-native callback' . '\n';
        $signed_headers = 'content-type;host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent';


        $canonical_request = $method . '\n' . $canonical_uri . '\n' . $canonical_querystring . '\n' . $canonical_headers . '\n' . $signed_headers . '\n' . $payload_hash;

        echo '<br><br>';
        print_r(str_replace('\n','<br>',$canonical_request));
        #var_dump($canonical_request);
        $algorithm = 'AWS4-HMAC-SHA256';
        $credential_scope = "{$date_stamp}/{$this->region}/{$this->service}/aws4_request";
            #$date_stamp . '/' . $this->region . '/' . $this->service . '/' . 'aws4_request';
        #$credential_scope = $this->createScope($date_stamp,$this->region,$this->service);
        echo '<br><br>';
        #$string_to_sign = $algorithm . '\n' .  $amz_date . '\n' .  $credential_scope . '\n' .  hash('sha256', utf8_encode($canonical_request));
        $hash = hash('sha256', $canonical_request);
        $string_to_sign = "AWS4-HMAC-SHA256\n{$amz_date}\n{$credential_scope}\n{$hash}";
        print_r(str_replace('\n','<br>',$string_to_sign));
        echo '<br><br>';



        $signing_key = $this->get_signature_key($this->secret_key,$date_stamp,$this->region,$this->service);
        $signature = hash_hmac('sha256',$string_to_sign,$signing_key);

        $authorization_header = $algorithm . ' ' . 'Credential=' . $this->access_key . '/' . $credential_scope . ', ' .  'SignedHeaders=' . $signed_headers . ', ' . 'Signature=' . $signature;

        $headers = array(
            'host'=> $this->host,
            'content-type'=> $this->content_type,
            'x-amz-user-agent'=> 'aws-amplify/1.1.2 react-native aws-amplify/1.1.2 react-native callback',
            'x-amz-content-sha256'=> $payload_hash,
            'x-amz-security-token'=> $this->security_token,
            'x-amz-date'=> $amz_date,
            'authorization'=> $authorization_header
        );
        $this->s->headers = $headers;
        return $headers;
    }
Run Code Online (Sandbox Code Playgroud)

几天来我一直在寻找我的错误,但我想我需要一个有新眼光的人......

谢谢!

Aze*_*pas 6

经过数小时的自我质疑,我终于找到了为什么会出现该错误的原因。

在 PHP 中 "\n" 和 '\n' 没有相同的含义。

"\n" 是一个真正的换行符 -这就是 AWS 所要求的
'\n' 是由 \ 和 n 个字符组成的字符串。

不过,AWS API 仍然很糟糕。