Monday, November 1, 2010

How to construct an Amazon Query based url

I am currently writing an iPhone app to manage Amazon Cloud infrastructure and thought it might be helpful to share some of the code with the community.
In this post i will include two methods, one to url encode a string and the second to construct a Query based url.

#import "AWSUtils.h"

#include <CommonCrypto/CommonHMAC.h>

#import "Base64Encoder.h"

@implementation AWSUtils

+(NSString*) calculateHMACWithKey:(NSString*) key andData:(NSString*) data{

const char *cKey = [key cStringUsingEncoding:NSASCIIStringEncoding];

const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];

unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];

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

NSData *hmac = [[NSData alloc] initWithBytes:cHMAC

length:sizeof(cHMAC)];

return [Base64Encoder encode:hmac];

}

+(NSString*) urlEncode:(NSString*) url {

return (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,

(CFStringRef)url,

NULL,

(CFStringRef)@"!*'();:@+$,/?%#[]",

kCFStringEncodingUTF8 );

}

+(NSString*) getQueryString:(NSMutableDictionary*) dict{

NSEnumerator *enumerator = [[[dict allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] objectEnumerator];

NSString *key;

NSString *queryString = [[[NSString alloc] init] autorelease];

NSMutableArray *queryArray = [[[NSMutableArray alloc] initWithCapacity:[dict count]] autorelease];

while (key = [enumerator nextObject]) {

[queryArray addObject:[NSString stringWithFormat:@"%@=%@",[AWSUtils urlEncode:key], [AWSUtils urlEncode:(NSString*)[dict valueForKey:key]]]];

}

queryString = [queryArray componentsJoinedByString:@"&"];

NSString *signedUrl = [AWSUtils calculateHMACWithKey:@"private key" andData:[NSString stringWithFormat:@"GET\nec2.amazonaws.com\n/\nAWSAccessKeyId=accessKeyId&%@",queryString]];

NSString *encodedAndSignedUrl = [AWSUtils urlEncode:signedUrl];

NSString *urlToUse = [NSString stringWithFormat:@"https://ec2.amazonaws.com/?%@&Signature=%@&AWSAccessKeyId=accesskeyId",queryString, encodedAndSignedUrl];

return urlToUse;

}

@end

No comments: