Wednesday, May 18, 2011

Google REST Web Service Security C# URL Authentication

Google URL Signing
 
To access certain Google API web services a Client ID and a digital signature is required.  A Client ID and a cryptographic key (used to generate a unique digital signature) are provided by Google once consumers have created an account.  Before a REST web service request can be made, a digital signature must be generated and passed in as part of the URL, this process is known as URL signing.  The cryptographic key, sometimes called shared secret/signing key must be kept secret and is never passed in as part of a URL request.

The following are required before a digital signature can be generated:

  • A signing string (made up of the path and query of the request URL).
  • A signing key (cryptographic key provided by Google).
  • The correct encryption algorithm (Hash Message Access Control eg. HMAC SHA1, HMAC SHA2).

To generate a digital signature requires the following 3 steps.

1)  Construct request URL, the URL must be percent-encoded/URL encoded to ensure it is valid.

Constructing a valid URL

A URL is made up of reserved and unreserved characters.  Reserved characters in a URL have a specific purpose, for example a ‘/ ’ separates the different parts of a URL.  If a ‘/ ’ is required in a URL for any other purpose, then it must be percent-encoded to distinguish it from its reserved counterparts.  Percent-encoded reserved characters are always preceded by a ‘%’ to indicate percent-encoding.

The following are reserved characters:

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

To percent-encode a URL’s reserved characters, use the pair of hex digits which corresponds to each reserved character’s ASCII value, finish by preceding each hex digit pair with a ‘%’.  If any reserved character is non-ASCII then use the pair of hex digits which corresponds to its UTF-8 character instead.

For example, to percent-encode a '/ ':

a)  convert to or look up the ASCII value for a ‘/ ’, the ASCII value is 47

b)  next look up the pair of hex digits which correspond to this ASCII value, the hex digit pair is 2F

c)  next precede with a ‘%’, the result is %2F

URL encoding the following URL:

http://maps.googleapis.com/maps/api/geocode/json ?address=East 25th St & 3rd Ave&sensor=false&client=yourClientID

The address parameter contains spaces, spaces are not allowed in a URL, it also contains a '&'.  This URL must be encoded by replacing spaces with a '+', and replacing '&' with the two hex digits corresponding to its ASCII value.

Encoding would produce the following URL:

http://maps.googleapis.com/maps/api/geocode/json?address=East+25th+St+%26+3rd+Ave&sensor=false&client=yourClientID

For more information on ASCII characters and their corresponding pair of hex digits see the following link:


To see a list of reserved/unreserved characters and additional information on URL usage, go to the following link.


2)  Using the encoded URL

http://maps.googleapis.com/maps/api/geocode/json?address=East+25th+St+%26+3rd+Ave&sensor=false&client=yourClientID

Omit the protocol and host, leaving only the path and query, now you have the signing string.

/maps/api/geocode/json?address=East+25th+St+%26+3rd+Ave&sensor=false&client=yourClientID

3)  Generate signature using signing key, signing string and encryption algorithm.

Google provides a C# method for developers called ‘Sign’, this method accepts two string parameters and returns the signed URL request in full as a string.  When calling this method the full URL and the signing key are passed in as the two parameters of type string, see the method below.

Google’s C# 'Sign' method code snippet

public static string Sign(string url, string keyString)
{
      ASCIIEncoding encoding = new ASCIIEncoding();
      // converting key to bytes will throw an exception, need to
      // replace '-' and  '_' characters first.

      string usablePrivateKey = keyString.Replace("-", "+").Replace("_", "/");

      byte[] privateKeyBytes = Convert.FromBase64String(usablePrivateKey);

      Uri uri = new Uri(url);

      byte[] encodedPathAndQueryBytes = encoding.GetBytes(uri.LocalPath + uri.Query);

      // compute the hash

      HMACSHA1 algorithm = new HMACSHA1(privateKeyBytes);

      byte[] hash = algorithm.ComputeHash(encodedPathAndQueryBytes);

      // convert the bytes to string and make url-safe by replacing '+' and '/' characters

      string signature = Convert.ToBase64String(hash).Replace("+", "-").Replace("/", "_");

      // Add the signature to the existing URI.

      return uri.Scheme+"://"+uri.Host+uri.LocalPath + uri.Query +"&signature=" + signature;
}

A signature parameter is attached to the end of the URL, and the fully signed request URL is returned as a string.

http://maps.googleapis.com/maps/api/geocode/json?address=East+25th+St+%26+3rd+Ave&sensor=false&client=yourClientID&signature=HMACgeneratedSignature

Using this process a request can be made to any Google web service which requires a digital signature.

To download Google's C# console application for URL signing, which includes the 'Sign' method shown above, see the following link:


In some cases SSL (Secure Socket Layer) is used to provide transport level security when making requests. In these situations change the protocol from ‘http’ to ‘https’, before making the request.

Google Maps API Web Services require URL Authentication, documentation is provided on the URL signing process, how to produce valid URL's, SSL Access and processing XML results.  See the following link for more information: