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

What am I doing wrong when signing my request? AWS SignatureV4 and PHP

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

现在我正在尝试将我的脚本转换为 Symfony 的 PHP 服务。当我 运行 我对 AWS pinpoint 的第一个请求时,我得到了这个:

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. The Canonical String for this request should have been\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:IQoJb....\nx-amz-user-agent:aws-amplify/1.1.2 react-native aws-amplify/1.1.2 react-native callback\n\ncontent-type;host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent\n0240a9479d0a66d74eaae42dc...95247aaa800fcbe5cf2

The String-to-Sign should have been 'AWS4-HMAC-SHA256\n20191226T151542Z\n20191226/eu-west-1/mobiletargeting/aws4_request\nb2c451534fe370503ecf4068b45c...63e91280cc3187ae3230034107

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

这是我的 headers 函数

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;
    }

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

谢谢!

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

在PHP中,“\n”和'\n'的含义不同。

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

AWS API 不过还是很烂。