Developer Guide - リクエストの署名認証について(日本語参考訳)
を見てやったわけだが、
改良点は
旧版のパラメータにVersionとTimestamp(2009-01-01T12:00:00Zのような形式でGMT。+09:00が通用するかはわからない。)を追加する。
パラメータの値にはA-Z, a-z, 09-, ハイフン(-)、アンダーバー(_)、ピリオド(.)およびチルダ(~)以外の文字を含めてはいけなく、URLエンコードしなくてはいけない。
パラメータはキーの文字列順にソートする。
「GET」「webservices.amazon.com」「/onca/xml」「パラメータ」を改行(改行コードは\n)で連結した文字列をSecret Access KeyをキーとしHMAC-SHA256ハッシュアルゴリズムで計算する。
HMAC-SHA256ハッシュをBase64エンコードしたものを「Signature=結果」でパラメータに追加する。
って感じなよう。
コード書いてみたが、とりあえずほぼ完成したと思う。
#!/usr/bin/perl
use Digest::SHA;
$SecretAccessKey='1234567890';
$Server='webservices.amazon.com';
$Service='AWSECommerceService';
$AWSAccessKeyId='AWSAccessKeyId=00000000000000000000';
$Operation='Operation=ItemLookup';
$ItemId='ItemId=0679722769';
$ResponseGroup='ResponseGroup=ItemAttributes,Offers,Images,Reviews';
$Version='2009-03-31';
$Version='2009-01-06';
&AmazonGet($AWSAccessKeyId,$Operation,$ItemId,$ResponseGroup);
###
sub AmazonGet{
my(@list,$gmt,@t,$req,$str,$sig,$url,$xml);
foreach(@_){
@t=split(/=/,$_,2);
$t[1]=~s/([^A-Za-z0-9\-\_\.\~])/'%'.uc(unpack('H2',$1))/eg;
push(@list,$t[0].'='.$t[1]);
}
@t=gmtime;
$t[5]+=1900;
$t[4]++;
for(0..4){
$t[$_]='0'.$t[$_] if(length($t[$_])==1);
}
$gmt="$t[5]-$t[4]-$t[3]T$t[2]:$t[1]:$t[0]Z";
$gmt="2009-01-01T12:00:00Z";
$gmt=~s/([^A-Za-z0-9\-\_\.\~])/'%'.uc(unpack('H2',$1))/eg;
push(@list,"Timestamp=$gmt");
push(@list,"Service=$Service");
push(@list,"Version=$Version");
@list=sort(@list);
$req=join('&',@list);
$str="GET\n$Server\n/onca/xml\n$req";
$sig=Digest::SHA::hmac_sha256_base64($str,$SecretAccessKey);
while(length($sig)%4){
$sig.='=';
}
$sig=~s/([\+\=])/'%'.uc(unpack('H2',$1))/eg;
$url='http://'.$Server.'/onca/xml?'.$req.'&Signature='.$sig;
print "$url\n";
}
###
Developer Guideと同じ結果が帰ってくるようなコードを書いただけだが、
サブルーチンAmazonGetにパラメータを渡すとxmlかえす感じにするつもり。
AmazonGetは、
まず、パラメータのリストを受け取り、「=」で区切って右側をURLエンコードしてる。
エンコードしちゃいけない文字を除外し、
Developer Guideではエンコードされた文字列は大文字になっているんでucで大文字に変換してる。
次、gmtimeでタイムスタンプ作る。
(サンプルと同じ結果だしたいんで$gmtには固定で値入れてる。)
「:」はURLエンコードする。
Timestamp、Service、Versionを追加。
パラメータのソート。
HMAC-SHA256ハッシュアルゴリズムの計算。
「Digest::SHA」ってモジュールの「hmac_sha256_base64」ってメソッド使うとBase64エンコードしたものがとりだせる。
ここでちょいとハマったのが、
最初やったとき、Developer Guideでは
8の計算結果が「Nace+U3Az4OhN7tISqgs1vdLBHBEijWcBeCqL5xN9xg=」
なんだが、
手元では「EDWJ1+VXQhAtPDKQ0f+wpaFQcBVDJyTIpDP7BZgxMiA」ってでた。
しかも、
10のリクエストURLでは「Signatu re=pwqYQRc3RepIrf7m%2BVMRy%2FjFXx%2FZBSPsaSFFexIUoSI%3D」ってなってる。
ちょっとググってたらRubyのサンプルコードが見つかったんで、それと見比べてみたら、
Developer Guideでは「Version=2009-03-31」なんだが、正しい結果がでているサンプルコードでは「Version=2009-01-06」となっていることに気づいた。
「Version=2009-01-06」だとDeveloper Guideと同じ結果になった。
こんなこともあろうかとProduct Advertising APIについてのメールが来てからすぐに改造しないでいままでまってた。
すぐに問題点見つかったが、サンプルがなかったらこれはハマったと思う。
うまくいったと思ったが、よく見ると
Developer Guideでは「Nace+U3Az4OhN7tISqgs1vdLBHBEijWcBeCqL5xN9xg=」
なのに、手元では「Nace+U3Az4OhN7tISqgs1vdLBHBEijWcBeCqL5xN9xg」
だった。末尾に「=」がねえ!
Base64末尾の「=」と思うんで、hmac_sha256_base64ではBase64の末尾の「=」がついてこないんだと思う。
Base64は4の倍数文字で、足りない場合は「=」で埋まるはずなんで、4の倍数文字じゃない時は「=」追加するようにした。
こんなかんじかな。
あと、webservices.amazon.comはアマゾン日本はwebservices.amazon.co.jpだと思う。
【日記の最新記事】

