SDKを使わないで自分で作ってみる
この記事は参考程度と思ってください。環境ごとに値が違うので。
参考になるのは、「完全なバージョン 4 署名プロセスの例 (Python)」
参考ドキュメントはこちら
1.署名バージョン 4 の正規リクエストを作成する
CanonicalRequest = HTTPRequestMethod + '\n' + CanonicalURI + '\n' + CanonicalQueryString + '\n' + CanonicalHeaders + '\n' + SignedHeaders + '\n' + HexEncode(Hash(RequestPayload))
上記をPHPに置き換えると。
private function CanonicalRequest($canonicalRequest) { $str = $canonicalRequest['method'] . "\n" . $canonicalRequest['uri'] . "\n" . $canonicalRequest['query'] . "\n"; if (count($canonicalRequest['headers']) > 0) { foreach ($canonicalRequest['headers'] as $item) { $str .= $item . "\n"; } } else { $str .= "\n"; } $str .= "\n"; $str .=$canonicalRequest['signedheaders'] . "\n" . $canonicalRequest['payload']; return hash('sha256', $str, true); }
呼び出し側はこちら(payloadはPOSTなどのRAWデータが無ければこのまま)
$headers = [ 'content-type:application/json;charset=UTF-8', 'host:' . { host }, 'x-amz-date:' . gmdate("Ymd\THis\Z") ]; $canonicalRequest = [ 'method' => 'GET', 'uri' => '/test', 'query' => '', 'headers' => $headers, 'signedheaders' => 'content-type;host;x-amz-date', 'payload' => 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' ]; $canonical_request = $this->CanonicalRequest($canonicalRequest);
ヘッダが増えれば、signedheadersも変わるのと、アルファベット順である必要があります。
payloadの求め方は、$payload = hash(‘sha256’, ”);
2.署名バージョン 4 の署名文字列を作成する
StringToSign = Algorithm + \n + RequestDateTime + \n + CredentialScope + \n + HashedCanonicalRequest
PHP変換後、
private function signature($param, $canonical_request, $signing_key) { $credential_scope = $param['datestamp'] . "/" . { region } . "/" . { service } . "/" . 'aws4_request'; $string_to_sign = { algorithm } . "\n" // 'AWS4-HMAC-SHA256' . $param['amzdate'] . "\n" . $credential_scope . "\n" . bin2hex($canonical_request); return hash_hmac('sha256', $string_to_sign, $signing_key, true); }
呼び出し側
$gmtDate = gmdate("Ymd\THis\Z"); $param['amzdate'] = $gmtDate; $param['datestamp'] = substr($gmtDate, 0, 8); $signature = $this->signature( $param, $canonical_request, // 1の正規リクエストで生成したデータ $signing_key); // 3の署名データ
なぜか3の処理が入ってきてるけどドキュメント上はこちらが先だったり
3.AWS 署名バージョン 4 の署名を計算する
kSecret = your secret access key kDate = HMAC("AWS4" + kSecret, Date) kRegion = HMAC(kDate, Region) kService = HMAC(kRegion, Service) kSigning = HMAC(kService, "aws4_request")
PHP変換後は、
private function signatureKey($key, $dateStamp, $regionName, $serviceName) { $kDate = hash_hmac('sha256', $dateStamp, 'AWS4'.$key, true); //echo 'kDate : '.bin2hex($kDate); // 中身を確認するときはこちらで $kRegion = hash_hmac('sha256', $regionName, $kDate, true); $kService = hash_hmac('sha256', $serviceName, $kRegion, true); $kSigning = hash_hmac('sha256', 'aws4_request', $kService, true); return $kSigning; }
呼び出し側はこちら
$gmtDate = gmdate("Ymd\THis\Z"); $param['amzdate'] = $gmtDate; $param['datestamp'] = substr($gmtDate, 0, 8); $signing_key = $this->signatureKey( { secretkey }, $datestamp, { region }, { service });
hash_hmacのバイナリで行う必要があります。デバッグにbin2hexを使うと確認できます。
secretkeyは一時認証などで取得できます。
4.署名情報をリクエストに追加する
Authorization: algorithm Credential=access key ID/credential scope, SignedHeaders=SignedHeaders, Signature=signature
いままでの処理はこの1行を作るためのもので、やっとゴールです。
private function createAuthorization($param, $canonicalRequest, $signature) { $credential = { accesskey } . "/" . $param['datestamp'] . "/" . { region } . "/" . { service } . "/" . 'aws4_request'; $signedHeaders = $canonicalRequest['signedheaders']; $auth = { algorithm } . ' ' // 'AWS4-HMAC-SHA256' . 'Credential=' . $credential . ', ' . 'SignedHeaders=' . $signedHeaders . ', ' . 'Signature=' . bin2hex($signature); return $auth; }
呼び出し元は
$gmtDate = gmdate("Ymd\THis\Z"); $param['amzdate'] = $gmtDate; $param['datestamp'] = substr($gmtDate, 0, 8); $canonicalRequest = [ ... 'signedheaders' => 'content-type;host;x-amz-date', ... ]; $auth = $this->createAuthorization( $param, $canonicalRequest, $signature); // 3で生成したデータ $authorization = 'Authorization:' . $auth;
おおまかにですが、こんな感じになります。一部は環境ごとに違ったり別処理が必要だったり
実際開発時や確認はPostmanを利用するほうが楽になります。
PHP-SDKが使えればよかったんですが、認証の一時発行が外部から動作しない?のか使えなくて全てPHPのCURLにて置き換えてます。
5.AWSの一時認証(おまけ)
private function signingAWS($cognito_id, $openid_token) { // urlは適当に $url = 'https://cognito-identity.ap-northeast-1.amazonaws.com/'; $body = [ 'IdentityId' => $cognito_id, 'Logins' => [ 'cognito-identity.amazonaws.com' => $openid_token, ] ]; $headers = [ 'Content-Type: application/x-amz-json-1.1', 'Origin: null', 'X-Amz-Content-Sha256: ' . hash('sha256', json_encode($body)), 'X-Amz-Target: AWSCognitoIdentityService.GetCredentialsForIdentity', ]; $ret = $this->requestPost($url, $headers, $body); return $ret; }
この方法であってるか判りません。
cognito_idとopenid_tokenはAWSのログイン時に取得できます。
値は実際の環境に合わせてください。