3日間も悩んで、結果すごくおバカなミスだったという話。
Amazon Web ServiceでREST形式でいろいろできる手法に署名(Signature)をつけるよう仕様が変更される。8月15日までは移行期間で、それ以降はRESTに署名をつけないと処理されなくなる。そこで早めに対応しておこうと処理を書いてみた。
$secretKey=’0123456789′; // AWSより提供されたSecret Access Key
$url= ‘http://ecs.amazonaws.jp/onca/xml';
$parameters=array(‘Service’=>’AWSECommerceService’, ‘Version’=>’2009-03-31’………); //必要なパラメータ
$parameters[‘Timestamp’]=gmdate(‘Y-m-d\TH:i:s\Z’); //タイムスタンプが必要になった
ksort($parameters); //署名の際、クエリの並び順も関係するのでソートしておく
//クエリの組み立て
$query=”;
foreach($parameters as $id => $value){
$query.=’&’.$id.’=’.str_replace(“%7E”,’~’,rawurlencode($value));
}
$requests=parse_url($url);
//署名のための文字列を生成して署名を作る
$string4sign=”;
$string4sign.=”GET\n”;
$string4sign.=$requests[‘host’].”\n”;
$string4sign.=$requests[‘path’].”\n”;
$string4sign.=$query;
$signature=str_replace(“%7E”, ‘~’, rawurlencode(base64_encode(hash_hmac(‘sha256′, $string4sign, $secretKey, true))));
$url.=’?’.$query.’&Signature=’.$signature;
このスクリプトは最終的に動作したもの。しかし最初に書いたスクリプトで$urlにアクセスしても「SignatureDoesNotMatch」。なぜだ。
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.
Secret Access Keyが間違っているか、署名方法が間違ってるよ、といわれる。何度も何度も微調整して、他のサイトも参考にしたけど理由がわからなくて、3日間も悩んでしまった。間違っていた箇所は2つ。
(1) ちゃんとエンコードした値を渡す。
$query.=’&’.$id.’=’.str_replace(“%7E”,’~’,rawurlencode($value));
動かなかったスクリプトではちゃんとRFCにそったエンコードをかけていなかった。んで、ここはとりあえず解決。そしてもっと致命的なミスがあった。
(2) Access Keyが違う!。
そもそもなんで違うアクセスキーを使っていたのかがわからない。ていうか、どこでそのアクセスキーを取得したのかもわからない。すべて謎。たぶんどっかのスクリプトをコピペした際に、アクセスキーを書き換えるのを忘れていたんだと思う。「コピペ元の方、ごめんなさい」という感じだ。ま、そもそもAccess KeyとSecret Access Keyの組み合わせが違うんだから絶対に署名があうわけないのだ。
前者のミスは早々に気がついていたのですぐに修正したけれど、それでも動作しないから「記述方法が悪いんじゃないか」と思ったり、署名用の文字列の作り方が悪いんじゃないか、と思ったりで後者のミスに気がつかなかった。というか、そもそも間違えているなんて夢にも思わなかった。
結果的に2つもミスがあったので、ほんと3日間も悩んでいてバカみたいだった。でも、スッキリ。