Signing Google API URLs in iOS

Lately I’ve been working with the Google Directions API. The service is free to use, you don’t even need to sign up for an API key. However, if you do, you get to make more requests from the service per day. A lot more! So it certainly behooves you to sign up as a Premier customer if you plan to do more than make light use of the API. The catch is all requests to the API from Premier customers have to be signed with an API key, and this was just a little bit annoying to do for iOS development. So here’s a quick walk through to help out the next guy.

Considerations

Requests to Google Premier APIs must be made with a signed URL. Basically, the URL’s path and query string, along with an API key make up part of a hash that is appended to the query string as a signature parameter. To make the hash, you must take your key, a modified Base64 encoded string, decode it, and use it as the signing key in the HMAC-SHA1 algorithm. Finally, Base64 encode the result of the algorithm and voila, you have signed your URL.

Now, for whatever reason, iOS does not expose Base64 encode/decode methods natively. This may seem like a problem, but since Google’s URL signing uses a modified Base64 encode/decode that is URL safe, you probably would not have been able to use native methods anyway. Since Google has provided a sample URL signer in Objective-C we’ll start there.

Building a Reusable Signage Method

The example Google provides is a great staring point, and with a few modifications we can use it to sign our URL requests. First things first, look at the comments in the heading. It says that Google Toolbox for Mac provides the Base64 encoding and decoding, provides the link to the repository and notes that only GTMStringEncoding.m and urlsigner.m are necessary to compile. Google Toolbox for Mac is a pretty large library to include just to sign a few URLs — or so I think. Also, the comments say that just the GTMStringEncoding.m and urlsigner.m are required to compile, and if you’ve worked with C or Objective-C at all you know you must also include the headers and any other dependencies. So let’s grab just the files we need from the Google Toolbox for Mac repository:

  • GTMStringEncoding.h
  • GTMStringEncoding.m
  • GTMDefines.h
  • AvailabilityMacros.h
  • TargetConditionals.h
  • Availability.h
  • ConditionalMacros.h

We’re not going to include urlsigner.m. Its the name of the sample file and we don’t really need that in our project.

Now let’s modify the method itself. Remove the lines that create and flush the autorelease pool. That should be handled in your project’s own main method. Now let’s create a new method signature that facilitates reuse:

- (NSString *)signAPIURL:(NSString *)urlpath {

Since we want re-usability, we’ll pass a string urlpath that we’ll sign, so delete the line that declares the URL. (Oh, and be sure to change the value of the key to match your own API key.)

We need to separate out the path and query string from the rest of the URL path, the easiest way to do this is to create an NSURL instance from the urlpath parameter and format a new string from its path and query properties:

NSURL *u = [NSURL URLWithString:urlpath];
NSString *url = [NSString stringWithFormat:@"%@%@", [u path], [u query]];

Finally, edit the bottom of the method to return a string formatted with the original url path and appending the signature:

  NSString *signature = [encoding encode:binarySignature];

The final method should look like this:

- (NSString *)signAPIURL:(NSString *)urlpath {
 
     NSString *key = @"vNIXE0xscrmjlyV-12Nj_BvUPaw=";
 
     NSURL *u = [NSURL URLWithString:urlpath];
     NSString *url = [NSString stringWithFormat:@"%@%@", [u path], [u query]];
 
     // Stores the url in a NSData.
     NSData *urlData = [url dataUsingEncoding: NSASCIIStringEncoding];
 
     // URL-safe Base64 coder/decoder.
     GTMStringEncoding *encoding = [GTMStringEncoding rfc4648Base64WebsafeStringEncoding];
 
     // Decodes the URL-safe Base64 key to binary.
     NSData *binaryKey = [encoding decode:key];
 
     // Signs the URL.
     unsigned char result[CC_SHA1_DIGEST_LENGTH];
     CCHmac(kCCHmacAlgSHA1,
          [binaryKey bytes], [binaryKey length],
          [urlData bytes], [urlData length],
          &result);
     NSData *binarySignature = [NSData dataWithBytes:&result length:CC_SHA1_DIGEST_LENGTH];
 
     // Encodes the signature to URL-safe Base64.
     NSString *signature = [encoding encode:binarySignature];
 
     return [NSString stringWithFormat:@"%@&signature=%@", urlpath, signature];
}

Enjoy.

Be Sociable, Share!

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">