TwitterfonでConnection Errorが出る[解決]

2009年8月9日日曜日 0 コメント
どうも先日のDDoS攻撃が尾を引いているらしく、このところTwitterFonでTwitterにアクセスすると" Secure connection Failed" (セキュアな接続に失敗しました)って出てたんですが、ネットを探してたら、こちらに、


Had lots of post-DDOS Twitterfon problems. Uninstalled Twitterfon. Reset iPhone. Reinstalled. And now Twitterfon seems fine. :)


つまり、一度TwitterfonをiPhoneから削除します。そして電源をオフにして、再度オン。iTunesからTwitterfonを再インストールしてください。モノは試しとやってみたところ、見事復活。もうセキュアな接続に失敗しましたとか出なくなりました。

Amazon Production Advertising APIとCocoa 第3回

2009年8月6日木曜日 2 コメント
署名を作成する前段階まできました。

5. 作成した文字列と秘密キーで、HMAC-SHA256形式の署名を作成する。

これはどうあがいても理解できそうになかったので、CCHMACを使うことに。
Google Codeにあったこことか、stackoverflowにあったこことかすごく参考になりました。

まず最初にファイルをインポート。



#import <CommonCrypto/CommonHMAC.h>



そして署名を作成します。



unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];

const char *cKey = [S_ID cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [forSignString cStringUsingEncoding:NSASCIIStringEncoding];

CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);


解説はできません(笑)。AppleのDocumentによると、CCHmacは



void
CCHmac(CCHmacAlgorithm algorithm, const void *key, size_t keyLength, const void *data, size_t dataLength, void *macOut);



という定義がされているようです。CCHmacAlgorithm algorithmの部分を変更すれば、SHA1やMD5の署名も作れます。

6. 署名を付けたRESTリクエストを作成

署名(cHMAC)をNDDataに変換、再度Urlencodeして、urlStringの後に&Signature=[署名]の形式でくっつけ、その頭にhttp://ecs.amazonaws.jp/onca/xml?を付けてURLの形にします。



NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC
length:sizeof(cHMAC)];

NSString *hash = [self urlencode:[HMAC stringEncodedWithBase64]];

NSString *url = [NSString stringWithFormat:@"http://ecs.amazonaws.jp/onca/xml?%@&Signature=%@", urlString, hash];



これでOK。生成されたurlにFirefoxなどでアクセスしてみると、XMLが返ってくると思います。XMLが返ってきたときはとっても嬉しかったです。

Amazon Production Advertising APIとCocoa 第2回

2009年8月4日火曜日 0 コメント
さて、タイムスタンプまで作成が終わりました。

3.項目名と項目内容で対の配列を作り、かつ項目内容をURLエンコードする。

1.で作ったRESTの文字列のうち、?以下の内容を、項目名(例えばService)と項目内容(AWSECommerceService)を対にした配列にします。配列というか、Rubyでいうハッシュでしょうか。私はNSMutableDictionaryを使いました。
AWS_IDは、ヘッダファイルに

#define AWS_ID @"アクセスキー"

として定義しています。
同じく秘密キーも

#define S_ID @"秘密キー"

として定義しました。



NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:@"AWSECommerceService" forKey:@"Service"];
[dict setValue:AWS_ID forKey:@"AWSAccessKeyId"];
[dict setValue:@"ItemLookup" forKey:@"Operation"];
[dict setValue:isbn forKey:@"ItemId"];
[dict setValue:@"ISBN" forKey:@"IdType"];
[dict setValue:@"Books" forKey:@"SearchIndex"];
[dict setValue:[self urlencode:@"Medium,Offers,Images"] forKey:@"ResponseGroup"];
[dict setValue:[self urlencode:@"2009-03-31"] forKey:@"Version"];
[dict setValue:[self urlencode:time] forKey:@"Timestamp"];



urlencodeというメソッドが出てきますが、これは例えば,(カンマ)を%2にするようなURLに含めない文字列を%付きの文字にするものだそうです。なんかそれらしきメソッドもあるらしいのですが、問題が起こるという話もあったので、こちらにあったものを流用させていただきました。



-(NSString *)urlencode:(NSString *) url
{
NSArray *escapeChars = [NSArray arrayWithObjects:@";" , @"/" , @"?" , @":" ,
@"@" , @"&" , @"=" , @"+" ,
@"$" , @"," , @"[" , @"]",
@"#", @"!", @"'", @"(",
@")", @"*", nil];

NSArray *replaceChars = [NSArray arrayWithObjects:@"%3B" , @"%2F" , @"%3F" ,
@"%3A" , @"%40" , @"%26" ,
@"%3D" , @"%2B" , @"%24" ,
@"%2C" , @"%5B" , @"%5D",
@"%23", @"%21", @"%27",
@"%28", @"%29", @"%2A", nil];

int len = [escapeChars count];

NSMutableString *temp = [url mutableCopy];

int i;
for(i = 0; i < len; i++)
{

[temp replaceOccurrencesOfString: [escapeChars objectAtIndex:i]
withString:[replaceChars objectAtIndex:i]
options:NSLiteralSearch
range:NSMakeRange(0, [temp length])];
}

NSString *out = [NSString stringWithString: temp];

return out;
}



NSStringクラスにメソッドを上書きしても良いのかも知れません。リンク先ではNSStringのクラスメソッドとして作成されていますが、どうもクラスメソッドとインスタンスメソッドの違いが分かっていない私は、同じところで使いたいので、インスタンスメソッドにしちゃいました。ただし、このエンコードはスペースをエスケープしてくれないみたいです。

4. 項目名のバイト順に並べ替え、署名を作るための文字列を作る

さて、dictを項目名順に並べ替え、それを配列にします。


NSArray *keyArr = [[dict allKeys]sortedArrayUsingSelector:@selector(compare:)];


さらに、項目名でdictから項目内容を取り出し、&で繋いで文字列にします。



NSMutableString *urlString = [[NSMutableString alloc]init];
NSEnumerator *enumerator = [keyArr objectEnumerator];

id key;

while (key = [enumerator nextObject]){

  if([keyArr indexOfObject:key] > 0){
   [urlString appendString:@"&"];
   }

  [urlString appendFormat:@"%@=%@", key, [dict valueForKey:key]];

}

NSString *signHead = [[NSString alloc] initWithString:@"GET\necs.amazonaws.jp\n/onca/xml\n"];
NSString *forSignString = [NSString stringWithFormat:@"%@%@", signHead, urlString];



最後に以下の文字列をくっつけています(改行要)。後の処理を考えると、urlStringとsingHeadは別々にしておいたほうがベターです。



GET
ecs.amazonaws.jp
/onca/xml



これで署名を取得するための文字列が完成です。

Amazon Production Advertising APIとCocoa 第1回

0 コメント
5月ごろだったでしょうか、AmazonのWebservicesからメールが届きました。8月からAPIには認証を含める必要があるから準備せよと。その時はまだ先だし、春の忙しい時期だったので、放置していたのですが、Amazonは本気だったのです。
しばらくして「あなたのリクエストには認証が含まれてない」旨のリマインドメールが日本語で2度、英語で1度ほど届き、ようやく数日前に重い腰を上げたのでした。

ちなみに私がAmazon APIを使っているのは、近日発売予定の本のISBNをリスト化しておいて、Amazonに在庫が出てくるのを定期的にチェックするためのソフトウェアです。
言語はRubyCocoa。ただ、今回ようやくHillegass著"Cocoa programming for Mac OS X 3rd ed."を読み終わり、Objective-Cでも書けそうと思えたので、無謀にもObjective-Cを使ってCocoa(XCode 3.1)で作成しています。

なんとかXMLを取得するところまで行けたので、自分の覚え書きの意味合いも込めて、書いておきたいと思います。ちなみに配布する予定がないソフトウェアなので、互換性とか全く考えていません。かつ、Objective-Cは初心者です。ご注意を。

やることの流れは、このページ(Ajax Tower)が最もまとまっていて、わかりやすかったです。

※最初にすること
アクセスキーの他に、署名を作成するための秘密キー(Secret Access Key)が必要です。ここを見て確認してください。


1. 今まで通り、RESTのリクエストを作成してみます。例えば、ISBN9784844323457の本を検索するのであれば、
http://ecs.amazonaws.jp/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=[アクセスキー]&Operation=ItemLookup&ItemId=9784844323457&IdType=ISBN&SearchIndex=Books&ResponseGroup=Medium&Version=2009-03-31

FirefoxなどでそのURLを叩いてみると、XMLが返戻されてくると思います。文字列で検索をしたいとか、他のResponseGroupが欲しいとか、細かいことはAmazonのドキュメントを見て下さい。

2. 1.にTimeStampを加える
さらに、タイムスタンプを加える必要があります。しかも日本時間ではなく、GMTの時間が必要です。以下のように作ってみました。標準形式だそうなので、もっと簡単にできるのかと思ってドキュメントを散々探しましたが、これで力尽きました。まあ出来たからいいか。



NSTimeZone *zone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
NSCalendarDate *timeStamp = [NSCalendarDate calendarDate];
[timeStamp setTimeZone:zone];
NSString *time = [timeStamp descriptionWithCalendarFormat:@"%Y-%m-%dT%H:%M:%S.000Z"];



とりあえず署名を作るのに必要な項目が揃いました。

次回は、項目名と項目内容を対にした配列を作成と、URLエンコードです。